Redis
关于Redis
redi是一个开源的使用C语言编写的一个kv存储系统,是一个速度非常快的非关系型数据库。它支持包括String List Set Zset Hash五种数据结构。
五种数据结构
字符串 (String)
String是redis最基本的类型,一个key对应一个value。String类型是二进制安全的,那就代表redis的String可以包含任何数据,比如jpg图片或者序列化对象。(String是Redis最基本的数据类型,一个键最大能存储512MB)
列表(List)
在Redis中List类型是按照插入顺序排序的字符串链表(相当于Java的LinkedList)和数据结构中普通的链表一样,Redis可以的在头部(left)或者尾部(right)进行插入新的元素。在插入时,如果该键并不存在,Redis将会为该键创建一个新的链表。与此相反,如果链表中所有元素都均被删除了,则Redis会移除该键。
哈希(Hash)
我们可以把Redis中的Hash看成具有 <key, <ky1, value»,其中一个key可以包含多个不同key值的 <key, value>。所以该类型非常适合于存储值对象的信息。如:Username、Password和Age等。如果Hash中包含很少的字段,则该类型也进仅占很少的磁盘空间。
集合(Set)
Set类型看作没有排序的字符集合,如果多次添加相同的元素,则Set中只会保存一份该元素的拷贝。
有序集合(Zset)
Sorted-Set 中的每一个成员都会关联一个分数(score) ,Redis正是通过这个分数来为集合中每个成员进行排序。成员是唯一的,但是分数(score)是可以重复的。分数最低的索引为0。(ZSet的底层数据结构是 跳表 )
Redis的持久化
常见的过期策略
如果一个键(key)过期了,那么它什么时候会被删除呢?(有三种不同的删除策略)
- 定时删除:在设置键的过期时间的同时,设置一个定时器(timer),让定时器在键的键过期的时候立即对key进行删除。这种策略能够对内存友好,但是缺点是消耗CPU,在CPU紧张的场景,会影响响应时间和吞吐量。
- 惰性删除:该策略放任键不管,但是每次从键空间中获取键key时,都会检查该key是否过期,如果过期则删除key,如果没过期则返回。这种策略对内存不友好,当有大量过期键key存在且这些又没被访问到时,会消耗大量内存。
- 定期删除:每过一段时间,程序对数据库进行一次检查,删除里面的过期key。
Redis的过期策略
Redis服务器过期策略使用的是 “惰性删除” + “定期删除” 两种策略:通过配合使用这两种删除策略,服务器能够很好的合理使用CPU时间片和浪费内存空间之间取得平衡。(对CPU和内存都友好)
惰性策略的实现
所有的读写Redis数据库的Redis命令在执行之前都会调用 expireIfNeeded 函数对输入键进行检查;
如果输入键过期则将其删除,再执行实际的命令流程。(如下图)
定期删除策略
每当redis的服务器周期性操作serverCron函数执行时,activeExpireCycle函数就会被调用,它在规定时间内,分多次遍历服务器中的各个数据库,从数据库的expire字典中随机检查一部分键的过期时间,并删除其中的过期键。
- 函数每次运行时,都从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中的随机键
- 全局变量current_db会记录当前activeExpireCycle函数的检查进度,并在下一次activeExpireCycle函数调用时,接着上一次的进度进行
- 随着activeExpireCycle函数的不断进行,服务器中的所有数据库都会被执行一遍,此时current_db重置为0,开始新的一轮定期删除。
redis内存不足时,淘汰策略
(这里内存不足可以在redis config里设置当Redis的内存超过百分之多少,例如超过80%的时候会执行的淘汰内存key策略)
1.noeviction
不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。 大多数写命令都会导致占用更多的内存(有极少数会例外, 如 DEL )。
2.allkeys-lru
所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
3.allkeys-random
所有key通用; 随机删除一部分 key。
4.volatile-lru
只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
5.volatile-random
只限于设置了 expire 的部分; 随机删除一部分 key。
6.volatile-ttl
只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。
LFU算法
LFU算法是Redis4.0里面新加的一种淘汰策略。它的全称是Least Frequently Used,它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。 LFU算法能更好的表示一个key被访问的热度。假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热点数据。 LFU一共有两种策略:
volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key allkeys-lfu:在所有的key中使用LFU算法淘汰数据
针对一些策略所使用的场景:
1)allkeys-lru:如果我们的应用对缓存的访问符合幂律分布(也就是存在相对热点数据),或者我们不太清楚我们应用的缓存访问分布状况,我们可以选择allkeys-lru策略。 在所有的key都是最近最经常使用,那么就需要选择allkeys-lru进行置换最近最不经常使用的key,如果你不确定使用哪种策略。 设置是失效时间expire会占用一些内存,而采用allkeys-lru就没有必要设置失效时间,进而更有效的利用内存
2)allkeys-random:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略。 如果所有的key的访问概率都是差不多的,那么可以选用allkeys-random策略去置换数据。
3)volatile-ttl:这种策略使得我们可以向Redis提示哪些key更适合被eviction。 如果对数据有足够的了解,能够为key指定hint(通过expire/ttl指定),那么可以选择volatile-ttl进行置换
4)volatile-lru策略和volatile-random策略适合我们将一个Redis实例既应用于缓存和又应用于持久化存储的时候,然而我们也可以通过使用两个Redis实例来达到相同的效果,值得一提的是将key设置过期时间实际上会消耗更多的内存,因此我们建议使用allkeys-lru策略从而更有效率的使用内存。
Redis的持久化操作
Redis提供了不同级别的持久化方式:
RDB
“RDB” 持久化方式能够在指定的时间间隔对你的数据进行快照存储
RDB的“优点”
- RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
- RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复.
- RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
- 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.
RDB的“缺点”
- 如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
- RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.
AOF
“AOF” 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。AOF以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写。使得AOF文件的体积不至于过大。
AOF的优点
- 使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.
- AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
- AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。