从海量数据中查询某一些固定前缀的key
keys命令可以实现,但是redis里key要是特别多,keys命令效率会很低
推荐使用 scan 命令scan cursor [MATCH pattern] [COUNT count]
# 游标从0开始查找十个k1开头的keyscan 0 match k1* count 10
返回的第一个便是游标
可以将此游标用来继续查询
查询到的key可能会重复,可以在代码层做去重处理
删除策略
设置有效期,到期之后不会立即回收,占据着内存
如果不设有效期,一直存在,命中率很低的资源,占据内存,内存的资源没有被合理利用
如何删除:
- 定时删除:10次/S,每隔一段时间就会查看设置了过期时间的key,100ms的间隔中默认查看3个key,key太多的时候就会导致有些key没有删除一直存在内存中
- 惰性删除:当查询一个已经过时的key,刚好key过期,这个时候才删除key并返回一个空值
- 回收算法
TTL:根据过期时间
LRU(最近最少使用):根据缓存命中率,推荐!
不回收(默认行为):满了就只读,不写
淘汰机制
因为redis的删除策略,会导致添加新的数据的时候,内存不够用
内存不足时,会执行淘汰机制,淘汰机制有八种
- volatile-lru:在设置了生存时间的key中干掉一个最近最少使用的key
- allkeys-lru:在全部key中干掉最近最少使用的key
- volatile-lfu:在设置了生存时间的key中干掉一个最近时间段内最少频次使用的key
- allkeys-lfu:在全部key中干掉最近最近时间段内最少频次使用的key
- volatile-random:在设置了生存时间的key中随机干掉一个
- allkeys-random:在全部key中随机干掉
- volatile-ttl:在设置了生存时间的key中干掉一个剩余时间最少的key
- noeviction:内存不足时直接报错,默认配置
配置文件中配置淘汰机制
指定淘汰机制,配置文件中配置
maxmemory-policy noeviction
设置redis最大内存,单位为字节
maxmemory <bytes>
持久化策略
意义:
当redis服务器发送故障,通过重启快速恢复数据,从而保护数据库,否则,重启将是空壳。
恢复数据时是快照的方式恢复
RDB
产出二进制文件,默认开启方式,在服务启动的时候恢复数据
- save指令,save指令会马上执行,会阻塞当前redis服务器,直到当前RDB过程完成,线上环境不建议使用该指令
- bgsave指令,针对save指令阻塞进行优化,后台执行,开启子进程来创建rdb文件,会牺牲一点性能,返回的信息在日志中可看,建议采用

- 上面两种都是指令的形式,有自动化方案,配置文件添加
save 900 60,表示900s内key变化量达到60次,就进行持久化,只要达到了60次没到900s也会持久化;如果900s内没达到60次,就重新计时,通常根据业务需求来设置,普遍前面大后面小 - RDB相关配置
save 900 60 # 文件存储的地址 dir /redis-7000/data # 本地文件名 通常设置为dump-{端口号}.rdb dbfilename dump-7000.rdb # 存储至本地是否压缩数据,采用LZF压缩 默认开启 关闭可节省CPU运行时间 rdbcompression yes # 是否进行RDB文件格式校验 默认开启 关闭可节约读写10%的时间 rdbchecksum yes # 后台存储中如果出现错误,停止保存操作 默认开启 stop-writes-on-bgsave-error yes
优点:紧凑压缩的二进制文件,效率较高,在某个时间点的数据快照,非常适合用于数据备份,全量复制
缺点:无法做到实时持久化,可能丢失数据
AOF
产出日志文件,默认不开启,配置文件中 appendonly yes 开启,实际上如果开启了aof,redis在启动时只会读取aof文件,而不会读取rdb文件
- 写数据过程,将指令写入缓存区,将命令同步到AOF文件

- 触发机制三种策略:
- everysec 每秒,准确性较高,性能较好,突然宕机会丢失一秒内的数据,建议使用
- always 每次操作:数据0误差,io性能较差,不建议使用
- no,由操作系统控制每次同步到AOF文件的周期,过程不可控
- 重写,比如对一个key进行多次覆盖的操作,aof文件体量越来越大,重写就是再做一份替换掉大体量的,不是在原有的文件上修改
- 超时的数据不再写入
- 忽略无效指令,只保留最终数据的写入指令
- 同一数据的多条写命令合并为一条
- 手动重写指令
bgrewriteaof - 如果体量很大,子进程就会很耗时
# 开启aof
appendonly yes
# 持久化文件名
appendfilename "appendonly.aof"
# 触发机制策略
appendfsync everysec
# 自动重写触发条件设置 两种可以都配置也可以只配置一种 不配置就是非重写
# 自动重写的最小尺寸,默认值比较大 和aof_current_size当前缓冲区的大小
# aof_current_size>auto-aof-rewrite-min-size触发自动重写
auto-aof-rewrite-min-size 64mb
# 自动重写的百分比
# 和aof_base_size(基础尺寸)对比
# (aof_current_size-aof_base_size)/aof_base_size >= auto-aof-rewrite-percentage 触发
auto-aof-rewrite-percentage 100
# 这里配置的意思就是体量超过64mb开始重写,且比上次重写后的体量增加了100%时触发重写
存储体积较大时,恢复较慢,一个个运行指令的恢复
选择之惑
- 对数据非常敏感,不允许出错,使用AOF
- 数据呈阶段有效性,可以承受数分钟内的数据丢失,使用RDB,灾难恢复选用RDB

