1.特性

①.速度快:
 a.纯内存
 b.epoll作为i/o复用技术
 c.单线程避免了线程切换和竞争产生的消耗
   redis是属于io密集型的操作,cpu不是瓶颈,且采用了io复用,cpu并不会因io阻塞而闲置
   总结就是io密集型且cpu会成为瓶颈的时候选择多线程
②.持久化:
③.高可用: 如果系统每运行100个时间单位,会有1个时间单位无法提供服务,我们说系统的可用性是99%
④.分布式:
⑤.优缺点:
 a.优:可以做缓存、计数、社交、消息队列、排行榜
 b.缺:1.做冷数据成本很高 2.属于非关系型数据库,在做复杂关系查找时成本很高 3.对事务的支持差
 c.用lua脚本可以实现原子操作,对事务支持差是指不可回滚,跨节点的事务支持,没有隔离级别(即对事务并行的管理,没有并行所以也不需要隔离)
⑥.关系型/非关系型
 a.关系型是一个二维表存储,可以用sql在多表之间做复杂查询,对事务的支持很好,并发差(mysql/oracle)
 b.非关系型数据以对象的形式存储,每个对象的属性由自身来决定,做多表复杂关系查找成本很高(redis/mongo)

2.五种数据结构

1.string

①.批量获取mget 可以节约n-1次的网络时间
②.因为是单线程所以计数的时候不需要使用cas解决并发问题
③.常用 缓存、计数、共享session(cli访问多svr时把session放在redis)、限速(单用户单接口访问频次限制)

2.hash

①.redis最外层的key都是放在一个全局hash数据表里面 或者rehash中
  mysql的innodb表key是放在b+tree,key对应主键
  mongo todo:
②.rehash有两个hash在hash[0]容量不够的时候(负载因子>1),key会逐步迁移到扩容的hash[1]去, 一个key不会同时在0和1里面
③.hgetall针对大key,keys * 都是重命令,慎用
④.如果一定要遍历可以用scan,O(n),但是新插入会导致遍历不全的问题 (68-70页)
⑤.填充因子:表示hash填满的程度一般控制在0.7~0.8,侧面的反应了hash的大小
⑥.常用 缓存如用户信息,string用的缓存在序列化和反系列化有一定的开销
⑦.用加uid后缀和使用hash&uid作为hash_key的区别,使用hash是大key(在删除大key的时候可能会阻塞)
  hash_key是uid,hash_val如果业务复杂就只能是string类型,使用uid后缀就有可选性(string/hash/list/set/zset都可以)

3.list

①.延申类型和stl一致:
 a.lpush+lpop=stack 栈
 b.lpush+rpop=queue 队列
 c.lpush+ltrim=capped collection 有限集合
   ltrim: 对list进行修剪,只保留范围内的元素,例如ltrim key 1 -2 删除第一个和最后一个元素
 d.lpush+brpop=message queue 消息队列 这里在有数据的时候可以用lua脚本rpop+table单次弹出多个任务 减少网络的开销 demo
   brpop:在list内没元素时会阻塞,且一个元素只能被一个cli brpop

4.set

社交tag,用户有哪些tag,tag有哪些用户
spop/srandmember=random item 随机数
sadd+sinter=scocial graph 社交需求

5.zset

①.原理:skiplist(跳表)+hash,跳表和二叉树的性质差不多,一个是从上往下分,一个是从下往上跳,hash里面存了uid->score,查询uid对应score时间复杂度是O(1)
②.常用 排行榜

6.bitmaps

①.作用: bit最小单位是位的存储容器,可以用最小的内存承载更多的信息,比如可以用来存储用户基数很高且日活很好的日活用户
②.信息量: a.在1的占比为50%的时,bitmap最节约内存 b.在1占比很少的时候bitmap并不一定是一个好的选择,因为大内存承载的信息量却很少 c.在1占比很多时,0就是承载的信息
③.第一次初始化bitmaps,如果偏移量非常大,可能会造成redis阻塞,可以将大的bitmap分成多个较小的bitmap,可以避免一次性分配大量的内存空间造成阻塞

