网站怎么才能被百度收录,湖南企业网站制作公司,网站模板能上传图片,哪里有手机网站建设全量遍历keys
工作中线上Redis维护#xff0c;有时候我们需要查询特定前缀的缓存key列表来手动处理数据。可能是修改值#xff0c;删除key。那么怎么才能快速的从海量的key中查找到对应的前缀匹配项。Redis提供了一下简单的指令#xff0c;例如keys用来满足特定正则下的key…全量遍历keys
工作中线上Redis维护有时候我们需要查询特定前缀的缓存key列表来手动处理数据。可能是修改值删除key。那么怎么才能快速的从海量的key中查找到对应的前缀匹配项。Redis提供了一下简单的指令例如keys用来满足特定正则下的key如下
//查找nettyim前缀的key
keys nettyim*
//查询所有key
keys *以上命令特别简单只需要提供一个简单的正则字符串即可但是缺点明显 没有offsetlimit参数一次查询所有满足条件的key如果实例中有几百万key匹配到那查询到了也没有任何意义。keys算法是遍历算法复杂度是O(n)如果Redis实例中有千万级数据key这个指令就会导致Redis服务器卡顿所有读写Redis的其他指令都会被延后甚至会超时报错因为Redis是单线程顺序执行所有指令其他指令必须等到当前keys指令执行完才可以继续
大海捞针指令scan
scan 指令有一下几个特点 复杂度也是O(n)。但是scan命令可以通过游标分布进行查询不会阻塞线程相当于分页查找提供limit参数可以控制每次放回结果的条数limit只是一个hint返回的结果可多可少。同keys一样他提供了模式匹配功能服务器不需要为游标保存状态右边的唯一状态就是scan返回给客户端的游标整数返回的结果可能会重复需要客户端去重变量过程如果有数据修改改动后的数据能否查询到是不确定的单次返回的结果是空的并不意味着遍历结束而要看返回的游标值是否为零
scan基本用法
sacn提供三个参数第一个cursor整数值第二个是key的正则模式第三个是遍历limit hint。scan每次查询会返回一个游标值标识现在已经遍历到此处游标位置我们下一次遍历可以按照游标开始如下
scan 0 match nettyim* count 1000
scan 641024 match nettyim* count 100
scan 1812480 match nettyim* count 100
scan 7071744 match nettyim* count 100
....查询过程中我们设定limit 1000但是并不是每次都能查询到一千条数据因为limit不是限制返回的数量二手限制服务器每次遍历的Redis服务器存储数据的字典槽个数。如果将limit设置为10可能返回0个数据但是游标值不为0 那是因为前10个槽中没有数据而已。
字典结构
Redis中所有key存储在一个很大的字典中这个字典结构和java中HashMap类似如下图中所示他是一位数组结构加上二维链表结构。第一位数组的大小总数(2 ^ n) 扩容一次数组大小空间加倍 (2^n1)scan指令返回的游标就是第一位数组的位置索引这个就是上面说的数据槽slot如果不考虑字典的扩容缩容直接按数组下标逐个遍历就行。limit参数标识需要遍历的槽位数之所以返回的结果可能多可能少是因为不是所有的槽位上都挂载了链表有可能槽位是空的每次遍历都会将limit数量的槽位上挂接的所有链表元素进行模式匹配过滤后一起返回客户端。
scan遍历顺序
scan遍历的凡是不是从一维数组的第0 位开始遍历而是采用高位进位的方法来遍历。之所以用这样的方式是考虑到Redis的扩容规则当扩容的时候这样遍历就能避免槽位的重复遍历和遗漏如下图是普通的加法和高位进位加法的区别
普通遍历 高位进位遍历 上图中看出高位进位加法也是一样遵循二进制的规则只不过进位从左边开始增加移动同普通加法正好相反但是最终还是可以遍历所有槽位并且没有重复。
字典扩容
java中HashMap也有扩容的概念当LoadFactor达到阀值的时候需要重新分配一个新的2倍大小的数组然后将所有元素全部rehash挂到新的数组下面。rehash是将原始的hash值对数组长度取模运算因为长度变量所有每个元素挂载的位置槽就可能变化。有因为数组长度是2的n次方取模运算等价于位与操作;Redis中扩容方法如下图中所示当字典长度由8 为扩容到16位那么3号槽的数据011 将会被rehash到3号槽和11号槽中。也就是该槽位链表中大约有一半的元素还在3号槽位中其他元素被放到11号槽位中11 这个数字正好是1011就是3 的二进制数011 高位添加一个1. 按如上方式加上槽位二进制是XXX你们该槽位中元素将被rehash到0XXX和1XXX中XXX8如果字典长度由16 扩容到 32你们XXXX中元素rehash后到0XXXX 和1XXXXXXXX16
对比扩容前缩容后的遍历顺序 如上扩容缩容示意图我们发现用高位进位加法的遍历方式rehash后的槽位在遍历顺序上是相邻的扩容情况如上加入我们要遍历100 这个槽位那么扩容后当前槽位上所有元素到新的槽位0100,1100也就是在槽位二进制高位添加0,1。这时候我们可以直接从0100开始往后遍历而按照scan的变量规则下一个正好是1100高位加1之前的已经都遍历完了之后的按照这个变量方式也不会遗漏。缩容情况加入当前变量101那么缩容后当前槽位所有的元素对应的01也就是去掉高位的1这个时候我们可以直接从01这个槽位继续向后遍历01 之前的槽位已经遍历完了这样就可以避免缩容重复遍历缩容有一点不一样的地方是会对101中的元素进行遍历因为缩容的时候01 中的数据是结合了001 和101 链表中所有的数据。
渐进式rehash
java中HashMap在扩容时候会一次性将旧的数据数组下挂载的元素全部转移到新的数组下如果Hashmap中元素特别多线程会出现卡顿现象。Redis为了解决这个问题采用渐进式rehash同事保留新旧数组。让后在定时任务中对后续hash的指令操作渐渐的将数组中挂载的元素迁移到新的数组中区。scan此时遍历处于rehash阶段的字典需要同时访问新旧两个数组结构。如果在就数组下面找不到元素需要到新数组中在找一次。
更多scan指令
scan指令是一系列指令除了可以遍历所有key还有其他指定数据结构的特定指令。例如zscan遍历zset集合元素hscan遍历hash字典元素sscan遍历set集合元素原理同scan类似因为hash底层就是字典set也是特殊hash所有value都是nullzset内部也是使用字典来存储所有元素内容
大key扫描
有时候因为业务使用不当在Redis实例中存在一个很大的对象比如一个很大的zset。这样的对象给Redis继续数据迁移带来很大的问题。在数据迁移过程中集群环境下key太大导致迁移数据变慢造成服务卡顿。同时扩容时候会一次性申请更大的一块内存也会导致卡顿。如果这个key被删除内存会被一次性回收卡顿现象也会再次产生平时应该避免大key的产生如果Redis内存波动大极有可能因为大key导致的这时候需要定位具体那个key进一步定位出具体的业务然后在改进。scan指令遍历用type指令获取key类型size或者len得到大小用脚本扫描出来不过此方法比较繁琐Redis官方在redis-cli指令中提供这样的扫描功能我们可以直接用如下
redis-cli -h 127.0.0.1 -p 7001 --bigkeys以上命令会自动的查询大key但是会导致Redis的opsoperation pre seconds 每秒操作次数大幅提高我们可以增加一个休眠时间如下
redis-cli -h 127.0.0.1 -p 7001 --bigkeys -i 0.1上面指令每隔100条scan指令休眠0.1秒这样ops就不会剧烈提高只是扫描时间变长而已。