系统命令
shutdown 正常关闭服务器
redis-server 启动服务器
redis-cli 客户端连接服务器
flushall 删库跑路,一般不这么做

REDIS 持久化 RDB AOF 区别?

RDB:[Redis Database] 在指定时间间隔把内存中的数据快照写入磁盘,之后可以备份快照,或者复制到其他服务器创建相同副本,或者服务器重启也会用到这个快照恢复数据,默认持久化方式

触发时机
手动执行save和bgsave时
配置文件 设置  save <seconds> <changes>,自动间隔执行
主从复制时
执行flushall时
执行shutdown时

save 
阻塞redis服务器进程,完成之前服务器都是炸的
bgsave
主进程会fork一个子进程出来创建RDB,先把数据写入临时文件,再用二进制压缩并替换之前的RDB文件,全程主进程不受影响继续工作

##redis.conf配置文件  save <seconds> <changes>
`save 900 1` 当时间到900秒时,如果至少有1个key发生变化,就会自动触发`bgsave`命令创建快照
`save 300 10` 当时间到300秒时,如果至少有10个key发生变化,就会自动触发`bgsave`命令创建快照  
`save 60 10000` 当时间到60秒时,如果至少有10000个key发生变化,就会自动触发`bgsave`命令创建快照
----------------
AOF:[Append Only File] 默认没有开启,开启后每执行一条写命令就会记录到aof_buf缓存,随后写入AOF文件末尾,且根据配置定时重写压缩文件,多条合并成一条

##redis.conf配置文件
appendonly yes    //是否开启
appendfilename "appendonly.aof"   //日志名称
appendfsync always/everysec/no  每次/每秒/操作系统自行决定,默认是everysec
no-appendfsync-on-rewrite no //重写期间是否同步
auto-aof-rewrite-percentage 100 //日志文件如果增长100%触发重写
auto-aof-rewrite-min-size 64mb//日志文件大于64mb时,才会触发重写,权重比上面大

AOF重写
手动触发bgrewriteaof 和 bgsave 同一个原理,这里按下不表
重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,跟RDB相似,RDB存的是二进制压缩数据,AOF存的是日志

混合持久化
aof-use-rdb-preamble yes //开启,默认关闭
bgrewriteaof时fork出的子进程先将数据以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件

 

RESP 协议 [REdis Serialization Protocol]

redis 客户端 和 redis-server 之间的通讯协议,这个了解一下就好

"+OK\\r\\n"//simple string
"-ERR unknown command 'foobar'"//error msg
":1000\\r\\n"//integer
"$12\\r\\nHello World!\\r\\n"//bulk string
"$0\\r\\n\\r\\n"  //空字符串
"$-1\\r\\n"    //nil
"*2\\r\\n$5\\r\\nhello\\r\\n$5\\r\\nworld\\r\\n"//数组
单机模式
优点:部署简单,成本低,高性能,不需要同步数据
缺点:有宕机风险,单线程受限于CPU处理能力,单机承载QPS大概在几万左右,如果QPS达到10万+,单机模式会直接挂掉
主从复制
slaveof 192.168.1.1 6379 //Version<5.0
replicaof 192.168.249.20 6379 //Version>=5.0
成功配置一个从服务器, 从服务器向主服务器发送一个SYNC命令,主服务器BGSAVE,完成后发送RDB文件到从服务器,从服务器加载RDB文件启动,第一次连接是全量重同步
之后主服务器每执行一次写命令都会顺便发一份给从服务器,如果主从断开,重连的时候会根据offset复制偏移量部分重同步,比全量重同步块一些
优点:增加从服务器,QPS增加,降低MASTER读压力
缺点:主服务器写压力没有解决,宕机无法继续写入,从节点晋升主节点需要改代码的配置,和从服务器的配置,及其麻烦
哨兵模式 Sentinel
相当于主从模式下,给每台(主从)服务器添加一个进程,根据配置定时每秒,每十秒去判断服务器是否宕机,如果是主服务器宕机,则当有足够数量的哨兵进程确定主服务器宕机之后,将投票从从服务器中选举出新的主服务器,代码方面需要执行SENTINEL相关命令获取当前的主服务器是哪台,再实例化操作
优点:主服务器宕机的时候,不用半夜跑起来一顿操作,全自动化
缺点:已然没有解决主库写压力,QPS大的时候,该宕机还得宕机
集群模式  REDIS CLUSTER  version>3.0
采用无中心结构,至少6个节点(1主1从 乘以3)才能保证高可用集群,3个主服务器采用虚拟哈希槽分区,公式为
hash_slot = crc16(key) % 16384 ;
哈希槽的区间为[0,16383],扩容和缩容都是对槽的重新分配,服务不需要下线
某一组主节点服务器宕机,他的从节点会升级成主节点,宕掉的主节点重启之后会变成新主节点的从节点
也就是说每个主节点只保存有部分数据,不是全部,不过如果某一组主节点和他的从节点都宕机的话,整个集群会挂掉
优点:有条件的话肯定用这个方案啦
缺点:搭建发杂,用docker搭建会好点

一致性哈希