7.hyperloglog

①.常用 统计数据允许有一定的误差
②.牺牲正确性来换取内存小

3.小功能

1.慢查询 (75~78页)

①.配置
 config slowlog-log-slower-then 10000 记录>10ms的查询
 config slowlog-max-len 100 记录保留100条
 config rewrite 运行的配置跟新到配置文件中
②.slowlog get 返回慢查询,返回结果有 命令id、时间戳、执行时间、命令信息 cli信息 pic.1
③.运维建议:
 slowlog-max-len 保存多一点
 slowlog-log-slower-then 和系统所期望的QPS挂钩
 查询客户端变慢对应时间戳的日志来定位问题 (/var/log/redis/redis-server.log)
 可以定时把慢查询结果存入mysql
④.redis-benchmark -c 客户端数 -n 请求数 -q 可以测试QPS pic.2

2.pipeline

①.概念: cli将n个命令打包发送给svr以节约n-1个RTT
②.非原子操作: 有可能cli1使用了pipeline打包了cmd1234 cli2执行了cmd5 最终的执行顺序是5可能插入在x1x2x3x4x中的任意位置
③.打包的命令过多的话,会增加cli的等待时间和网络阻塞
④.只能操作一个redis实例

3.事务与lua

①.语法: multi开始事务命令、exec事务执行
②.redis不支持事务回滚
③.lua
 lua脚本可以调用redis命令
 script load 将lua脚本加载到redis内存中得到sha1值,节约脚本在网络传输的时间
 eval sha1 param 执行lua脚本
 可以实现原子性的操作多个命令
 script kill 如果脚本没有执行写操作在另外一个客户端执行这个命令可以停止当前正在执行的lua脚本,如果有执行写操作就不能被停止,只能停redis

4.bigkey/网络延时 todo:

①.redis查找bigkey pic.2

redis-cli -h localhost -p 4528 -n 6 --bigkeys -i 0.1

②.输出每个db里面有多少个key

redis-cli -h localhost -p 4528 info keyspace

③.可以检测网络延时

redis-cli -h localhost -p 4528 -n 6 --latency
redis-cli -h localhost -p 4528 -n 6 --latency-dist

4.客户端

1.连接池

①.缺: 连接池使用相对麻烦
②.优: 可以节约每次新建/关闭tcp的开销,直连资源无法控制,极端情况可能出现连接泄漏

2.client list 客户端状态
redis-cli -h localhost -p 4528 -n 6 client list

①.pic.6
②.输入缓存
 qbuf输入缓存总容量、qbuf-free 输入缓存
 输入缓存超过设定值(redis_max_querybuf_len)客户端就会断开
 导致输入缓存过大的原因redis处理速度更不上输入速度 可能原因大量bigkey、redis阻塞
③.输出缓存
 obl固定缓冲区、oll动态缓冲区(固定缓存用完用动态缓存)、omem固定和动态一共使用的内存
 cli类型被划分为 normar 普通、pubsub 发表订阅客户端、slave 用于复制(从节点内部伪装的一个cli)、
 针对不同客户端可以设置不同的输出缓存限制 a. 超过hard limit客户端断开,b.超过soft limit并持续了soft seconds秒客户端会断开
 预防因输出缓存超限制断开的方法 a.限制容易让输出缓存区增大的命令如monitor b.监控内存
④.连接时间
 age客户端已连接时间 idle最近一次空闲的时间 如果两个相等那这个cli大概率是有问题的,可以用client kill手动杀掉
 设置timeout>0,cli空闲>timeout cli就会断开,如果断开了会对正常的业务有影响
 但是如果不设置这个值,会出现没有及时释放不正常的cli,导致连接数超过maxclients,使得后面的cli没办法连接
 所以正确的方式是不要设置这个值,在连接池里面做周期性的空闲检测
