1.redis是什么
Redis 是一个包含多种数据结构、基于内存、可选持久性 key-value 存储系统,并提供多种语言的 API 的非关系型数据库。
优点:
- 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
- 支持丰富数据类型,支持string,list,set,sorted set,hash
- 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 丰富的特性:可用于缓存,消息,按key设置过期时间
缺点:
- 缓存和数据库双写一致性问题
- 缓存雪崩问题
- 缓存击穿问题
- 缓存的并发竞争问题
redis作为一个k/v数据库的概念
Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。首先Redis不支持自定义数据库的名字,另外Redis也不支持为每个数据库设置不同的访问密码,只是以序号从0开始区分,默认有16个库(可以通过配置更多),但与其说是数据库,不如说是命名空间,因为它并不像mysql等数据,每一个库之间都是完全隔离的,redis只是加以区分,并没有完全隔离。
2.redis数据类型
类型 简介 特性 场景 String(字符串) 二进制安全 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M --- Hash(字典) 键值对集合,即编程语言中的Map类型 适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去) 存储、读取、修改用户属性 List(列表) 链表(双向链表) 增删快,提供了操作某一段元素的API 1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列 Set(集合) 哈希表实现,元素不重复 1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐 Sorted Set(有序集合) 将Set中的元素增加一个权重参数score,元素按score有序排列 数据插入集合时,已经进行天然排序 1、排行榜 2、带权重的消息队列 高阶:HyperLogLog、Geo、Pub/Sub、BloomFilter,RedisSearch,Redis-ML 等
3.应用场景
- 热点数据的缓存
- 分布式 session
- 计数器、标记相关问题
- 排行榜相关问题
- 分布式锁
- 其他业务不可变或者变动几率小的数据
4.原理解析
- redis 服务器启动时,会把 AE_READABLE 向 eventLoop(IO 多路复用)注册。
- 客户端请求与服务器建立连接,服务器生成 Scoket(s1)通道并绑定 AE_READABLE 事件。
- 客户端(s1)请求执行 set key value(写命令),s1 触发 AE_READABLE 由命令请求器将 key 和 value 读取到内存并对数据修改
- 设值结束后将 s1 与 AE_WRITABLE 事件关联绑定,从而触发写操作,成功后由命令回复器输出结果“OK”
- 返回结果将 s1 的 AE_WRITABLE 事件与命令回复处理器解除绑定。
5.IO 多路复用
IO 多路复用:I/O 是指网络 I/O,多路指多个 TCP 连接(即 socket 或者 channel),复用指复用一个或几个线程。也就是说一个或一组线程处理多个 TCP 连接。最大优势是减少系统开销小,不必创建过多的进程/线程,也不必维护这些进程/线程。 主要机制:select、poll、epoll
select 机制:
基本原理:
客户端操作服务器时就会产生这三种文件描述符(简称 fd):writefds(写)、readfds(读)、和 exceptfds(异常)。select 会阻塞住监视 3 类文件描述符,等有数据、可读、可写、出异常 或超时、就会返回;返回后通过遍历 fdset 整个数组来找到就绪的描述符 fd,然后进行对应的 IO 操作。
优点:
几乎在所有的平台上支持,跨平台支持性好。
缺点:
1.由于是采用轮询方式全盘扫描,会随着文件描述符 FD 数量增多而性能下降。
2.每次调用 select(),需要把 fd 集合从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用 户空间)
3.默认单个进程打开的 FD 有限制是 1024 个,可修改宏定义,但是效率仍然慢。
poll 机制:
基本原理:
基本原理与 select 一致,也是轮询+遍历;唯一的区别就是 poll 没有最大文件描述符限制(使用链表的方式存储 fd)。
epoll 机制:
基本原理:
没有 fd 个数限制,用户态拷贝到内核态只需要一次,使用时间通知机制来触发。通过 epoll_ctl 注册 fd,一旦 fd 就绪就会通过 callback 回调机制来激活对应 fd,进行相关的 io 操作。
epoll 之所以高性能是得益于它的三个函数:
1. epoll_create()系统启动时,在 Linux 内核里面申请一个 B+树结构文件系统,返回 epoll 对象,也是一个 fd
2. epoll_ctl() 每新建一个连接,都通过该函数操作 epoll 对象,在这个对象里面修改添加删除对应的链接 fd, 绑定一个 callback 函数
3. epoll_wait() 轮训所有的 callback 集合,并完成对应的 IO 操作
优点:
1.没 fd 这个限制,所支持的 FD 上限是操作系统的最大文件句柄数,1G 内存大概支持 10 万个句柄
2.效率提高,使用回调通知而不是轮询的方式,不会随着 FD 数目的增加效率下降
3.内核和用户空间 mmap 同一块内存实现(mmap 是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间).
例子:
100 万个连接,里面有 1 万个连接是活跃,我们可以对比 select、poll、epoll 的性能表现
select:不修改宏定义默认是 1024,l 则需要 100w/1024=977 个进程才可以支持 100 万连接,会使得 CPU 性能特别的差。
poll:没有最大文件描述符限制,100 万个链接则需要 100w 个 fd,遍历都响应不过来了,还有空间的拷贝消耗大量的资源。
epoll:请求进来时就创建 fd 并绑定一个 callback,主需要遍历 1w 个活跃连接的 callback 即可,即高效又不用内存拷贝
6.持久化
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis 提供了两种持久化方式:RDB(默认)和 AOF
RDB:
RDB 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是 fork 一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
默认配置:
save 900 1 #在 900 秒(15 分钟)之后,如果至少有 1 个 key 发生变化,则 dump 内存快照。
save 300 10 #在 300 秒(5 分钟)之后,如果至少有 10 个 key 发生变化,则 dump 内存快照。
save 60 10000 #在 60 秒(1 分钟)之后,如果至少有 10000 个 key 发生变化,则 dump 内存快照。
优势:
1. RDB 文件紧凑,全量备份,非常适合用于进行备份(冷备)和灾难恢复。
2. 性能最大化。对于 Redis 的服务进程而言,在开始持久化时,它唯一需要做的只是 fork 出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行 IO 操作了。
3. RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
AOF:
全量备份总是耗时的,有时候我们提供一种更加高效的方式 AOF,工作机制很简单,redis 会将每一个收到的写命令都通过 write 函数追加到文件中。通俗的理解就是日志记录。
默认配置:
appendfsync always #每次有数据修改发生时都会写入 AOF 文件。
appendfsync everysec #每秒钟同步一次,该策略为 AOF 的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。
优势:
1. AOF 可以更好的保护数据不丢失,一般 AOF 会每隔 1 秒,通过一个后台线程执行一次 fsync 操作,最多丢失 1 秒钟的数据。
2. AOF 日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
3. AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
4. AOF 日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用 flushall 命令清空了所有数据,只要这个时候后台 rewrite 还没有发生,那么就可以立即拷贝 AOF 文件,将最后一条 flushall 命令给删了,然后再将该 AOF 文件放回去,就可以通过恢复机制,自动恢复所有数据
RDB 和 AOF 到底该如何选择
小孩子才做选择,我全部都要,一般都是两种方式配合使用
7.集群模式
Redis 一共提供了三种集群模式
主从复制/同步、哨兵模式、Cluster 集群
主从复制/同步
为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此, Redis 提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。
在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
原理
1. 从数据库连接主数据库,发送 SYNC 命令;
2. 主数据库接收到 SYNC 命令后,开始执行 BGSAVE 命令生成 RDB 文件并使用缓冲区记录此后执行的所有写命令;
3. 主数据库 BGSAVE 执行完后,向所有从数据库发送快照文件,并在发送期间继续记录被执行的写命令;
4. 从数据库收到快照文件后丢弃所有旧数据,载入收到的快照;
5. 主数据库快照发送完毕后开始向从数据库发送缓冲区中的写命令;
6. 从数据库完成对快照的载入,开始接收命令请求,并执行来自主数据库缓冲区的写命令;(从数据库初始化完成)
7. 主数据库每执行一个写命令就会向从数据库发送相同的写命令,从数据库接收并执行收到的写命令(从数据库初始化完成后的操作)
8. 出现断开重连后,2.8 之后的版本会将断线期间的命令传给重数据库,增量复制。
9. 主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。Redis 的策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
优点:
1.支持主从复制,主机会自动将数据同步到从机,可以进行读写分离;
2.为了分载 Master 的读操作压力,Slave 服务器可以为客户端提供只读操作的服务;
3.Slave 同样可以接受其它 Slaves 的连接和同步请求,这样可以有效的分载 Master 的同步压力;
4.Master Server 是以非阻塞的方式为 Slaves 提供服务。所以在 Master-Slave 同步期间,客户端仍然可以提交查询或修改请求;
5.Slave Server 同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis 则返回同步之前的数据;
缺点:
1.Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的 IP 才能恢复;
2.主机宕机,宕机前有部分数据未能及时同步到从机,切换 IP 后还会引入数据不一致的问题,降低了系统的可用性;
3.如果多个 Slave 断线了,需要重启的时候,尽量不要在同一时间段进行重启。因为只要 Slave 启动,就会发送 sync 请求和主机全量同步,当多个 Slave 重启的时候,可能会导致 Master IO 剧增从而宕机。
4.Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂;
5.因为所有从服务器都拥有一份主服务器全量数据备份,所有支持的数据总量有限。
哨兵模式
在主从同步/复制的模式下,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例。
作用:
1.通过发送命令,让 Redis 服务器返回监控其运行状态,包括主服务器和从服务器;
2.当哨兵监测到 master 宕机,会自动将 slave 切换成 master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机;
原理:
1.每个 Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的 Master 主服务器,Slave 从服务器以及其他 Sentinel(哨兵)进程发送一个 PING 命令。
2.如果一个 Master 主服务器被标记为主观下线(SDOWN),则正在监视这个 Master 主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认 Master 主服务器的确进入了主观下线状态
3.当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认 Master 主服务器进入了主观下线状态(SDOWN), 则 Master 主服务器会被标记为客观下线(ODOWN)
4.在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有 Master 主服务器、Slave 从服务器发送 INFO 命令。
5.当 Master 主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master 主服务器的所有 Slave 从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
6.若没有足够数量的 Sentinel(哨兵)进程同意 Master 主服务器下线, Master 主服务器的客观下线状态就会被移除。若 Master 主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master 主服务器的主观下线状态就会被移除 优点:
1. 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
2. 主从可以自动切换,系统更健壮,可用性更高。
Cluster 集群
Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在 redis3.0 上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的内容。
原理:
1. 在 Redis 的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是 cluster,可以理解为是一个集群管理的插件。当我们的存取的 Key 到达的时候,Redis 会根据 crc16 的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动路由到这个对应的节点上进行存取操作。
2.为了保证高可用,redis-cluster 集群引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。当其它主节点 ping 一个主节点 A 时,如果半数以上的主节点与 A 通信超时,那么认为主节点 A 宕机了。如果主节点 A 和它的从节点 A1 都宕机了,那么该集群就无法再提供服务了。
优点:
1. 集群完全去中心化,采用多主多从;所有的 redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输速度和带宽。
2.客户端与 Redis 节点直连,不需要中间代理层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
3.每一个分区都是由一个 Redis 主机和多个从机组成,分片和分片之间是相互平行的。
8.缓存雪崩、击穿、穿透
缓存雪崩
缓存雪崩是指缓存服务器重启或者大量缓存集中在某一个时间点失效,从而导致大量请求全部达到数据库上,从而导致数据库的不可用
解决方案:
1. 在批量存入缓存数据时,将缓存失效的时间加个随机值
2. 热点数据永远不过期,有更新操作就更新缓存(注意双写一致)就好了
缓存击穿
缓存击穿是指与雪崩有点类似,指的是在某个时间点内一个 key(非常热点 key)失效,从而导致所有请求打到数据库中
解决方案:
1. 使用互斥锁,在 key 失效的瞬间只允许一个请求将数据库最新的数据重新加载到 redis 中,其余线程还是冲 redis 中获取
2. 也可以将热点数据设置永远不过期,有更新操作就更新缓存(注意双写一致)就好了
缓存穿透
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求一般的缓存系统,一些恶意的请求会故意查询不存在的 key,请求量很大,就会对后端系统造成很大的压力。
解决方案:
1.在接口层面添加参数校验 ,过滤不合法请求
2.将缓存取不到的数据,在数据库中也没有取到的数据也存入 redis 中并设置过期时间
3.使用布隆过滤器,可以看这篇布隆过滤器解析,通过布隆过滤器判断是否存在该数据,不存在则返回。
9.过期策略
Redis 的过期策略,是有定期删除+惰性删除两种。
定期删除
默认 100ms 就随机抽一些设置了过期时间的 key,去检查是否过期,过期了就删了。
惰性删除
当客户端查询时再检查是否过期,过期就删了不返回,没过期该怎么样就怎么样。
内存淘汰机制
1.noeviction: 返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但 DEL 和几个例外)
2.allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
3.volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
4.allkeys-random: 回收随机的键使得新添加的数据有空间存放。
5.volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
6.volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。