Redis笔记
Redis特性
性能极高
简单稳定
持久化
集群
丰富的数据类型
哈希
字符串
列表
集合
有序集合
bitmap:二值性的数据统计,比如 1年内用户的签到记录,
hyperlog log:网页访问记录(PV),每个人访问不同的页面的时候 人次统计
GEOSpatial:地理位置相关的计算,微信里的附近的人,王者荣耀里的区排名
强大的功能
- 客户端语言广泛
- 支持ACL权限控制:redis6开始引入ACl模块
- 细颗粒:access controller list 访问控制列表,细颗粒的权限管理策略,可以针对任何人进行权限控制;
- 粗颗粒:UGO权限控制策略.只能控制到用户\组\其他;,其他里面包含ABC用户,此时做不到不同的控制
- 支持多线程IO模型,6.0之前是单线程模型,6开始支持了多线程模型;
- 单线程模型;唯一的进程接受任务以后处理任务
- 混合线程模型;;唯一的进程接受任务以后处理任务,但是有些任务和客户端无关,比如持久化,此时fork进程出来
- 多线程模型;多个线程接受任务之后,交给主进程处理;
单/多线程优缺点总结
单线程
- 优点:可维护性高,性能高,不存在并发情况,不存在线程切换的开销,不存在死锁问题;加锁解锁问题
- 缺点:性能会受影响,单线程只能使用1个处理器,所以会处理器浪费;
多线程
- 优点:结合了单多线程的优点,避开了缺点
- 缺点 么有明显不足,不是实际意义上的多线程,是多个线程收集到任务之后交给主进程处理,还是限制了性能的
- 混合进程,单线程收集任务之后,交给唯一的进程处理,但是有些事情和客户端无关,比如持久化,此时就fork新的进程处理,这就是混合;
缓存数据种类
实时同步数据
- 双十一的交易额 每秒钟都在变化,必须每秒钟都要刷新
- 如何保证?只要DB的数据发生了变化,就删除cache内的key
阶段性同步数据
金融交易平台首页会显示已注册用户数 和 平台交易额数 这2个数据可以不时时刻刻更新
如何做到?为相应的key增加时长属性,每天失效,就会每天建立新的key.
事务
事务没有隔离级别的概念,事务中的命令,分2个阶段,写阶段、执行阶段。
Redis单个命令:要么全部成功,要么全部失败(原子性)
Redis事务不保证原子性
本质
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程的中,会按照顺序执行!
一次性、顺序性、排他性!执行一系列的命令!
事务开始和执行
multi exec
事务中出错
编译时错误
命令不存在等语法错误,会导致全部命令失败
运行时错误
命令没有错误但是用法不对时,只有本条命令不存在
悲观锁
认为任何时间都会出问题,无论做什么都会加锁,就像上卫生间的时候会反锁,认为肯定会有人进来;
乐观锁
1、什么是乐观锁 我认为就是:很乐观,每次去哪拿数据的时候都认为别人不会修改数据,所以不会上锁。但是在提交更新的时候会判断一下在这期间有没有其他人更新这个数据。
2、乐观锁的原理 由CAS机制+版本机制来实现。 数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做。 (1)CAS机制:当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败。CAS 有效地说明了“ 我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可“。 (2)版本机制:CAS机制保证了在更新数据的时候没有被修改为其他数据的同步机制,版本机制就保证了没有被修改过的同步机制(意思是上面的ABA问题)。
3、乐观锁使用场景 3.1、解决幂等性问题(超卖,充值问题) 3.1.1、购票(超卖) 购票实现流程如下: step1:查询是否有票,有票的 话,继续下一步,否则提示无票,结束; step2:从用户账户扣除票款; step3:余票减一操作; 这里的话,正常情况没问题,但是比如用户连续多点了几次,或者网络问题导致的再或者多人同时购买的时候的并发情况下,step1步骤会有两个或者多个线程同时进入,这时候判断都是有票的,然后继续进入step2,step3,这时候,就可能会出现余票负数,多卖的情况;
3.1.2、充值问题 充值实现流程如下: step1:用户输入充值金额,请求后端业务系统; step2:后端生成订单,订单状态是未支付,然后再请求第三方支付接口; step3:用户端确认支付; step4:第三方支付通过我方提供的回调接口异步通知支付结果; 这个step4是有缺陷的,假如第三方支付系统问题或者网络问题,有多个线程同时执行进入 ,根据订单id查询订单信息,发现status状态都是未支付,所以都进入if里面,这时候就出现了账户重复充值的情况;
4、如何实现乐观锁 乐观锁一般来说有以下2种方式:
获取version
更新的时候比较version
获取上次修改时间
更新时候比较上次修改时间
持久化
RDB:(redis database)
单独fork1个进程,用来持久化
主从复制中,RDB就是备用了! 从机上面,AOF几乎不使用
Redis会单独创建( fork )一个子进程来进行持久化,会先将数据写入到一-个临时文件中,待持久化过程都结束了,再用这个临时文
件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢
复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢
失。Redis默认持久化的方式就是Rdb,如果最后一次持久化的过程中断点的话,最后一次的RDB文件可能会在tmp阶段丢失;
RDB保存的是demo.rdb文件
dbfilename dump.rdb
- save是持久化策略.一旦触发就 会生成rdb文件
save 900 1
save 300 10
save 60 10000
SAVE持久化触发机制
- save满足
- 执行
flushall - 退出
Redis
如何使用Rdb文件恢复数据
- 只要将
Rdb文件放在启动目录就可以了.Redis启动时候会自动加载Rdb文件
获取启动目录
CONFIG GET dir
"field": "dir",
"value": "/data" rdb文件放在这里就可以自动回复数据
优点:
- 适合大规模的数据恢复
- 如果对数据的完整性数据的要求不高,就可以使用这个rdb方式
缺点:
- 需要一定的时间间隔进程操作,如果中间宕机了,此次变动过的数据就丢失了.
备注:
- 生产环境可以对rdb文件做备份
AOF:(append only file)
将所有执行过的命令都记录下来,回复的时候,执行这个命令的集合文件
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录) ,只许追加文件但不可以改写文件, Redis
启动之初会读取该文件重新构建数据,换言之, Redis重启的话就根据日志文件的内容将写指令从前到后执行一-次以完成数据的恢复工作
默认不开启,开启的是
Rdb,需要手动开启appendonly no|yes默认的文件名是
appendfilename "appendonly.aof"
如果rdb文件或者aof文件被破坏了,可以使用config-check-rdb|config-check-aof脚本来修复对应的物理文件
优点:
- 每一-次修改都同步,文件的完整会更加好!
- 每秒同步一次,可能会丢失一秒的数据
- 从不同步,效率最高的!
缺点:
- 数据文件来说,
AOF文件占用的磁盘会更大,修复时需要的时间也更多; AOF运行效率要慢,所以redis默认用的是RDB方式
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
Redis会检查aof文件的大小,如果大于64M,就会新开1个文件
但redis默认不会重写aof文件,所以这个文件会越来越大
no-appendfsync-on-rewrite no # 因为默认不会重写,所以这个文件会越来越大
小结:
1、RDB持久化方式能够在指定的时间间隔内对你的数据进行快照存储
2、AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据, AOF命令以Redis协议追加保存每次写的操作到文件末尾, Redis还能对AOF文件进行后台重写,使得ADF文件的体积不至于过大。
3、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
4、同时开启两种持久化方式
●在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
●RDB的数据不实时,同时使用两者时服务器重启只会找AOF文件,那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库( AOF在不断变化不好备份) , 快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。
5、性能建议
●因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
●如果Enable AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价
一是带来了持续的I0
二是AOF rewrite的最后将rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要
硬盘许可,应该尽量减少AOF rewrite的频率, AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
●如果不Enable AOF , 仅靠Master Slave ReplIcation实现高可用性也可以,能省掉一大笔I0 ,也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个,微博就是这种架构。
消息队列
后台发布文章 ====> A队列 ========>关注了A队列的人都可以收到消息
监听1个队列
SUBSCRIBE hhh
发布信息到队列内
PUBLISH hhh 444
主从复制
模型A:m->2s
主从复制,读写分离
一个主人 2个仆人的方式,1个主机a有 bc2个丛集
主从复制,读写分离! 80% 的情况下都是在进行读操作! 减缓服务器的压力!架构中经常使用! 一主二从! 只要在公司中,主从复制就是必须要使用的,因为在真实的项目中不可能单机使用Redis!
主从配置,N个机器分别走不同的conf文件
port 6381
dbfilename dump6381.rdb
logfile "6381.log"
- 默认N个机器都是主节点
如果是有密码的机器,从机的conf文件内要增加主机的密码的配置项
masterauth 123123
- 然后认老大!,docker环境的老大就是老大容器所在的
dockerip
SLAVEOF 192.168.208.4 6379
以上是通过代码认主的方式 下面是conf文件认主的方式 conf方式是永久的
requirepass 123123
masterauth 123123
replicaof 192.168.208.4 6379
细节
主机写请求,从机读请求
主机死了之后,丛集无法写入
主机复活之后 从机会自动连接
丛集复活之后,期间主机写入的变量会恢复吗?
如果是conf配置的主从,就可以恢复,也就是说,一旦恢复了主从连接,数据就可以恢复如果是命令配置的主从,在模式恢复之前,数据就不会恢复
模型B:m->sm->s
1个主机a,带1个丛集b,这个丛集b又是一个主机b,带1个丛集c
此时的b节点是丛集,无法写入,此时可以完成主从复制
此时如果A宕机,b能不能直接作为主机使用呢?
- 在哨兵模式之前:B机器可以使用info no one 让自己变成主机c节点就可以来连接我了,只能手动操作
- 哨兵模式之后就可以自动把b作为主机
- 此时A恢复了,B已经是主机了,此时的B会听A的吗?不会!
全量/增量复制
Slave启动成功连接到master后会发送一个sync同步 Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后, master将传送整个数据文件到slave ,并完成一次完全同步。 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。 增量复制: Master继续将新的所有收集到的修改命令依次传给slave ,完成同步 但是只要是重新连接master , 一次完全同步(全量复制)将被自动执行.
全量复制:首次连接或者断开之后的首次连接会执行全量复制
增量复制:主机设置变量之后,丛集立马也会更新这个变量
哨兵模式
自动选择老大
当主机宕机了,丛集需要人工设置来修改为主机保证服务的可用性,但是这个操作费事费力,还会造成一段时间内的服务不可用.这种方式不推荐,所以redis从2.8版本开始就提供了哨兵架构来搞定这个问题;
单机哨兵:
多机哨兵:多个哨兵之间可以互相监督,哨兵监督主从机器之外还得监控其他哨兵,多个哨兵检测到A机器不可用了,哨兵之间就会对其他机器发起投票,在b和c内投票1个机器作为主机;
哨兵监控3个机器abc ,a是主机
当a宕机之后,哨兵会在bc内选择1个主机x出来
当a恢复之后,a也是机器x的丛集
优点:
哨兵基于主从模式,所有的主从的优点全都有
主从可以切换,故障可以转移,系统的可用性更好
- 哨兵模式就是主从模式的升级,手动到自动,更加健壮
缺点:
- Redis 不好在线扩容.如果集群的容量一旦到了上线,在线扩容就很麻烦了.如果是conf配置的,就更麻烦了.
- 实现哨兵模式实际上是挺麻烦的.
哨兵模式的全部配置
- 如果有多个哨兵集群.那就得配置多个哨兵端口
- sentinel monitor myredis 127.0.0.1 6379 1
缓存穿透和雪崩
高可用问题
缓存穿透(某个值一直查不到)
缓存穿透(查不到数据):用户想要查询一个数据,发现Redis内存数据库里没有,也就是缓存没有命中,于是向 持久层的数据库查询,发现也没有,于是本次查询失败,当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库,这会给持久层数据库造成很大的压力,出现缓存穿透。
解决:
- 布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的压力
- 缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源;
但是: 1、如果空值能被缓存起来,这就意味着缓存需要更多的空间存储更多的键 2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间的窗口不一致,在某一时间 db有了,但是此时缓存还没有,缓存会返回空.
缓存击穿(某个值过期的瞬间被查询了N次)
缓存击穿(访问量过大):是指某一个key 非常热点,在不停的扛着大的并发,大并发集中对这个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。 当某个key过期的瞬间,就会有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大。
解决:
1、设置热点数据永不过期 2、加互斥锁 使用分布式锁,保证每一个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,只需等待(对分布锁要求高)
缓存雪崩(扫地大妈把机器的线踢掉了)
缓存雪崩:指在某一个时间段,缓存集中过期失效,Redis宕机 比如 :双十一零点,抢购,这波商品应该放在缓存区,假设缓存一小时,到了凌晨一点,商品缓存过期,而对于这批商品的访问,都跑到数据库中,对于数据库,产生压力峰。所有请求都会到达存储层,存储层的调用量增加,存储层狗带(缓存服务节点的宕机,对数据库服务器造成的压力不可预知)
解决:
1、redis高可用(多增加redis)
2、限流降级(通过加锁来控制数据库写缓存的线程数量)
3、数据预热(在正式部署之前,把可能的数据先访问一遍)
4、设置随机失效keys
整体解决方案
redis高可用 这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis ,这样一台挂掉之后其他的还可以继续工作,实就是搭建的集群。(异地多活! ) 限流降级(在SpringCloud讲解过! ) 这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。 数据预热 数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一-遍,这样部分可能大量访问的数据就会加载到缓存中。在即\将发生大并发访问前手动触发加载缓存不同的key ,设置不同的过期时间,让缓存失效的时间点尽量均匀。