⑤.flag
 N:normal客户端 S:Slave从redis P:订阅频道 M:主节点
 b:被阻塞(比如blpop命令但是list没有元素)
 i:在执行CLIENT PAUSE的客户端 O:MONITOR命令的客户端
⑥.全部参数说明(141页 表4-5)

3.client pause

①.暂停5000ms

redis-cli -h localhost -p 4528 -n 6 client pause 5000

②.作用: 阻塞客户端只对普通和发布订阅客户端有效(连不上&执行不了任何命令),对slave无效
③.应用:可以使得主从复制一致,但是线上阻塞成本是很高的

4.info clients
redis-cli -h localhost -p 4528 -n 6 info clients

connected_clients: 客户端连接数,可以设置告警阈值
client_longest_output_list: 当前所有输出缓存区中队列对象个数的最大值
client_biggest_input_buf:当前所有输入缓存区占用的最大容量
blocked_clients:正在执行阻塞命令的cli个数

5.info stats
redis-cli -h localhost -p 4528 -n 6 info stats

rejected_connections:自启动以来拒绝的客户端连接数,需要重点监控

5.持久化

1.RDB快照/AOF日志

①.RDB是一个紧凑压缩的二进制文件,非常适用于备份
②.RDB恢复数据远远快于AOF
③.RDB没办法做到秒级持久化,因为bgsave每次都fork创建子进程备份,属于重量级操作
④.新老版本的RDB备份数据无法通用,AOF是记录命令所以可以通用
⑤.AOF主要是解决了数据秒级持久化
⑥.redis重启加载 会优选选择AOF再选择RDB (161页 图5-4)

2.AOF同步策略

①.AOF把命令追加到缓存中,然后同步到AOF文件中
②.linux写文件的同步方式 conn.1.1
  write:写入系统缓冲区后返回,同步到硬盘依赖于系统调度机制
  fsync:会做强制同步到硬盘,且阻塞直到同步到硬盘后返回
③.redis的同步方式
  always:命令写入缓存以后调用fsync同步到AOF文件,优点是数据安全,弊端是TPS会降低
  everysec:命令写入缓存后系统调用write,fsync由专门的线程一秒调用一次,是建议的配置,严格上来说最多丢失2s的数据,因为2s没有执行fsync redis会被阻塞
  no:命令写入缓存以后系统调用write,然后就不管了,等linux系统自己调用fsync,通常最长同步周期是30s,优点是提高了性能,但是30s的数据安全性无法保证

redis-cli -h localhost -p 4528 CONFIG GET appendfsync                 #查看
redis-cli -h localhost -p 4528 CONFIG SET appendfsync always          #设置
3.AOF重写

①.当AOF文件的大小大于上一次重写后的(100+x)%后会重写(x可配置)
  当AOF文件>x(MB)时会触发重写(x可配置)
②.fork一个子进程对AOF文件命令合并,然后重新写入AOF
③.父进程负责重写缓冲区的命令

4.持久化对性能的影响

①.fork
  RDB和AOF都会用到fork,fork会阻塞redis,所以需要注意
  a.不要使用xen fork成本高
  b.降低fork的频次,如放宽AOF重写触发的机制
  c.单机多实例运行的时候,可以使用监控程序使得同时只有单个实例在做fork操作
②.cpu
  子进程负责把内存数据写入文件(aof&rdb都不是简单地拷贝内存,而是涉及格式处理、压缩算法等,占用大量CPU)
  这个过程属于cpu密集型操作,子进程对单核cpu的利用率接近90%,所以不要做单核绑定操作,会使得子进程抢父进程的cpu资源导致redis阻塞
