Redis💍
我们知道 , redis 是一款优秀的非关系型数据库 , 使用 C 语言编写的,支持网络交互的, 内存中的Key-Value 数据结构存储系统,它可以用作数据库、缓存和消息中间件。
🎩接着我们来看 , 关于Redis需要了解的几个问题
目录
关系型数据库与非关系型数据库
关系型数据库
🎩采用关系模型来组织数据的数据库,关系模型就是二维表格模型。一张二维表的表名就是关系,二维表中的一行就是一条记录,二维表中的一列就是一个字段
关系型数据库(例如: mysql)就是通过表与表之间的关系来映射数据与数据之间的关系 , 易于理解, 通用的sql语言丰富的完整性(实体完整性、参照完整性和用户定义的完整性),大大降低了数据冗余和数据不一致的概率缺点
缺点也很明显, 因为要保证ACID各种原则 , 导致其压力很大, 当海量的数据交互请求到达数据库时, 就会导致效率很低, 甚至可能压垮数据库, 横向扩展困难,无法简单的通过添加硬件和服务节点来扩展性能和负载能力
在此基础之上 , redis 的出现则较好的解决了关系型数据库的这些问题
非关系型数据库
🎩非关系型,分布式,一般不保证遵循 ACID(数据库事务正常执行的四个原则) 原则的数据存储系统。键值对存储, 结构不固定。
来说不是一种数据库, 而是一种数据结构化存储方法的集合 ,只适合存储一些较为简单的数据 ,不适合复杂查询的数据 ,不适合持久存储海量数据
![](https://i-blog.csdnimg.cn/blog_migrate/b8a7391475d5b1dc1d2fc4d83df80789.png)
主从复制
🎩主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点, 使用一个 Redis 实例作为主机,其余的作为备份机。主机和备份机的数据完全一致,主机支持数据的写入和读取等各项操作,而从机则只支持与主机数据的同步和读取。也就是说,客户端可以将数据写入到主机,由主机自动将数据的写入操作同步到从机。主从模式很好的解决了数据备份问题,并且由于主从服务数据几乎是一致的,因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。
意思就是说, 我们通常搭建多台 redis 服务机 , 从中挑选一个作为主机 , 其他的作为从机 , 一般的数据请求都是读多写少 , 所以我们只通过主机来写入数据(当然它也是可以读的), 写入时将数据备份给其他的从机, 以后读的操作尽可能交给从机去做 , 达到读写分离的目的, 有效的减轻了服务器的压力, 达到负载均衡
🎩负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。
哨兵机制
🎩 哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的进程, 作为进程,它会独立运行。其原理是哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例。
也就是说 , 在多个redis服务器集群时 , 哨兵会去监视这几台服务器 , 如果某台服务器撑不住了, 它会有相应的解决策略 , 例如 : 主机如果接受请求太多撑不住了, 那么哨兵会从从机中挑选出新的主机来暂时代替之前主机的位置 , 如果之前的主机恢复后则再将主机位置还回, 哨兵机制也是建立在主从复制的基础之上的
单哨兵 :
多哨兵 :
缓存问题
在了解缓存的问题之前, 我们先来看缓存的处理流程是怎样的
如图, 当 java 请求查询信息时 , 请求会先到达 redis , 如果redis中没有, 则会去关系型数据库中查询(mysql)
那么在此基础之上, 就会有这样几个问题
缓存穿透
🎩 key 对应的数据在数据库中并不存在,每次针对此 key 的请求从缓存获取不到,请求都会到数据库,从而可能压垮数据库。比如用一个不存在的用户 id 获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
就是说 , 我们请求的数据, redis 还是 mysql中都没有对应的数据, 那么最终导致的结果就是, 消息始终会到达我们的关系型数据库(mysql), 但是mysql中又没有对应的数据 , 只能返回 null , 不存入缓存 , 这时如果有大量这样的请求过来 , 就相当于直接跳过了 redis , 直接来到 mysql , 那么此时所有的压力都在 mysql 上, 很有可能压垮mysql
这里用一个通俗的例子来说一下 , 就是你去超市买菜 , 你还要专门挑那些超市门面上没有的菜, 这时售货员跟你说 , 没有这个了(redis) , 我给你去仓库(mysql)看一下 ,看了仓库之后 , 也没有, 这时你一直挑门面上没有的菜 , 然后这个售货员就一直去仓库中找 , 那么此时仓库一直被你折腾, 仓库管理员估计都想给你几下让你涨涨记性了
那么怎么来解决这样的问题呢?
1. 使用布隆过滤器(BloomFilter, 通过hash映射来判断元素是否存在)或者压缩 filter 提前拦截.
2.将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓存里边获取 了。这种情况我们一般会将空对象设置一个较短的过期时间.
3.对参数进行校验,不合法参数进行拦截
缓存击穿
🎩某个 key 对应的数据库中存在,但在 redis 中的某个时间节点过期了,此时 若有大量并发请求过来,这些请求发现缓存过期,都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮。
缓存击穿是相对于热点数据而言的 , redis 存储的数据一般都是有时限的, 如果有大量请求都在请求这个热点数据时 , 这个数据此时突然在 redis 中过期了, 那么此时大量的并发请求都会涌入mysql中 , 一时间并发量太大, mysql就有可能会撑不住 , 相当于从一点穿透进数据库 ,所以也叫击穿
这里就是说, 这时有一大群人来超市买黄瓜 , 哎 , 黄瓜碰巧这时刚卖完了 , 这时有人说, 咱们去超市仓库看一下, 这时一大群人就拥到仓库去了 , 仓库平时就几个人, 那么见得了这么大场面 , 一大群人突然拥进来在仓库里找东西 , 这时小仓库就承受不住了
怎样解决 ?
1.热点数据设置永不过期
2.加上互斥锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将数据放到 redis 缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存.
缓存雪崩
🎩缓存雪崩是指,在高并发情况下,大量的缓存失效,或者缓存层出现故障。于是所有的请求都会达到数据库,数据库的调用量会暴增,造成数据库也会挂掉的情况。
雪崩的问题就比穿透严重的多了, 它是大量的缓存失效了, 这时所有请求都到达数据库 , 那么数据库肯定是承受不住这么大的压力的
解决方法
1.随机设置 key 失效时间,避免大量 key 集体失效。setRedis(Key,value , time + Math.random() * 10000);
2.若是集群部署,可将热点数据均匀分布在不同的 Redis 库中也能够避免 key全部失效问题
3.不设置过期时间
4.跑定时任务,在缓存失效前刷进新的缓存
总结
💄雪崩是大面积的key缓存失效;穿透是redis里不存在这个缓存key;击穿是redis ,某一个热点 key 突然失效,最终的受害者都是数据库(数据库 : 受伤的总是我😭)。
对于“Redis 宕机,请求全部走数据库”这种情况,我们可以有以下的思路:事发前:实现 Redis 的高可用(主从架构+Sentinel(哨兵),尽量避免 Redis 挂掉这种情况发生。
事发中:万一 Redis 真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)
事发后:redis 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
关于redis的问题就先说到这里 , 感谢阅读😘