Redis基础
Redis 是 C 语言开发的一个开源的(遵从 BSD 协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。它是一种 NoSQL(not-only sql,泛指非关系型数据库)的数据库。
优点:
- 性能优秀,数据存在内存中,读写速度非常快,支持并发10W QPS。
- 单进程单线程,是线程安全的,采用 IO 多路复用机制。
- 丰富的数据类型,支持字符串(string)、哈希散列(hash)、列表(list)、集合(set)以及有序集合(sorted set)。
- 支持数据持久化。可以将内存数据保存到磁盘中,重启时加载。
- 支持高可用。持主从复制、哨兵。
基本使用场景:
- 数据存储
- 缓存
- 分布式锁
- 消息队列,支持发布订阅
数据类型&结构
redisObject
redisObject 是 Redis 类型系统的核心, 数据库中的每个键、值,以及 Redis 本身处理的参数, 都表示为这种数据类型。
redis是c语言开发的,redisObject 数据结构如下:
/*
* Redis 对象
*/
typedef struct redisObject {
unsigned type:4; // 类型
unsigned notused:2; // 对齐位
unsigned encoding:4; // 编码方式
unsigned lru:22; // LRU 时间(相对于 server.lruclock)
int refcount; // 引用计数
void *ptr; // 指向对象的值
} robj;
类型(type)
- REDIS_STRING:字符串
- REDIS_LIST:列表
- REDIS_SET:集合
- REDIS_ZSET:有序集合
- REDIS_HASH:哈希表
编码方式(encoding)
- REDIS_ENCODING_RAW :SDS 实现的动态字符串
- REDIS_ENCODING_INT:long整型
- REDIS_ENCODING_HT:哈希表
- REDIS_ENCODING_ZIPMAP:压缩map,(3.0)版本未使用
- REDIS_ENCODING_LINKEDLIST:双端链表
- REDIS_ENCODING_ZIPLIST:压缩列表
- REDIS_ENCODING_INTSET:整形集合
- REDIS_ENCODING_SKIPLIST:跳跃表
- REDIS_ENCODING_EMBSTR:使用 embstr 实现的动态字符串
下图展示了 redisObject 、Redis 所有数据类型、以及 Redis 所有编码方式(底层实现)三者之间的关系:
字符串(String)
string 类型是 Redis 最基本的数据类型,键值(key-value)数据结构,string 类型的值最大能存储 512MB。
string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
编码方式:
字符串对象支持的编码有:int、raw以及embstr
常用命令:
有:get、set、del、mget、mset、incr、decr...
哈希散列(Hash)
Hash是一个键值(key-value)集合,是一个key和values的映射表。
常用命令:hget、hset、hgetall、hdel...
列表(List)
列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。
常用命令:lpush、rpush、lpop、rpop、lrange(获取列表片段)等。
应用场景:
- 消息队列。Redis的链表结构,可以轻松实现阻塞队列,Redis 提供了 List 的 Push 和 Pop 操作,还可以直接查询或者删除某一段的元素。
- 数据分页查询:文章列表或者数据分页展示的应用。
集合(Set)
Set是Sting类型的无序集合。集合是通过hashtable实现的。set中的元素是无序的,而且不可重复。
常用命令:sdd、spop、smembers、sunion...
应用场景:
- 数据去重:Set 是无序、元素不重复的集合,可以用作分布式数据去重。
- 集合操作:Set 支持交集、并集、差集的操作,可以对一些数据进行操作。比如交集,可以把两个人的好友列表整一个交集,看看俩人的共同好友是谁。
有序集合(Sorted set)
Zset和Set一样是String类型元素的集合,且不允许重复的元素。
常用命令:zadd、zrange、zrem、zcard等。
使用场景:Sorted Set 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择 Sorted Set 结构。
- 排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
- 用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
缓存问题
Redis 为何这么快?
- redis基于内存,绝大多数的请求都是内存操作,速度很快。
- 数据结构简单,对数据操作也简单。类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度是 O(1)。
- 采用单线程,避免了不必要的上下文切换和竞争条件。不存在多线程导致cpu切换,不用考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。
- 使用多路复用 IO 模型,非阻塞 IO。
Redis 和 Memcached 的区别?
- 存储方式上:redis支持持久化,而memcache值存储在内存中,断电后memcache会丢失所有数据。
- 数据类型上:memcache仅支持简单的键值key-value,而redis支持5中数据类型。
- 底层模型上:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。Redis 直接自己构建了 VM 机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
- 值的大小上:Redis 可以达到 1GB,而 Memcache 只有 1MB。
淘汰策略有哪些?
redis有8种淘汰策略:
策略 | 描述 |
---|---|
volatile-lru | 从已设置过期时间的KV集中优先对最近最少使用(less recently used)的数据淘汰 |
volatile-ttl | 从已经设置过期时间的KV集中优先对剩余时间短(time to live)的数据淘汰 |
volatile-random | 从已设置过期时间的KV集中随机选择数据淘汰 |
volatile-lfu | 统计访问频率,从已经设置过期时间的KV集中优先淘汰访问频率最低的数据 |
allKeys-lru | 从所有KV集中对最近最少使用(less recently used)的数据淘汰 |
allKeys-random | 从所有KV集中随机淘汰数据 |
allkeys-lfu | 统计访问频率,从所有KV集中淘汰访问频率最低的数据 |
noeviction | 不淘汰策略,若超过最大内存,返回错误信息 |
Redis 4.0 加入了 LFU(least frequency use)淘汰策略,包括 volatile-lfu 和 allkeys-lfu,通过统计访问频率,将访问频率最少,即最不经常使用的 KV 淘汰。
Redis持久化
redis虽然是一种内存型数据库,但也提供持久化方案,将内存中的数据保存到磁盘中,避免数据丢失。
http://oldblog.antirez.com/post/redis-persistence-demystified.html
redis支持两种持久化方案:
- RDB:持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
- AOF:记录每次对redis服务器写的操作,当服务器重启时会重新执行这些命令来恢复原始数据。
RDB
在默认情况下, Redis 将数据库快照保存在名字为 dump.rdb
的二进制文件中。
命令
RDB文件可以通过两个命令创建:
- SAVE: 执行一个同步保存操作(会阻塞redis的服务进程),将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘。
- BGSAVE:主进程fork一个子进程来创建新的RDB文件,记录接收到的BGSAVE时刻的数据库状态,父进程继续处理接收到的命令。当子进程完成写临时文件后,用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
一般来说,在生产环境很少执行 SAVE 操作,因为它会阻塞所有客户端,保存数据库的任务通常由 BGSAVE 命令异步地执行。然而,如果负责保存数据的后台子进程不幸出现问题时, SAVE 可以作为保存数据的最后手段来使用。
优点
- RDB 文件是经过压缩的二进制文件,占用的空间会小于内存中的数据,更加利于传输,非常适合用于进行备份。
- RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
缺点
- 使用 RDB 方式实现持久化,一旦 Redis 异常退出,就会丢失最后一次快照以后更改的所有数据。这个时候我们就需要根据具体的应用场景,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受范围。
AOF
快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF(append-only file) 持久化。
具体来说,RDB持久化相当于备份数据库状态,而AOF持久化是备份数据库接收到的命令,所有被写入AOF的命令都是以redis的协议格式来保存的。
AOF文件重写机制
AOF(append-only file)的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。但AOF 文件体积变得过大时,Redis自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
优点
- 使用 AOF 的优点是会让 Redis 变得非常耐久。可以设置不同的
fsync
策略,AOF的默认策略是每秒钟fsync
一次,在这种配置下,就算发生故障停机,也最多丢失一秒钟的数据。 - AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。
缺点
- AOF 文件的体积通常要大于 RDB 文件的体积。
- 根据所使用的
fsync
策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒fsync
的性能依然非常高, 而关闭fsync
可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。
Redis主从复制
Redis哨兵模式
参考资料
https://zhuanlan.zhihu.com/p/91539644
https://www.cnblogs.com/MrHSR/p/9952653.html
https://redisbook.readthedocs.io/en/latest/datatype/object.html(redisObject)
https://segmentfault.com/a/1190000018887256