③.内存
  linux有写时复制机制,所以fork并不会消耗2倍的redis内存
  避免在大量写入时做进程重写操作,这样会导致父进程维护大量内存副页面
  THP配置可提高fork速度,但复制页从4k页变成2MB,会增加内存消耗,需要关闭
④.硬盘
  不要和其他高硬盘服务部署在一起,如存储服务,消息队列等
  AOF重写时关闭fsync,可以提高效率,可能会导致重写时的数据丢失,酌情选择
  对于单机多个redis实例的情况可以分配不同的硬盘

6.复制

1.概念

①.复制是说建立从节点数据库
②.默认从数据库为只读,主从数据库做读写分离,在主从切换时可能会存在秒级的写到从节点(原主节点)导致业务失败
③.传输延时repl-disable-tcp-nodelay 可以控制主节点是否合并较小的tcp数据包一起发送给从节点

2.拓扑图

①.一主一从:可以把AOF交给从节点来处理,需要注意主节点是没有开启AOF的
       如果主数据库重启数据会丢失,而且同步到从数据库,从数据库也会丢失数据,重启主数据库需要先断开复制
②.一主多从:优点可以把比较耗时的命令丢到从服务器上去做,缺点过度消耗主节点的宽带
③.树状主从:可以解决一主多从中主节点的宽带压力

3.全量复制

①.流程
  -> 从节点发送同步命令 -> 主节点同步偏移量给从数据库 -> 主节点执行bgsave
  -> 发送RDB -> 从节点flush old data、load RDB -> 主节点发送同步期间产生的数据buf
  -> 从节点有可能AOF重写 (179页)
②.会导致全量复制的场景
  第一次复制\复制积压缓冲区没有足够的数据
  主节点运行id变化(主节点重启或者主从切换都可能导致主从数据不一致所以需要全量复制)
③.时间开销
  主节点的bgsave RDB传输时间\从节点清空数据\从节点加载RDB\从节点可能的AOF重写
④.导致复制失败的可能原因
  DB传送超repl-timeout\复制过程中产生的新数据buf>client-output-buffer-limmit
⑤.日志说明
  M:当前主节点日志 S:当前从节点日志 C:子进程日志

4.部分复制

①.偏移量:主从节点会记录已执行的命令的字节长度的累加值
②.复制积压缓冲区: 固定大小1M,主节点会把已执行命令写入
③.如果主节点复制积压缓冲区有差的这些命令偏移数据则直接发给从节点,缓冲区可以配置大一点避免全复制

5.心跳

①.作用:主节点判断从节点是否还在、从节点上报自己当前的偏移量

6.运维

①.延时字节数过高,可以加个监控程序修改读命令路由到其他从节点或者主节点
②.读从库并不会判断数据是否过期,redis在3.2以上版本配置了是否返回过期数据
③.主从内存相关配置需要保存一致,否则会导致主从数据不一致
④.当主节点出问题时,手动切换从节点为主节点或者采用哨兵或者集群避免面全量复制
⑤.采用树结构拓扑来代替一主多从防止主节点重启的复制风暴
⑥.多例部署的时候主节点应该分散在不同的机器上,避免多实例同时进行复制
⑦.可以通过info replication查看主从节点的偏移量

7.阻塞

1.内在原因

①.慢查询
  解决方法: 改用时间复杂度度低的命令,把大对象拆分成小对象
②.cpu饱和
  top 查看cpu使用率高的程序 P按cpu排序 M按内存排序
  redis-cli –stat 查看ops如果很高需要集群水平分担ops压力(requests(x): x:ops)
  info commandstats 查看是否有开销不合理的命令 (平均耗时长(usec_per_call) & 调用次数多(calls))
③.持久化阻塞
  info stats 查看上一次fork耗时latest_fork_usec
  AOF刷盘组塞 redis会日志告警 磁盘压力也可能是其他进程导致的 可以用iotop查看

2.外在原因