应用场景
- 给数据库提供缓存,不使用
- 购物车数据存储设计,不使用
- 抢购,限购类、限量发放,使用
- 操作先后顺序的数据控制,可以使用
- 最新消息展示,可以使用
- 黑白名单
- 排名功能,使用
RDB-AOF混合持久化
redis4.0新功能,redis5中默认打开
开启后,AOF在重写时会直接读取RDB中的内容,重写后的新 AOF 文件前半段是 RDB 格式的全量数据,后半段是 AOF 格式的增量数据。
在配置文件中修改
aof-use-rdb-preamble yes|no
事务
开启事务multi
然后进行操作
提交事务exec,才会顺序执行操作命令
如果开启事务的过程中发现有错误
discard 取消事务
事务执行流程

监听
使用起来很简单
开启监听watch keyname ,关闭监听 unwatch
事务开启前最好是开启监听,这样多线程场景下其他线程在事务提交更新了key value那么当前事务提交必然失败
有点类似乐观锁
就需要关闭然后再次开启
高可用架构
主从,哨兵,集群是三种东西
主从复制架构
从节点要去找主节点
从节点的配置文件添加,指定主节点的ip和端口
我的版本中replicaof取代了原先的slaveof
replicaof <masterip> <masterport>
# 如果主节点配置了密码 这里就要配置
masterauth <masterpassword>
哨兵机制
主节点挂掉,从节点找不到主节点,整个主从架构就失效,所以需要用到哨兵
在每个redis节点中都要启动哨兵,哨兵会在主节点挂掉的时候在从节点中重新选举出新的主节点
配置哨兵要修改sentinel.conf配置文件,每一个节点都要配置
# 哨兵服务端口号
port 26379
# 哨兵工作信息目录
dir /tmp
# 启动守护进程
daemonize yes
# 指定master节点ip和端口
# sentinel monitor <给master起的名字> <masterip> <端口> <几个从节点>
sentinel monitor master 192.168.101.50 6379 2
# 哨兵每隔多久监听一次redis架构
sentinel down-after-milliseconds master 30000
# 主节点的名称和密码 master和slave需要密码相同
sentinel auth-pass master 123
在每个节点使用命令启动哨兵,指定哨兵配置文件启动
redis-sentinel sentinel.conf
集群
RedisCluster的理解
- 解决哨兵+主从的基本功能之外解决存储数据量的问题
- 没有中间层,客户端可以直接跟RedisCluster的任一节点连接
- ping-pang机制,每个节点之间相互通信,如果没有返回信息就被认为是挂掉了
- 投票机制,集群内投票认为哪个节点挂掉
- 每个主节点都支持读写操作,分担写的压力,后续可以继续增加新的主机,做hash迁移,新主机就可以分担压力
- 每个节点负责的区域不同,进行set的时候根据key来做crc16算法,从而得到一个数值,数值%16384=(0~18383),从而确定存储到哪个节点
- 每个节点都可以做主从,保证高可用
- 后期支持新增主节点和从节点,需要做数据迁移,从之前的主节点迁移部分数据到新增主节点
- 每一个集群节点,至少要跟着一个从节点
- 超过半数节点宕机,redis集群瘫痪