传统哈希取模算法 N表示服务器的总数
hash(key) % N  每个键都根据哈希算法分布到N台服务器中,但是当N变化时(宕机/添加)会导致键和服务器的映射关系发生变化导致缓存失效,新的键值不影响,主要是旧的键值,hash(key1)%3=1突然变成hash(key1)%2=2,到第2台服务器发现没有key1,如果同一时刻发生大量缓存失效,会导致缓存雪崩
一致性哈希算法
通过一个哈希环数据结构实现,环的取值范围[0-2^32-1]
将服务器的主机名或者IP进行哈希运算映射到哈希环上
hash(server.name/server.ip) 
将键值通过哈希运算也映射到哈希环上,在哈希环上顺时针寻找最近哈希值的服务器,把值存进去
服务器增加的时候同样哈希运算映射到哈希环,只会影响当前哈希值逆时针上一个服务器到当前哈希值之间的区间对象需要重新分配服务器,其他服务器没影响
服务器减少的时候,也只是需要把当前减少的服务器哈希值到逆时针上一个服务器哈希值之间的区间对象重新分配,其他服务器没影响
虚拟节点
可以把一台服务器虚拟成多个节点,使数据分布更加分散,但是节点越多,添加和减少服务器时重新分配的对象就越多,可以权衡一下

 分布式锁

单机分布式锁
set lock_resource_id 1 nx ex 10 //原子操作,抢锁10秒自动释放
正常情况:A获得锁,A执行逻辑,成功之后,A在10秒内释放锁
异常情况:A获得锁,A执行逻辑,超过10秒,redis主动释放锁
B获得锁,A完成业务,然后把B的锁给释放了,
拉都拉不住,此时B的心态逐渐发生了一些变化
解决方案:
避免在可能会超时的场景使用锁,加锁时可以把value设置成一个自己知道的数字,释放的时候判断一下是否仍是自己的锁再删除,判断和删除是两个原子性操作,需要用到LUA脚本才能实现,这并不是一个完美解决方案,这里没有解决A超时锁被提前释放,B乘虚而入的问题,不过这也是一个无解的问题
set lock_resource_id thread_id nx ex 10
##LUA脚本##
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end    
##LUA脚本##
Redisson分布式锁
开启一个定时的守护线程/进程,判断是否要给当前锁添加过期时间,也就是再给你10秒钟
Redlock
单节点不需要用到redlock
多节点,主从复制的时候,会发生A从主节点获取锁,没同步到从节点发生故障转移,从节点被选举成主节点,B获得相同的锁,就变成两个客户端都获取到同一把锁的状态
redlock原理:客户端向所有主节点获取锁,当且仅当从一半以上的节点取到锁,且使用的时间小于锁失效的时间,才算获得成功,释放的时候向全部节点释放
php有现成的包(暴露身份了),使用简单
https://github.com/ronnylt/redlock-php
这个算法不是100可靠的,详情可以去研究
https://redis.io/topics/distlock

异步队列

List 常用命令
lpush,lpop,blpop(blocking 堵塞)
rpush,rpop,brpop(blocking 堵塞)
rpoplpush,brpoplpush(blocking 堵塞)
llen,lrange

可以用(b)rpoplpush把队列的值pop出来push到另一个队列里面,业务处理完再删除,有问题就回滚,实现消息确认机制,原子性操作
rpoplpush myqueue queuebak

缺点:不能生产一次消费多次,其实不算缺点,设计如此

PUB/SUB

发布/订阅 可以实现生产一次消费多次,但是不能实现消息持久化,客户端下线之后,就会丢失信息,设计如此

stream Version>=5.0

就目前来说,stream还不能当做主流MQ使用,慎用在生产环境
stream实现了消费组消费和应答机制,简单使用如下

xadd mystream * k1 v1 k2 v2 k3 v3//添加元素
xadd mystream 1609404470049-1 k4 v4//添加元素
xrange mystream - + //查看全部元素
xdel mystream 1609404470049-1//删除元素
xdel mystream 删除key
xlen mystream//容量
##独立消费
xread count 2 streams mystream 0//从头开始读2条
xread count 2 streams mystream $//从尾开始读最新
xread block 0 streams mysteam $//永久堵塞读最新
xread streams mystream1 mystream2 $ $//同时读两个
##消费组消费 每个组状态独立,互不影响,同一个组内多个消费者是竞争关系,每个消费组都有一个游标last_delivered_id在数组上往前移动,表示消费组消费到哪条信息,消费未应答的id存储在pending_ids里面.
xgroup create mystream mygroup $//创建组读最新数据
xinfo stream mystream//查看stream和消费者组的信息
xreadgroup group mygroup c1 count1 streams mystream //消费组mygroup的消费者c1消费1条数据

延时队列

通过zset的score来实现
zadd key 100 member1
zadd key 110 member2
zadd key 120 member3
zrangebyscore key  100 100 //member1
zrem key member1

布隆过滤器

原理:使用多个不同的哈希函数把元素哈希到一个BIT向量表中,二向箔打击,非常节省空间和成本,查找也快,缺点就是判断元素是否已经存在会有误差,很小,但是无法完全消除,不过判断元素是否不存在准确率是100%,另外不支持删除操作,REDIS并不自带布隆过滤器的实现,需要应用端实现多个哈希函数和利用REIDS的SETBIT,GETBIT命令实现
应用:
1.大量的邮件,URL去重,过滤,识别
2.记录用户已经读过的文章
3.解决缓存穿透的问题

缓存穿透

一般的业务逻辑是先去REDIS查询有没缓存KEY,如果没有就去数据库查,但是一些恶意请求会故意查询不存在的key,导致每个请求直达数据库
解决方案:
1.查询结果为空的情况也缓存进REDIS,过期时间设置稍微短些
2.将所有可能存在的KEY哈希到一个足够大的bitmap中,对一定不存在的key进行过滤,用到上面说的布隆过滤器

缓存雪崩

REDIS重启或者大量缓存同一个时间点失效,这样请求又直达数据库,引起连锁反应
解决方案:
1.不同的KEY,设置不同的过期时间,可以带个随机数