①.cpu竞争
  和其他cpu密集型服务放在同一台物理机器上
  绑定cpu 绑定cpu会导致子进程和父进程共享同一cpu,子进程在进行重写cpu使用率在90%+

②.内存交换
  cat /proc/pid/smaps | grep Swap 可以查看进程使用swap的情况
  这种情况和redis的高效率时背道而驰的,需要确保机器有充足的物理内存

③.网络
  避免cli和redis之间异地跨机房调用。宽带速度,同物理机>同机架>跨机架>同城机房>异地机房
  redis的客户端超过maxclients
  tcp的连接数限制
  已经完成3次握手的tcp队列满了
  网卡软中断: 单个网卡队列只能使用一个cpu,高并发下网卡数据交互都集中在同一个cpu

3.CacheCloud

①.发现阻塞建议使用CacheCloud,监控的指标有命令耗时、慢查询、持久化阻塞、连接拒绝、CPU\内存\网络\磁盘使用过载等

8.内存

1.划分

①.redis自身内存,这一部分很小可以忽略
②.对象内存:每个数据都有key对象和value对象,
  可以理解为sizeof(keys)+sizeof(values)
③.缓存内存:客户端缓存,复制积压缓存(用于部分复制),AOF缓存(AOF重写时保存新写入的命令)
④.内存碎片
  导致内存碎片的原因是频繁的执行append、setrange等更新操作,大量过期键删除
  查看: info memory -> mem_fragmentation_ratio:2.97
     redis向操作系统申请的物理内存是它自身使用内存的2.97倍 说明碎片化很严重
  解决: 数据对其,节点安全重启
⑤.子进程内存消耗

2.管理

①.设置内存上限,只是限制used_memory,并不包括内存碎片,
  所以实际的消耗是会更大的,需要小心物理内存不够的情况
②.内存回收:惰性删除+定时任务删除(允许有25%的过期键存在)
③.内存溢出策略:
  a.不删除任何数据/删除超时数据/随意删除(207页)
  b.内存溢出每次写入数据都会出发删除策略,严重影响效率,可以把内存上限改小,一次性回收到位

3.优化

①.key缩减键的长度 value使用更高效的系列化工具如protobuff
②.共享对象池,只针对数值
③.字符串重构使用hash结构(就是使用hash作为单个用户缓存数据有多维度的数据) 注:内部结构为ziplist的hash
④.编码优化: 追求空间和时间的平衡
⑤.控制redis中的keys数量
  使用客户端手动哈希分组 如key为1948480 等价于 key=group:hash:1948 field=480
  缺点是无法使用键过期

9.哨兵

1.概念

①.作用: 解决主从复制中主节点出问题需要人工操作的问题
②.人工操作:
  -> 主节点宕机 -> 从节点晋级为新主节点 -> cli连接到新主节点
  -> 其余从节点复制新主节点 -> 原主节点恢复后复制新主节点
③.哨兵流程: 发现节点不可达
->确定为客观不可达max(quorum,哨兵总数的一半+1)个哨兵认为不可达
->选取一个sentinel(哨兵节点)来完成上面的人工操作
④.挑选从节点成为主节点的标准:
  过滤掉不健康的节点,根据配置选择优先级高的节点(slave-priority),选择复制偏移量最大的,选择runid最小的(只是确保最终有一个结果前面条件一致时它们其实都可以)

2.配置

①.
  quorum: 至少需要多少个哨兵认为不可达才为客观
  down-after-milliseconds: 超过多少时间没有有效回复,才算不可达
  parallel-syncs: 新主节点确认以后,每次有多少个节点执行复制
②.监控多个主节点(即多例),只需要指定多个masterName来区分不同主节点就可以了
③.运维
  sentinel不应该部署在同一台物理机上
  至少部署3个且奇数个sentinel
  如果是同一个业务的多个主节点集合部署一个sentinel集合就好
  如果不是同一个业务,就一个主节点一个sentinel集合,会有更好的健壮性
  sentinel节点配置尽可能一致,判断故障节点会更准确,sentinel不存数据随时可以更新配置

