Info
redis 每秒执行多少次指令
redis-cli info stats | grep ops
redis每秒执行10w次指令,cpu完全榨干,如果 qps 过高,可以考 虑通过 monitor 指令快速观察一下究竟是哪些 key 访问比较频繁,从而在相应的业务上进 行优化,以减少 IO 次数。
redis连接了多少客户端
redis-cli info clients
redis内存占用多大
redis-cli info memory | grep used | grep human
> redis-cli info memory | grep used | grep human
used_memory_human:827.46K # 内存分配器 (jemalloc) 从操作系统分配的内存总量
used_memory_rss_human:3.61M # 操作系统看到的内存占用 ,top 命令看到的内存
used_memory_peak_human:829.41K # Redis 内存消耗的峰值
used_memory_lua_human:37.00K # lua 脚本引擎占用的内存大小
过期策略
redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定时遍历这个 字典来删除到期的 key。除了定时遍历之外,它还会使用惰性策略来删除过期的 key,所谓 惰性策略就是在客户端访问这个 key 的时候,redis 对 key 的过期时间进行检查,如果过期 了就立即删除。定时删除是集中处理,惰性删除是零散处理。
定时扫描策略
redis默认每秒会进行十次过期扫描,不会遍历字典中所有的key。而是采用了一种简单的贪心策略。
- 从过期字典中随机20个key
- 删除20个中已经过期的
- 如果过期的key比率超过1/4,重复步骤一
同时,为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时 间的上限,默认不会超过 25ms。
假如有 101 个客户端同时将请求发过来了,然后前 100 个请求的执行时间都是 25ms,那么第 101 个指令需要等待多久才能执行?2500ms,这个就是客户端的卡顿时间, 是由服务器不间断的小卡顿积少成多导致的。
所以业务开发人员一定要注意过期时间,如果有大批量的 key 过期,要给过期时间设置 一个随机范围,而不能全部在同一时间过期。
# 在目标过期时间上增加一天的随机时间
redis.expire_at(key, random.randint(86400) + expire_ts)
从库的过期策略
从库不会进行过期扫描,从库对过期的处理是被动的。主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key。
因为指令同步是异步进行的,所以主库过期的 key 的 del 指令没有及时同步到从库的 话,会出现主从数据的不一致,主库没有的数据在从库里还存在,比如上一节的集群环境分 布式锁的算法漏洞就是因为这个同步延迟产生的。
LRU
当redis内存超出物理内存限制,内存数据会和磁盘产生交换,会让redis性能极具下降。
在生产环境中,不允许redis出现交换行为,为了限制最大使用内存,redis提供了配置参数maxmemory来限制内存超出期望大小。超出maxmemory时,有以下几种策略
- noevication 不会继续服务写请求,读请求可以进行。这是默认的淘汰策略
- volatile-lru 尝试淘汰设置了过期时间的key,最少使用的key被优先淘汰,没有设置过过期时间的key不会被淘汰。
- volatile-ttl 根据key的剩余寿命ttl的值来淘汰。
- volatile-random 淘汰的key是过期key集合中随机的key
- allkeys-lru 对所有的key进行淘汰。
- allkeys-random
- volatile-xxx 对所有带过期的key进行淘汰。
LRU算法
实现 LRU 算法除了需要 key/value 字典外,还需要附加一个链表,链表中的元素按照 一定的顺序进行排列。当空间满的时候,会踢掉链表尾部的元素。当字典的某个元素被访问 时,它在链表中的位置会被移动到表头。所以链表的元素排列顺序就是元素最近被访问的时 间顺序。
位于链表尾部的元素就是不被重用的元素,所以会被踢掉。位于表头的元素就是最近刚 刚被人用过的元素,所以暂时不会被踢。
懒惰删除
删除指令 del 会直接释放对象的内存,大部分情况下,这个指令非常快,没有明显延 迟。不过如果删除的 key 是一个非常大的对象,比如一个包含了千万元素的 hash,那么删 除操作就会导致单线程卡顿。
为了解决卡顿,引入了unlink,能对删除进行懒处理,丢给后台线程回收内存。
flush
Redis 提供了 flushdb 和 flushall 指令,用来清空数据库,这也是极其缓慢的操作。
Redis 4.0 同样给这两个指令也带来了异步化,在指令后面增加 async 参数就可以将整棵大树
连根拔起,扔给后台线程慢慢焚烧。
主线程将对象的引用从「大树」中摘除后,会将这个 key 的内存回收操作包装成一个任 务,塞进异步任务队列,后台线程会从这个异步队列中取任务。任务队列被主线程和异步线 程同时操作,所以必须是一个线程安全的队列。
不是所有的 unlink 操作都会延后处理,如果对应 key 所占用的内存很小,延后处理就 没有必要了,这时候 Redis 会将对应的 key 内存立即回收,跟 del 指令一样。
异步async
Redis 需要每秒一次(可配置)同步 AOF 日志到磁盘,确保消息尽量不丢失,需要调用 sync 函数,这个操作会比较耗时,会导致主线程的效率下降,所以 Redis 也将这个操作移到 异步线程来完成。执行 AOF Sync 操作的线程是一个独立的异步线程,和前面的懒惰删除线 程不是一个线程,同样它也有一个属于自己的任务队列,队列里只用来存放 AOF Sync 任 务。
保护redis
指令安全
Redis 在配置文件中提供了 rename-command 指令用于将某些危险的指令修改成特别的 名称,用来避免人为误操作。比如在配置文件的 security 块增加下面的内容:
rename-command keys abckeysabc
如果还想执行 keys 方法,那就不能直接敲 keys 命令了,而需要键入 abckeysabc。 如
果想完全封杀某条指令,可以将指令 rename 成空串,就无法通过任何字符串指令来执行这
条指令了。
rename-command flushall ""
端口安全
运维人员务必在 Redis 的配置文件中指定监听的 IP 地址,避免这样的惨剧发 生。更进一步,还可以增加 Redis 的密码访问限制,客户端必须使用 auth 指令传入正确的 密码才可以访问 Redis,这样即使地址暴露出去了,普通黑客也无法对 Redis 进行任何指令 操作。
requirepass yoursecurepasswordhereplease
密码控制也会影响到从库复制,从库必须在配置文件里使用 masterauth 指令配置相应的
密码才可以进行复制操作。
masterauth yoursecurepasswordhereplease