配置文件
# 开启集群
cluster-enabled yes
# 节点信息文件名
cluster-config-file nodes.conf
cluster-node-timeout 5000
# 集群对外ip地址
cluster-announce-ip 111.229.225.62
# 节点端口
cluster-announce-port 7000
# 节点间通信的端口
cluster-announce-bus-port 17000
集群具体搭建看https://www.yuque.com/kelisidiyanuodi/dttdng/mqa1so#b96b8066
Redis是多线程还是单线程?
Redis 确实是单线程模型,指的是执行 Redis 命令的核心模块是单线程的,而不是整个 Redis 实例就一个线程,Redis 其他模块还有各自模块的线程的。
单线程+多路IO复用
Redis的网络请求模块是单线程+多路IO复用——非阻塞IO
一个线程处理所有网络请求,其他模块仍用了多个线程。
阻塞IO:发送请求,没有回复就一直阻塞不能进行其他操作直到回复
非阻塞IO:发送请求,没有回复就一直发送
IO多路复用:多个客户端发起请求,这些IO操作会被暂时挂起,入内存队列,服务端可以选择什么时候读取处理这些IO,所以服务端可以同时hold多个IO,多路复用指使用一个线程来检测多个文件描述符(socket)的就绪状态,如果有一个文件描述符就绪,则返回,否则阻塞直到超时,得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动一个线程执行
多路复用又包括select,poll,epoll
select:调用select函数后,进程会阻塞,直到描述符就绪或者超时,函数返回;但是有文件描述符数量的限制,一般是1024
poll:与 select() 类似,但是 poll() 没有最大文件描述符数量的限制,基于链表来存储
epoll:给监视的请求设置标识符,标识符显示就绪状态,就不需要阻塞,在传递内核与用户空间的消息时使用了内存共享,而不是内存拷贝,在描述符读写就绪时,通过回掉函数将自己加入就绪队列中,描述符数目也不受限制
redis使用的就是epoll
缓存问题
缓存预热
现象:服务器启动后迅速宕机
问题:
- 请求数量较高
- 主从之间数据吞吐量较大,数据同步操作频度较高
解决方案:
- 前置准备工作
- 统计数据访问记录,统计访问频度较高的热点数据
- 利用LRU数据删除策略,构建数据留存队列,例如storm+kafka
- 准备工作
- 根据级别,让redis优先加载级别较高的热点数据
- 利用分布式多服务器同时进行数据读取
- 实施
- 使用脚本程序固定触发数据预热过程
- 使用CDN
缓存预热就是系统启动前,提前将相关缓存数据直接加载到缓存系统,避免在用户请求的时候先查询数据库
缓存雪崩
忽然数据库连接激增,应用服务器无法及时处理请求,大量408,500错误页面
数据库崩溃 -> 应用服务器崩溃 ->重启应用服务器无效 ->redis服务器崩溃 ->redis集群崩溃->重启数据库再次被瞬间流量放倒
问题:
- 大量的key在同一时间过期
- redis未命中,向数据库获取数据,导致数据库访问量激增,数据库忙不过来
解决方案:
- 不让大量缓存在同一时间失效,比如定时任务刷新缓存,随机设置有效时间,或者不设置有效时间(永久有效)
- 页面静态化处理
- Nginx缓存+redis缓存+ehcache缓存
- 对数据库排除,例如超时查询、耗时较高事务
- 灾难预警机制
- 限流、降级,待业务低俗运转后再初步放开
- LRU与LFU切换
- 根据业务有效期进行分类错峰,A类90分钟、B类80分钟、C类70分钟
- 定期维护
- 超热数据永久key
- 加锁,谨慎使用
缓存击穿
并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力、数据库崩溃
问题:
- 缓存中没有该数据,但数据库中有数据
- 某个key过期,但该key访问量巨大,请求都打到数据库上了
- 多个请求压到redis后,均未命中
- 短时间大量对数据库同一数据访问
解决方案:
- 加大key过期时间或永久key,但是永久key不太好
- 设置不同失效时间
- 后台启动定时任务,定时更新,刷新数据有效期
- 访问数据库的时候用上分布式锁
缓存穿透
redis命中率下降,数据库没有该数据,而用户不断请求,导致数据库崩溃
问题:
- 这时的用户很可能是攻击者、黑客,攻击,疯狂访问id不存在的数据,导致数据库压力过大
- 缓存和数据库中都没有数据
- redis大面积未命中
- 大量非正常URL访问
- redis未持久化
解决方案:
- 布隆过滤器
- 后端接口增加校验,如用户鉴权,请求校验,拦截器,拦截不合法的参数请求
- 缓存null,对查询结果为空的数据缓存处理,设定短过期时间
- 实时监控redis命中率与null数据对比
性能指标监控
命令:
- redis-benchmark [-h] [-p] [-c] [-n ] [-k],直接在linux里用,不需要连接redis-cli客户端
- 举例1:
redis-benchmark50个连接,10000此请求对应的性能 - 举例2:
redis-benchmark -c 100 -n 5000100个连接,5000次请求性能
- 举例1:
- redis-cli,连接redis-cli客户端后使用的指令
- monitor
- showlog [operator]
- get:获取慢查询日志
- len:获取慢查询日志条目数
- reset:重置慢查询日志