3.api

①.
  sentinel masters: 展示被监控的主节点相关信息
  sentinel slaves : 展示主节点对应从节点相关信息
  sentinel sentinels : 展示哨兵节点信息
  sentinel failover : 主节点强制故障转移,可以手动更换一个配置更高的主节点
  sentinel flushconfig: 配置刷新到硬盘上

4.客户端

①.连接过程: 遍历所有哨兵找到一个可用的->通过该哨兵返回主节点信息->验证是否为真的主节点->和哨兵节点集合保存通讯
②.高可用读写分离: 应该把所有从节点看作成一个资源池,无论上线还是下线从节点,客户端都能及时感知到
          而不是一个客户端连死一个从节点,那该从节点挂了,就不是高可用读性分离了

5.实现原理

①.每10秒
  每个sentinel节点会向主节点和从节点发送info获取最新的redis节点拓扑结构
  通过主节点可以获取从节点的信息
  加入新的节点或者节点不可达都能被sentinel感知

②.每2秒
  每个sentinel会向redis数据节点的__sentinel__:hello频道发送该sentinel节点对主节点的判断以及当前sentinel节点的信息,同时订阅该频道
  可以发现新的sentinel节点
  sentinel之间交换主节点的状态

③.每1秒
  每1秒,心跳判断其他sentinel节点是否不可达

间隔 行为 目标 说明
10秒 发送 info 命令 redis主从节点 获取拓扑结构(主从关系)和最新状态
2秒 向频道 __sentinel__:hello 发布信息+订阅 sentinel节点 发现新的sentinel,交换对主节点的看法
1秒 发送 PING 心跳 其他sentinel节点 判断sentinel是否不可达

10.集群

1.数据分布方式

①.节点取余分区
  / 当扩容或者收缩节点,会导致节点映射需要重新计算,导致数据的重新迁移
  / 扩容是可以采用翻倍扩容,避免数据映射被全面打乱
②.一致性哈希分区(276页图10-3)
  * 加入和删除节点只会影响相邻的节点
  / 节点少时的节点变化会大范围影响哈希环中的映射关系
  / 需要翻倍或减半节点才能保证负载均衡(可以通过虚拟节点来解决)
③.虚拟槽分区 key->slot->node
  * 通过中间槽解耦了数据和节点之间的关系
  * 支持key、槽、节点之间的映射查询,用于数据路由、在线扩容收缩
  * 集群伸缩=以槽为单位的数据在节点之间的移动
④.集群的限制
  / key的批量操作(mget/pipeline)支持有限,可以通过hashtag操作使得keys分布在同一个槽内,会影响负载均衡
  / key事务操作支持有限,同上
  / key作为数据分区的最小粒度,不能将大key分布在不同的节点上
  / 复制拓扑结构只支持一层,不支持树结构

2.搭建/原理

①.搭建
  准备节点->节点握手->分配槽
  建议使用redis-trib.rb搭建集群
②.原理
  meet: 新节点加入,只要在集群内的任意一个节点上执行meet,握手状态就会在集群内传播
    如果在集群里面的节点,用命令加入到其他集群会导致数据错乱,建议使用工具执行meet命令
  ping: 每秒执行10次,选择最后通讯时间>note-timeout的节点,发送节点自身信息和1/10的其他节点信息
  pong: ping/meet消息的响应消息
  fail: 主观的判断某个节点下线

3.路由

①.节点对于不属于它的键命令只回复重定向响应,不负责转发
②.通过ping信息,每个节点都会知道其他节点的槽信息
③.smart客户端: 在客户端内部维护slot->node的映射关系

4.运维

