Redis是建立在TCP协议上的CS架构,客户端client对redis server采取请求响应的方式交互每次交互会有网络延迟,大约30ms
假设有这样一个场景,redis中存储上千个key值,获取每个key对应field的value,那么要向redis请求上千次 hget(key, field),获取响应也是对应的次数如果能一次性将所有请求提交给server端,执行完成后批量获取响应,只需向redis请求1次,性能获大幅提升
没有用pipeline之前,基本上获取所有数据需要90多s,现在只需03s,性能提升清晰可见
实现以下场景:定时任务每隔1s执行任务函数,但是任务函数执行完成的时间比1s要长,此时启动定时任务要加上两个参数,否则会报错
可允许的实例个数,如果没有设置,则默认为1,表示id相同的任务实例数
像上面的例子中,会报skipped: maximum number of running instances reached (1)的错误,意思APScheduler试图重新执行作业,但前一个仍在运行。
这个参数可以理解为任务的超时容错配置,给executor 一个超时时间,这个时间范围内要是该跑的还没跑完,就别再跑了
像上面的例子中,会报Run time of job …… next run at: ……)” was missed by的错误
在 redis 实际使用中,会遇到一个问题:如何从海量的 key 中找出满足特定前缀的 key 列表来?
redis 提供了一个简单暴力的指令 keys 用来列出所有满足特定正则字符串规则的 key。
这个指令有致命的弊端,在实际环境中最好不要使用:
我们可以通过配置设置禁用这些命令,在 redisconf 中,在 SECURITY 这一项中,我们新增以下命令:
另外,对于FLUSHALL命令,需要设置配置文件中appendonly no,否则服务器是无法启动。
Redis 为了解决这个问题,它在 28 版本中加入了 scan 。scan 相比 keys 具备有以下特点:
SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements):
scan 参数提供了三个参数,第一个是 cursor 整数值 ,第二个是 key 的正则模式 ,第三个是 遍历的 limit hint 。第一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。
从上面的过程可以看到虽然提供的 limit 是 1000,但是返回的结果只有 10 个左右。因为这个 limit 不是限定返回结果的数量,而是限定服务器单次遍历的字典槽位数量(约等于)。
在 Redis 中所有的 key 都存储在一个很大的字典中,这个字典的结构和 Java 中的 HashMap 一样,是一维数组 + 二维链表结构,第一维数组的大小总是 2^n(n>=0),扩容一次数组大小空间加倍,也就是 n++。
scan 指令返回的游标就是第一维数组的位置索引,我们将这个位置索引称为槽 (slot)。如果不考虑字典的扩容缩容,直接按数组下标挨个遍历就行了。limit 参数就表示需要遍历的槽位数,之所以返回的结果可能多可能少,是因为不是所有的槽位上都会挂接链表,有些槽位可能是空的,还有些槽位上挂接的链表上的元素可能会有多个。每一次遍历都会将 limit 数量的槽位上挂接的所有链表元素进行模式匹配过滤后,一次性返回给客户端。
scan 的遍历顺序非常特别。它不是从第一维数组的第 0 位一直遍历到末尾,而是采用了高位进位加法来遍历。之所以使用这样特殊的方式进行遍历,是考虑到字典的扩容和缩容时避免槽位的遍历重复和遗漏。
高位进位法从左边加,进位往右边移动,同普通加法正好相反。但是最终它们都会遍历所有的槽位并且没有重复。
Java 中的 HashMap 有扩容的概念,当 loadFactor 达到阈值时,需要重新分配一个新的 2 倍大小的数组,然后将所有的元素全部 rehash 挂到新的数组下面。rehash 就是将元素的 hash 值对数组长度进行取模运算,因为长度变了,所以每个元素挂接的槽位可能也发生了变化。又因为数组的长度是 2^n 次方,所以取模运算等价于位与 *** 作。
这里的 7, 15, 31 称之为字典的 mask 值,mask 的作用就是保留 hash 值的低位,高位都被设置为 0。
接下来我们看看 rehash 前后元素槽位的变化。
假设当前的字典的数组长度由 8 位扩容到 16 位,那么 3 号槽位 011 将会被 rehash 到 3 号槽位和 11 号槽位,也就是说该槽位链表中大约有一半的元素还是 3 号槽位,其它的元素会放到 11 号槽位,11 这个数字的二进制是 1011,就是对 3 的二进制 011 增加了一个高位 1。
抽象一点说,假设开始槽位的二进制数是 xxx,那么该槽位中的元素将被 rehash 到 0xxx 和 1xxx(xxx+8) 中。 如果字典长度由 16 位扩容到 32 位,那么对于二进制槽位 xxxx 中的元素将被 rehash 到 0xxxx 和 1xxxx(xxxx+16) 中。
观察这张图,我们发现采用高位进位加法的遍历顺序,rehash 后的槽位在遍历顺序上是相邻的。
假设当前要即将遍历 110 这个位置 (橙色),那么扩容后,当前槽位上所有的元素对应的新槽位是 0110 和 1110(深绿色),也就是在槽位的二进制数增加一个高位 0 或 1。这时我们可以直接从 0110 这个槽位开始往后继续遍历,0110 槽位之前的所有槽位都是已经遍历过的,这样就可以避免扩容后对已经遍历过的槽位进行重复遍历。
再考虑缩容,假设当前即将遍历 110 这个位置 (橙色),那么缩容后,当前槽位所有的元素对应的新槽位是 10(深绿色),也就是去掉槽位二进制最高位。这时我们可以直接从 10 这个槽位继续往后遍历,10 槽位之前的所有槽位都是已经遍历过的,这样就可以避免缩容的重复遍历。不过缩容还是不太一样,它会对图中 010 这个槽位上的元素进行重复遍历,因为缩融后 10 槽位的元素是 010 和 110 上挂接的元素的融合。
Java 的 HashMap 在扩容时会一次性将旧数组下挂接的元素全部转移到新数组下面。如果 HashMap 中元素特别多,线程就会出现卡顿现象。Redis 为了解决这个问题,它采用渐进式 rehash。
它会同时保留旧数组和新数组,然后在定时任务中以及后续对 hash 的指令 *** 作中渐渐地将旧数组中挂接的元素迁移到新数组上。这意味着要 *** 作处于 rehash 中的字典,需要同时访问新旧两个数组结构。如果在旧数组下面找不到元素,还需要去新数组下面去寻找。
scan 也需要考虑这个问题,对与 rehash 中的字典,它需要同时扫描新旧槽位,然后将结果融合后返回给客户端。
背景:最近公司APP要上线积分功能,需要按用户、业务场景、不同的累计周期、相关的业务信息等校验积分获取策略。为方便测试同学测试过程中直接列出redis集群中相关的统计指标key,进而查询相关信息,方便测试。
首先测试了下keys和scan居然都只能查看到当前节点的匹配到的key,what the fuck!!
想到之前工作中也碰到过同样的问题,于是折腾了会儿写了个脚本:
是的,当试图访问一个不存在的key时,Redis 会返回一个特殊的错误,即“key not found” (ERR no such key)。在一些情况下,比如使用INCR或DECR *** 作,如果key不存在,Redis会创建一个新的key,并将其设置为0,从而完成 *** 作。
phpredis是php的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系很有用;
如果对系统存储使用的数据以两种角度分类,一种是按数据的大小划分,分成大数据和小数据,另一种是按数据的冷热程度划分,分成冷数据和热数据,热数据是指读或写比较频繁的数据,反之则是冷数据。
可以举一些具体的例子来说明数据的大小和冷热属性。比如网站总的注册用户数,这明显是一个小而热的数据,小是因为这个数据只有一个值,热是因为注册用户数随时间变化很频繁。再比如,用户最新访问时间数据,这是一个量比较大,冷热不均的数据,大是数据的粒度是用户级别,每一个用户都有数据,如果有一千万用户,就意味着有一千万的数据,冷热不均是因为活跃用户的最新访问时间变化很频繁,但是可能有很大一部非活跃用户访问时间长时间不会发生变化。
大体而言,Redis 最适合处理的是小而热,而且是写频繁,或者读写都比较频繁的热数据。对于大而热的数据,如果其它方式很难解决问题,也可以考虑使用 Redis 解决,但是一定要非常谨慎,防止数据无限膨胀。原因如下:
首先,对于冷数据,无论大小,都不建议放在 Redis 中。Redis 数据要全部放在内存中,资源宝贵,把冷数据放在其中实在是一种浪费,冷数据放在普通的存储比如关系数据库中就好了。
其次,对于热数据,尤其是写频繁的热数据,如果量比较小,是最适合放到 Redis 中的。比如上面提到的网站总的注册用户数,就是典型的 Redis 用做计数器的例子。再比如论坛最新发表列表,最新报名列表,可以控制数量在几百到一千的规模,也是典型的 redis 做最新列表的使用方式。
另外,对于量比较大的热数据(或者冷热不均数据),使用 Redis 时一定要比较谨慎。这种类型数据很容易引起数据膨胀,导致 Redis 消耗内存巨大,让系统难以承受。薄荷的一个惨痛教训是把用户关注(以及被关注)数据放在 Redis 中,这是一种数据量极大,冷热很不均衡的数据,在几百万的用户级别就占用了近 10 GB左右内存,让 Redis 变得难以应付。应对这种类型的数据,可以用普通存储 + 缓存的方式。
如果用对了地方,比如在小而热的数据情形,Redis 表现很棒,如果用错了地方,Redis 也会带来昂贵的代价,所以使用时务必谨慎。
就看你的数据量大小了,如果太大,却是不好遍历,如果有规则的key值的话,倒是可以利用分页的方式来处理,如果没有规律的话,只有用keys来取了,或者是通过keysa;keysz;keys1;keys0等通配符的方式来顺序的读取,读取时要把内存分配的大一些,不然容易溢出
以上就是关于关于redis批量获取数据pipeline全部的内容,包括:关于redis批量获取数据pipeline、Redis 之用 scan 模糊匹配 key、(工作小记)redis集群下keys命令查询某一类业务相关的key等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)