Redis数据库
Redis是什么?
Redis是一个C语言编写的开源的非关系型数据库,可以用来存储键值对数据
它有持久化机制,可以实现主从同步, 所以有时被用来做分布式锁
它的数据被存放在内存中,读写很快,经常被用来做缓存
Redis与Memcached的区别有哪些?
- Redis有持久化机制; Memecached没有
- Redis有5种数据类型; Memcached只有String这一种类型;
- Redis原生支持集群; Memcached不支持
- Redis是单线程; Memcached是多线程
关系型数据库 与 非关系型数据库的区别
关系型数据库的优势:支持复杂查询 和 事务
非关系型数据库的优势:性能 和 灵活性
两者是互补的,一方的优势就是另一方的劣势
为什么要使用Redis?
项目中一般使用Redis作为缓存,可以保证高性能和高并发
当请求第一次访问MySQL数据库,要从硬盘上读取数据,如果把这些数据通过Redis缓存到内存中,这样下次相同的请求就能直接从内存中获取,速度会快很多
Redis常用的数据类型
-
string: 字符串
- 存放一些简单的k-v键值对
- 使用场景: b站的关注数,粉丝数
-
list: 列表
- 用一个双链表实现的列表
- 使用场景: b站的关注列表,粉丝列表,可以用lrange做出那种下拉不断分页的效果
-
hash: 哈希表
- 存放一些对象信息
- 使用场景: 存用户信息,商品信息
-
set: 集合
- 存放一些不重复数据
- 使用场景: 像微博这类社交应用,用集合存放每个人的好友,sinter求交集获取共同好友
-
zset: 有序集合
- 根据权重参数有序地存放不重复数据
- 使用场景: 虎牙直播间的弹幕消息排行榜
为什么不用dict做缓存,而选用Redis?
缓存分为 本地缓存 和 分布式缓存
dict变量只能做 本地缓存,它的生命周期随着变量离开作用域就结束了,而且在多实例的情况下,dict变量的值难以保证一致
redis支持分布式缓存,在多实例的情况下,各个实例共享同一份缓存数据
除此之外,Redis可以给值设置一个过期时间,这是非常实用的,比如图形和短信验证码都是有时间限制的,
如果用dict还要自己判断过期,这样会严重影响项目的性能
Redis的线程模型? 为什么能支持高并发?
Redis是单线程的,它采用IO多路复用机制同时监听多个socket,并且因为都是在内存中操作,处理得很快,能够支持高并发
考虑到RDB文件的Fork,一些定时任务的处理,Redis其实也可以说是多进程的,但是它对于数据的处理,始终是单线程
Redis为什么用单线程而不用多线程?
Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是网络的带宽或者内存的大小。
Redis处理命令的流程
Redis基于Reactor模式,开发了自己的文件事件处理器,它会根据套接字的accept,read等操作关联不同的事件处理器,当有新的Redis客户端发起连接,或者已连接的客户端发来数据,就会触发相应的事件处理器,执行不同的命令
Redis的过期键清除策略
Redis过期键清除策略是定期删除+惰性删除。
-
定期删除: 每隔一段时间就抽取一些设置了过期时间的key,检查是否过期,若过期就删除.
-
惰性删除: 使用的时候,发现key过期了再删除
定期和惰性能保证删除过期数据吗?
不能,如果定期删除没抽取到该过期数据,后面也一直没获取过这个数据,那么它可能一直占据内存
为了解决这个问题,redis允许我们在配置文件中指定内存淘汰策略maxmemory_policy,默认是不开启
一般可以设为lru,淘汰最近最少使用的数据
Redis的持久化机制
- RDB: 记录的是某一时刻Redis存放在内存中的全部数据
- 优点: 恢复数据较快,可以通过fork子进程来完成持久化操作, 不会影响Redis服务端执行命令
- 缺点: 数据安全性较低,RDB是每隔一段时间进行持久化,如果系统宕机会导致这段时间的数据丢失
- AOF: 记录的是执行过的写命令
- 优点: 数据安全,默认设置是每秒钟把缓存中的写命令刷到磁盘文件,这样出现故障最多丢失一秒的数据
- 缺点: 要通过执行命令恢复数据,所以速度较慢
AOF文件越来越大怎么处理?
Redis可以在AOF文件体积变得过大时,自动地在后台Fork一个子进程,专门对AOF进行重写。本质上就是合并相同key的操作,用后面的值覆盖前面的值。
在重写的过程中执行的命令会被记录在AOF重写缓冲区,等到新的AOF文件创建完成,Redis就会将重写缓冲区的内容追加到新的AOF文件中,再用新的AOF替换原来的文件
Redis主从同步机制
主库和从库第一次连接时,会先进行全量同步
- 从库先发送sync命令,主库收到后调用save命令生成rdb文件,并记录 此期间的写命令 到 缓冲区
- 主库把rdb文件发给从库,从库收到后, 读取rdb文件的内容到内存中
- 主库还会继续把缓冲区的写命令发给从库,从库收到后执行这些写命令
全量同步结束后,进行增量同步,主库每执行一个写命令就向从库发送相同的命令,从库接收并执行该命令
Redis主从可以自动切换吗?
不能,但Redis提供了现成的解决方案,这就是哨兵机制,由哨兵来检测各个Redis服务是否正常,为了避免哨兵单点故障,哨兵也需要多机部署,如果master挂掉,哨兵Leader会在slave节点中选择一个作为新的master.
如果数据太大,Redis存不下怎么办?
使用集群模式,将数据分片,根据一致性hash算法 将不同的key 路由到 不同的节点
一致性哈希算法
一致性哈希算法可以解决服务器数量发生改变时,所有的路由被打乱,导致服务器缓存在同时失效的问题。它把节点和数据,以相同的hash函数映射到同一个hash环上,数据要在hash环上顺时针查找距其最近的节点来存放,这样当新增一个节点时,最多只会影响后面一个节点的数据.
缓存雪崩
原因: 大量缓存key同时失效,导致大量请求直接访问数据库
解决: 设置随机的过期时间
缓存穿透
原因: 大量缓存key查询不到,导致大量请求直接访问数据库,很可能是黑客攻击
解决: 对于数据库没查到的数据也添加到缓存,设置一个较短的过期时间
缓存击穿
原因: 非常热点的缓存key过期,导致大量请求直接访问数据库
解决: 热点的缓存key设为永不过期; 使用分布式锁,只让获取到锁的线程 读取数据库并更新缓存
Redis如何实现分布式锁?
- 使用 set dlock uuid EX 过期秒数 NX来实现加锁
- 通过 get dlock 判断它是不是刚刚自己设置的uuid,若是,说明加锁成功,否则失败
- 释放锁时,也通过get dlock判断它是不是刚刚自己设置的uuid,若是才执行del
MySQL,Redis的应用场景及优缺点
MySQL一般作为主存储,Redis作为缓存
MySQL: 可以存放复杂的关系型数据,数据主要存储在磁盘,容量大,但读写慢
Redis: 只能存放键值对数据,数据主要存储在内存,读写快
服务端性能优化措施
- 数据结构和算法优化
- 数据库: 消除慢查询语句,为某些常用作搜索条件的字段建立索引,删除不必要的索引
- 缓存: 使用redis这种内存数据库缓存MySQL数据库中查询的数据,缓解MySQL压力