①.官方建议集群节点控制在1000以内,过大会严重消耗宽带
②.数据倾斜
  节点和槽分配严重不均,redis-trib.rb info {host:prot} 获取槽和节点映射关系
  不同槽中key数量差异过大,过度使用hashtag,通过命令可以查看槽的键数量、所有的key
  存在bigkey,使用migrate键迁移
  内存配置不一致
③.请求倾斜
  热点数据拆分
  不要对热键数据使用hashtag,会导致热点数据被分配到同一个槽 -> 同一个节点
  对一致性要求不高的场景,客户端可以使用本地缓存
④.读写分离
  集群下的读写分离成本比较高 (这里主要指的是集群不支持读写分离,需要开发中间件的成本)
  用多从节点做跨机房部署降低读命令网络延迟(集群支持一主多从,不支持树状拓扑图)
  主节点出现故障时,把读业务路由到从节点,保证读业务的正常使用
⑤.主从切换命令
  cluster failover 手动切换集群中的主从角色

11.缓存

1.更新策略

  算法剔除: 缓存不设计过期时间,只有当缓存超过预设值时通过算法对现有的缓存进行剔除
  超时剔除: 数据设置过期时间,到时间在从源数据获取数据,放入缓存数据设置过期时间
  主动跟新: 通过消息系统通知缓存跟新
  适用场景:
      低一致性的业务可以使用最大内存加算法剔除的方式
      高一致的业务可以结合使用超时剔除加主动更新

2.热点key的重建

①.问题
  假设数据重建是一个复杂的计算,热点数据在缓存过期的瞬间,大量线程来重建缓存,会造成后端负担
②.解决方案
  a.在重建数据的地方加锁保证只有一个线程在重建数据
  b.数据不设置过期时间,只设置一个逻辑过期时间,在发现逻辑过期后,会有一个单独的线程去构建数据,最后路由替换 (和c++并发4.1.3.d rcu是同一原理)

3.缓存穿透

①.概念
  指查询一个根本不存在的数据,缓存和存储层都不会命中,每次都要查到存储层,失去了缓存的意义
②.原因
  业务代码或者数据出现问题/恶意攻击/爬虫
③.解决
  a.给空值做缓存,适合数据变换高的场景,会需要更多的缓存空间,如果是恶意攻击的话问题就会更严重,可以给缓存加个过期时间
  b.布隆过滤,在缓存之前用所有的key做一次拦截(351页图11-5),适用于数据相对比较固定的场景,缓存空间占用少

4.无底洞

①.概念: 加大量缓存的但是性能不会得到提升
②.原因: 大量节点做水平扩容,导致keys分布在更多的节点上,一个业务获取多个key需要更多次数的网络io
③.解决
  a.串行命令,逐次的执行n个get命令,n次网络时间+n次命令执行时间
  b.串行io,相当于smart cli 知道数据在哪个节点上,node次网络时间+n次命令执行
  c.并行io,创建多线程单次向多个节点发送命令,最慢的节点max_slow(网络时间+执行命令时间)
  d.hash_tag: 将相关的keys强行分配到一个槽点,1次网络时间+n次命令执行时间,会导致数据倾斜

5.雪崩

①.概念: 缓存由于某些原因不可用了,大量请求会直接到达存储层,造成存储层宕机
②.解决
  a.提高缓存的高可用性
  b.让redis、mysql都运行在自己的线程池中,以限定资源的使用

12.陷阱

1.Linux配置优化

①.vm.overcommit_memory
  =0 内核检测是否有足够的内存,有则内存申请通过,没有申请失败错误返回给进程
  =1 内核运行超量使用内存直到用完为止
  =2 内核决不过量的使用内存,即内存地址空间不超过swap+overcommit_ratio比例的RAM值
  建议使用=1,同时redis设置maxmemory,即让redis来接管内核的内存管理
②.swapniess

