https://www.nowcoder.com/discuss/723854
这个整理的比我强多了鸭
Nosql not only sql
来源:固定列的数据库已经满足不了人们所需,对已经建好的数据库插入列是很难操作的事情,于是出现了非关系型数据库
redis瓶颈在于网络和内存大小,cpu处理速度不是瓶颈(至少暂时,以及未来一段时间内不是)
redis特性
1、内存存储,持久化数据
2、数据结构简单,Redis中的数据结构是专门进行设计的;(跳表dancing link)
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
redis要接受进程完整的指令需要等待,这就很呆,如果我们可以在redis执行某个进程指令时,将其他指令的io存下来,然后通过某些方式监听,然后在当前进程发送指令时让他先发着,把该进程阻塞,去执行已经发送完的指令会不会更快一点呢
这就是IO多路复用(很多知识点完全就是知不知道的事情,不要因为专业的名词而去害怕了解他,要相信大部分人都是憨批,大家都能理解的你也可以)
在6.0以后Redis变为多线程 但内核还是单线程 仅仅是IO多线程,但不再是单核处理器,在多核计算机下可以使用多核,效率会在原有基础上增加一倍不止
(我的理解如下,有错的话谢谢指导,欢迎批评)
就是一个线程接收指令,然后将IO操作分派给多个IO线程,自己也会执行一个任务,然后进入自旋状态,然后等待所有io线程完成,清空所有线程的任务队列
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
数据类型
string 字符串整数浮点数
list 列表(双端链表)
set 无序集合(c++的set是下面的zset)
hash 散列 key-value
zset 有序集合 跳表实现
每种数据类型底层数据结构不同(后续添加)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis 事务
redis也支持事务 但是没有隔离级别
redis的事务是将命令序列化,然后依次执行(中途有错误命令会跳过,所以没有原子性)
redis可以实现乐观锁
这是因为redis有一个watch功能如果在事务执行期间其他事务对数据进行了修改,该事务会自动放弃
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis 持久化
redis数据在内存中,断电即失,所以必须要持久化数据
RDB持久化
(只讲当前使用的做法)将当前的数据储存为快照,然后单独开一个子进程,将快照中的数据写进磁盘中,
优势 :适合大量不特别要求精度数据的备份,恢复数据也快
劣势:如果在快照中的数据写入磁盘途中,父进程修改的数据不会被保存,会造成数据精度的误差
AOF持久化
将每一个write命令写进日志文件中
坏处:日志文件会越来越大,恢复的慢,当到一定程度时就会重构日志
好处:AOF可以更好的保护数据不丢失
AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复
AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis过期key的淘汰策略
设计key的过期时间,时间到了就淘汰
那么redis如何得知那些key时间到了呢?
1.定时删除
时间到了就删除,但是当redis业务量大时,cpu浪费在无关业务上会造成redis 性能,吞吐量下降
2.惰性删除
取key的时候看看过期时间到了没,有就删,但是这样会造成内存的压力
3.定期删除(1+2)
隔一段时间随机抽取一批设置了过期时间的key检查是否过期
取用key时看是否过期
比前两个好但还是不够好
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis缓存淘汰策略
缓存淘汰(只记常用的了,脑容量不够了)
当写入一个新key而内存不够时,淘汰最近最少使用
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis主从复制
想要了解的更细致一点的话,去百度别人的博客吧
我这里只做一点简单的介绍
假设4个redis 一个做主机,3个做从机
主机只进行写操作,然后将同样的写命令给三个从机(主从复制),三台从机负责执行读命令
(可以想下淘宝,读的命令显著的多于写命令)
特性:
数据冗余
故障恢复(四台都有同样数据)
负载均衡(读写分离了)
高可用(集群,哨兵)基石
如何实现主从复制?
2.8前是只支持全局增量,就是重构从库,这就很不方便
2.8后支持局部增量,就是将对主库写的指令同步给从库
如何保证主库从库数据一致性?
主库不是写么,从库不是读么,假设有个key更新了,但是主库还没复制给从库,你去从库查询数据不就挂了么,那怎么办呢?
1.从库复制完了才可以查,这就很憨,也很慢,局限性也很大
2.来个中间件,写操作后时间T(主从复制时间)内操作都在主库上(这也很憨)
3.来个cache缓存请求,所有的操作都通过cache,写操作会被保存,并设置过期时间T,如果读操作在cache中就去主库查
当主库发送更新时,会向从发送同样的更新指令,更新成功会有ack返回,如果断开链接,等到重新链接是会吧该段时间内的更新指令重新发送给从库,整个过程是异步完成的
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis哨兵模式(同样的这里也只是简单介绍)
如果redis集群的中主机宕机了,我们可以选择一台从机来当主机,人工的话太麻烦就想办法让其自动化,哨兵就是用于监视redis主机的,就是没事发个信息问你还活着没,但是哨兵挂了的吧,怎么办,所以我们有哨兵集群,互相监视,奇数个哨兵互相监视+共同监视主机,哨兵坏了就立马替换,保证哨兵总数,当主机宕机的时候,哨兵投票选择从机当主机
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis缓存穿透
缓存就是cache,将部分数据放进缓存中并设置过期时间,查询缓存中的数据会快一些
当多个用户同时访问不在缓存中也不在数据库的数据就会每次直接去数据库底层访问,数据也缓存不了,会给服务器造成极大的压力
这就叫缓存穿透
解决方法,给不存在的数据设置个null值
bool过滤器(解释下,就是对一个变量多个hash函数后查该变量的哈希值是否存在,这样的话不存在的结果是准确的,存在的结果是不准确的)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
缓存击穿
场景:过气明星突然爆火,多人并发查询
热点数据过期,大量并发同时访问
问题:缓存中不存在数据库存在的数据,多个用户并发查找该数据,会在该数据没上传至缓存前对服务器造成很大的压力
解决思路: 我们假设每个向DB的请求都是并发的
然后加锁(每个key都有个锁),redis向mysql请求数据的时候其他的请求sleep,等待返回后,查看redis缓存中是否含有,再决定是否抢锁,向DB请求
互斥锁解法就是这样
设置热点数据永不过期(这也很好理解)
接口限流熔断降级(不理解)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
缓存雪崩
多个缓存同时过期,同时有大量查询
解决方案:
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
- 设置热点数据永远不过期
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis如何做缓存预热?
1.提前将热点数据塞进去
那你怎么知道那些数据是热点数据呢?
(预热数据的时候就很容易雪崩,穿透)
考虑预热,那么访问量、数据量都会很大,因此要考虑并行(提高预热速度)+ 限速(并发量太大的话,DB也处理不过来)。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
redis和DB数据不一致怎么办?
1.先删缓存,后更新数据库
2.先更新DB后删除缓存
1会出现什么问题?
此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)
-
请求A进行写操作,删除缓存
-
请求B查询发现缓存不存在
-
请求B去数据库查询得到旧值
-
请求B将旧值写入缓存
-
请求A将新值写入数据库
上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。
解决方法:串行化:如果读到了空缓存情况就等待所有的写命令完成后再读
延时双删:
(1)先淘汰缓存;
(2)再写数据库(这两步和原来一样);
(3)休眠1秒,再次淘汰缓存;
这么做,可以将1秒内所造成的缓存脏数据,再次删除!
2.出现的问题是,删除缓存失败就会读取脏数据,
(1)缓存刚好失效;
(2)请求A查询数据库,得一个旧值;
(3)请求B将新值写入数据库;
(4)请求B删除缓存;
(5)请求A将查到的旧值写入缓存;
ok,如果发生上述情况,确实是会发生脏数据。
概率很小
如何解决:将要删的key放进消息队列给系统要保证删掉
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
一致性hash算法 学习源自https://www.zsythink.net/archives/1182
假设我们有三个redis,有3w张图片缓存,如果吧图片编号%3去缓存的话可以达到负载均衡,但是redis变多了,或者有个redis宕机了怎么办
这个时候就需要一致性hash算法了(md我不写了 这链接讲得太好了,自己看去)