1.redis 键值设置
1.1key的设计
- 遵循 [业务名称]:[数据名(value的含义)]:[id]
- 长度不超过44byte
例子:
login登录业务 id为10 存储的是user 信息
key为String 类型 底层编码采用 int 、embstr、 raw 三种
embstr在小于44字节使用 ,连续内存,占用内存小
当(v)字节数大于44字节时,会转为raw模式存储,在raw模式下,内存空间不是连续的,而是采用一个指针指向了另外一段内存空间,在这段空间里存储SDS内容,这样空间不连续,访问的时候性能也就会收到影响,还有可能产生内存碎片raw 采用
1.2big key
大的不是key,而是key对应的value
bigkey的判定
- key 本身的数据量过大,一个string类型的key,它的值为5MB
- key对应的 成员数量过多,zset 中, 成员数量有10000个。
- key中成员的数据量过大,hash中,成员数量虽然只有1000个,但是总的数据量value超过了100MB
如何判断元素的大小呢?
推荐值:
- 单个key的value小于10KB
- 对于集合类型的key,建议元素数量小于1000
1.3 获取redis 所有key
scan 来操作
默认10条,第0条开始扫描,下次扫描18开始。
1.4 删除big key
需要配置
lazyfree-lazy-server-del no
3.如rename命令,当目标键已存在, redis会先删除目标键,如果这些目标键是一个big key, 那就会引入阻塞删除的性能问题。 此参数设置就是解决这类问题建议开启
Lazy Freeing
·lazyfree-lazy-user-del
支持yes或者no。默认是no。 建议开启
如果设置为yes,那么del命令就等价于unlink,也是非阻塞删除。
del
与unlink命令
底层均调用delGenericCommand方法
,区别是unlink第二个参数传的是1
,del传的是lazyfree-lazy-user-del,所以如果lazyfree-lazy-user-del配置的是yes的话
,那么del命令的功能就等价于unlink。
BigKey内存占用较多,即便时删除这样的key也需要耗费很长时间,导致Redis主线程阻塞,引发一系列问题。
-
redis 3.0 及以下版本
- 如果是集合类型,则遍历BigKey的元素,先逐个删除子元素,最后删除BigKey
list 采用 ltrim
- 如果是集合类型,则遍历BigKey的元素,先逐个删除子元素,最后删除BigKey
-
Redis 4.0以后
- Redis在4.0后提供了异步删除的命令:unlink
1.5 bigkey 调优配置 lazy freeing
del 命令是 阻塞的
使用unlink命令 配置参数
比如当我(Redis)需要删除一个很大的数据时,因为是单线程原子命令操作,这就会导致 Redis 服务卡顿,
于是在 Redis 4.0 中就新增了多线程的模块,当然此版本中的多线程主要是为了解决删除数据效率比较低的问题的。
unlink key
flushdb async
flushall async
把删除工作交给了后台的小弟(子线程)异步来删除数据了。
1.6 总结
- Key的最佳实践
- 固定格式:[业务名]:[数据名]:[id]
- 足够简短:不超过44字节
- 不包含特殊字符
- Value的最佳实践:
- 合理的拆分数据,拒绝BigKey
- 选择合适数据结构
- Hash结构的entry数量不要超过1000(默认是500,超过500后,ziplist转为hash)
- value值不超过10kB
- 设置合理的超时时间
2 批处理
pipline 管道可以执行任意命令
2.1 集群模式下 批处理
如MSET或Pipeline这样的批处理需要在一次请求中携带多条命令,而此时如果Redis是一个集群,那批处理命令的多个key必须落在一个插槽中,否则就会导致执行失败
。
第一种方案:串行执行,所以这种方式没有什么意义,当然,执行起来就很简单了,缺点就是耗时过久。
第二种方案:串行slot,简单来说,就是执行前,客户端先计算一下对应的key的slot,一样slot的key就放到一个组里边,不同的,就放到不同的组里边,然后对每个组执行pipeline的批处理,他就能串行执行各个组的命令,这种做法比第一种方法耗时要少,但是缺点呢,相对来说复杂一点,所以这种方案还需要优化一下
第三种方案:
并行slot,相较于第二种方案,在分组完成后串行执行,第三种方案,就变成了并行执行各个命令,所以他的耗时就非常短,但是实现呢,也更加复杂。
第四种:hash_tag,redis计算key的slot的时候,其实是根据key的有效部分来计算的,通过这种方式就能一次处理所有的key,这种方式耗时最短,实现也简单,但是如果通过操作key的有效部分,那么就会导致所有的key都落在一个节点上,产生数据倾斜的问题,所以我们推荐使用第三种方式。
lettuce
spring mset 实现了并行slot
3. 持久化配置
- 配置no-appendfsync-on-rewrite = yes,禁止在rewrite期间做aof,避免因AOF引起的阻塞
4.慢查询
慢查询的阈值可以通过配置指定:
slowlog-log-slower-than:慢查询阈值,单位是微秒。默认是10000,建议1000 1ms
慢查询会被放入慢查询日志中,日志的长度有上限,可以通过配置指定:
slowlog-max-len:慢查询日志(本质是一个队列)的长度。默认是128,建议1000
修改这两个配置可以使用:config set命令:
4.1 如何查看慢查询
- slowlog len:查询慢查询日志长度
- slowlog get [n]:读取n条慢查询日志
- slowlog reset:清空慢查询列表
5. 内存信息
info memory
memory xxx
5.1 内存缓冲区
复制缓冲区
:主从复制的repl_backlog_buf,如果太小可能导致频繁的全量复制,影响性能。通过replbacklog-size来设置,默认1mbAOF缓冲区
:AOF刷盘之前的缓存区域,AOF执行rewrite的缓冲区。无法设置容量上限
-客户端缓冲区
:分为输入缓冲区和输出缓冲区,输入缓冲区最大1G且不能设置。输出缓冲区可以设置
以上复制缓冲区和AOF缓冲区 不会有问题,最关键就是客户端缓冲区的问题
客户端缓冲区:指的就是我们发送命令时,客户端用来缓存命令的一个缓冲区,也就是我们向redis输入数据的输入端缓冲区和redis向客户端返回数据的响应缓存区,·输入缓冲区最大1G且不能设置,所以这一块我们根本不用担心,如果超过了这个空间,redis会直接断开
,因为本来此时此刻就代表着redis处理不过来了,我们需要担心的就是输出端缓冲区
我们在使用redis过程中,处理大量的big value,那么会导致我们的输出结果过多,如果输出缓存区过大,会导致redis直接断开,而默认配置的情况下, 其实他是没有大小的,这就比较坑了,内存可能一下子被占满,会直接导致咱们的redis断开,所以解决方案有两个
1、设置一个大小
2、增加我们带宽的大小,避免我们出现大量数据从而直接超过了redis的承受能力
client list 查看客户端详细信息
6集群 是否需要
- 集群完整性问题
- 集群带宽问题
- 数据倾斜问题
- 客户端性能问题
- 命令的集群兼容性问题
- lua和事务问题(保证多个命令执行的原子性,使用集群,那么就会失效。除非引入 hashtag{} 保证key 落在一个机器上)