cat /proc/sys/vm/swappiness    #查看swapniess
sudo sysctl vm.swappiness=10   #修改swapniess
free -mh                       #查看swap的使用情况
vmstat 1                       #每秒打印系统swap&其他系统信息状态
cat /proc/$pid/smaps           #查看单进程内存占用情况
值范围 行为
0 尽量不使用swap,除非内存真的满了(更容易触发OOM)
1-10 尽量用物理内存,swap最后才用
60(默认) 比较平衡(默认值)
100 非常积极使用 swap,即使内存空闲也可能换出

③.OOM
  概念: OOM killer值越大,在内存不足时越容易被内核kill掉 echo $val >> /proc/$pid/oom_adj
④.THP: 开启支持大内存页(2MB)分配,原是4kB
  开启会大幅增加重写期间父进程的内存消耗,同时复制内存页放大了512倍会拖慢写操作的执行时间
⑤.NTP: 保证不同机器时钟一致的服务,有利于问题排查
⑥.ulimit: 设置open files参数,防止TCP连接数受限制,即客户端数目受限
⑦.TCP backlog: 完成三次握手后等待accept的tcp连接队列长度限制

2.flushall/flushdb误操作

①.作用: flushall:清空实例的所有数据 flushdb:清空实例当前db的数据
     只不过是在AOF中追加了一条命令记录,但是发送了重写就意味着数据都丢了
②.调大重写的配置(减少重写触发)
  auto-aof-rewrite-percentage(AFO大小是上次重写后的多少倍)
  auto-aof-rewrite-min-size(最少达到多大) 才会重写
③.RDB补救
  自动RDB策略 save 60 10000 #60秒内有10000个key发生变化则执行bgsave
  如果开启了RDB自动备份策略那么基本上RDB就会被替换掉,因为删除操作的key很多
  RDB恢复的数据是上次备份的数据,只是说尽力的挽回损失
④.快速操作:
  redis-cli -h localhost -p 4528
  config set auto-aof-rewrite-percentage 1000
  config set auto-aof-rewrite-min-size 100000000000
  去除AOF中的flush操作
  重启reidis主节点,恢复数据
⑤.预防
  rename-command flushall u2uhrh3h3h934 将这两个字符在redis中输入的效果调换
  对应的cli命令代码也需要修改
  不支持config set需要在第一次启动时配置好配置文件
  AOF包含了之前的命令则redis无法启动,因为之前的命令已经无法识别
  建议只对危险命令使用 如flushall,主从配置要保存一致

3.安全redis

①.防火墙限制输入和输出的ip、端口
②.bind,指定redis和那一块网卡进行绑定,redis只能通过绑定的那块网卡进行访问 加上操作①就只有特定的ip能访问redis
③.定期备份/不使用默认端口/非root启动

4.bigkey

①.宽带计算
  假设一个bigkey大小是1MB,每秒访问1000次,
  需要的宽带是1000MB/s,千兆网卡的字节速度是(128MB/s)
②.发现bigkey
  被动方式: 在客户端抛出异常时打印所操作的key
  主动方式: scan+debug object 遍历key的大小,可能会阻塞redis建议在从节点上执行
③.删除bigkey
  删除string的bigkey不会造成redis阻塞
  删除其他类型的bigkey会造成redis阻塞,建议设置逻辑过期时间然后使用scan优雅删除数据
  redis 4.0版本将支持lazy delete free模式 删除bigkey不会阻塞redis

5.寻找热点key

①.客户端统计
  如果使用的key多可能会导致内存泄漏
  只能统计单个客户端,需要累计所有的
  不同客户端可能使用的语言不一致,维护成本高
②.代理统计,对拓扑图有要求387页图12-5
③.monitor,在高并发会导致内存暴增影响redis性能
④.抓包统计,redis用的是简单的resp协议
⑤.热点数据的优化:
  a.拆分复杂数据,部署在不同机器不同节点
  b.迁移key到新的机器上
  c.本地缓存,数据的实时性没有保证

文档更新时间: 2026-03-31 19:24   作者:morninglu