1.优势
- Redis支持多种数据结构,满足多种场景需求
- Redis将数据存储在内存中,读写数据不受硬盘I/O速度的限制,所以数据读写非常快
- 丰富的附加功能
持久化功能:将存储在内存里面的数据保存到硬盘里面,保障数据安全,方便数据备份和恢复
发布订阅功能:将消息同时分发给多个客户端,用于构建广播系统
过期键功能: 为键设置一个过期时间,或在指定时间段后,键自动被删除
事务功能: 原子的执行多个操作,完成复杂的功能,并且减少客户端与服务器之间的通行往返次数
复制功能:为指定的Redis服务器创建多个复制品,用于提升数据安全并且分担请求负载
Sentinel: 监控Redis服务器的状态,并且在服务器发生故障时,进行自动故障转移
集群: 创建分布式数据库,每个服务器分别执行一部分读写操作
2.支持数据结构
字符串,散列(hash),列表(list),集合(set),有序集合(sorted set), HyperLogLog
字符串(String): 存储文字,数字或二进制数据
基本操作 | 复杂度 | 数字操作 | 复杂度 | 二进制操作 | 复杂度 |
SET GET | O(1) | INCRBY/DECRBY | O(1) | SETBT/GETBIT | O(1) |
SETNX | O(1) | INCR/DECR | O(1) | BITCOUNT | O(N) N为被计算的二进制位的数量 |
GETSET | O(1) | INCRBYFLOAT | O(1) | BITOP | O(N) N为被计算的二进制位的数量 |
APPEND | O(N) N为被推入值的长度 | ||||
STRLEN | O(1) | ||||
SETRANGE GETRANGE | O(N) N为value的长度 | ||||
MSET MGET | O(N) N为要设置的字符串键数量 | ||||
MSETNX | O(N) N是给定键的数量 |
散列(Hash)存储多个域值对(filed/value)键值都可以是文字,整数,浮点数或二进制数据
使用散列的好处:
1. 散列可以将一些相关的信息存储到同一个地方,而不是直接分散的存储在真个数据库中,这不仅方便了数据管理,还可以避免错误操作发生。
2. 同时使用散列可以最大限度的避免键名冲突
3. 使用散列可以做大限度的减少键的使用,从而减少Redis中对于键的内存消耗(注:Redis的键包含了很多附加管理信息(如键的类型,最后一次访问时间))
结论:只要有可能的话尽量使用散列而不是字符串来存储数据,因为散列管理方便,能够避免键名冲突,并且还能节约内存使用。
基本操作 | 复杂度 | 批量操作 | 复杂度 | 数字操作 | 复杂度 |
HSET HGET | O(1) | HMSET/HMGET | O(N) N为输入的域值对数量 | HINCRBY | O(1) |
HSETNX | O(1) | HGETALL | O(N) N为返回的域值对数量 | HINCRBYFLOAT | O(1) |
HEXISTS | O(1) | HKEYS/HVALS | O(N) N为返回的域或值对数量 | ||
HDEL | O(N) N为被删除的域值对数量 | ||||
HLEN | O(1) |
列表(list):以有序的方式存储多个可重复的值
推入和非阻塞弹出操作 | 复杂度 | 长度,索引和范围操作 | 复杂度 | 插入和删除操作 | 复杂度 | 阻塞弹出操作 | 复杂度 |
LPUSH RPUSH | O(1) | LLEN | O(1) | LSET | O(1) | BLPOP BRPOP | O(N) N为输入列表的数量 |
LPOP RPOP | O(1) | LINDEX | O(N) N为列表的长度 | LINSERT LREM | O(N) N为列表长度 | ||
LRANGE | O(N) N为被返回的列表项数量 | LTRIM | O(N) N为被移除列表项的数量 |
集合(set):存储无序且多个互不相同的元素
元素操作 | 复杂度 | 集合运算 | 复杂度 |
SADD | O(N) N为被添加元素的数量 | SINTER SINTERSTORE | O(N*M) N为给定集合中基数最小的集合,M为给定集合的个数 |
SREM | O(N) N为被移除元素的数量 | SUNION SUNIONSTORE | O(N) N为所有参与计算的元素数量 |
SISMEMBER | O(1) | SDIFF SDIFFSTORE | O(N) N为参与差集计算的元素数量之和 |
SCARD | O(1) | ||
SMEMBERS | O(N) N为集合内元素的数量 | ||
SPOP | O(1) | ||
SRANDMEMBER | O(N) N为被返回的元素数量 |
有序集合(sorted set)(跳跃表+字典):按照元素的分值来有序的存储各个不相同的元素
注:虽然有序集合中的每个元素必须是各不相同,但是对于元素的分值没有这一限制,有序元素的分值(浮点数)是可以相同的
基本操作 | 复杂度 | 基于分值的范围操作 | 复杂度 | 集合运算操作 | 复杂度 |
ZADD ZREM | O(M*log(N)) N为有序集合已有元素数量,M为被成功操作(删除或添加)元素数量 | ZRANGE ZREVRANGE | O(log(N)+M) N为有序集合的基数,M为被返回元素的数量 | ZUNIONSTORE | O(N)+O(log(M)) N为参与并集计算的元素数量,M为结果集的基数 |
ZSCORE | O(1) | ZCOUNT | O(log(N)) N为有序集合的基数 | ZINTERSTORE | O(N*K)+O(M*log(M)) N为给定有序集合中基数最小的有序集合基数,K为给定有序集合的数量,M为结果集的基数 |
ZINCRBY | O(log(N)) N为有序集合的基数 | ZRANGEBYSCORE ZREVRANGEBYSCORE | O(log(N)+M) N为有序集合的基数,M为被返回元素的数量 | ||
ZCARD | O(1) | ZREMRANGEBYRANK ZREMRANGEBYSCORE | O(log(N)+M) N为有序集合的基数,M为被移除元素的数量 | ||
ZRANK ZREVRANK | O(log(N)) N为有序集合的基数 |
注:如果参与并集计算的集合比较多,那么Redis服务器可能会被阻塞,因此最好在空余时间或备份服务器上面进行计算
HyperLogLog:使用常量空间估算大量元素的基数
优点:即使输入元素的数量或体积非常非常大,计算基数(非重复数量)所需的空间总是固定的,并且很小,非常节省内存。
不足:HyperLogLog只会根据输入元素来计算基数,并不会存储输入元素本身,所以并不能像集合那样返回输入的各个元素。
操作 | 复杂度 |
PFADD | O(N) N为被添加元素的数量 |
PFCOUNT | 当命令作用于耽搁HyperLogLog时,复杂度为O(1),并且具有非常低的平均时间 当命令作用于多个HyperLogLog时,复杂度为O(N),并且常数时间也比处理单个HyperLogLog时要大得多 |
PFMERGE | O(N) N为被合并的HyperLogLog数量 |
3.支持功能
键过期功能:让Redis在指定的时间点或时间段内自动删除特定的键
注:redis默认每100毫秒检查一次键过期
设置生存时间 | 复杂度 | 设置过期时间 | 复杂度 | 查看剩余生存时间 | 复杂度 | 移除键的生存或过期时间 | 复杂度 | 专门用于为字符串设置生存时间的命令 | 复杂度 |
EXPIRE | O(1) | EXPIREAT | O(1) | TTL | O(1) | PERSIST | O(1) | SETEX | |
PEXPIRE | O(1) | PEXPIREAT | O(1) | PTTL | O(1) | PSETEX |
发布与订阅:Redis的发布与订阅功能可以让用户将消息同时发送给多个客户端 (四种角色:publisher,channel,pattern,subscriber)
订阅与退订频道 | 复杂度 | 订阅与退订模式 | 复杂度 | 发布消息 | 复杂度 | 查看订阅状态 | 复杂度 |
SUBSCRIBLE | O(N) N为被订阅频道的数量 | PSUBSCRIBE | O(N) N为被订阅模式的数量 | PUBLISH | O(N) N为接收待消息的订阅者数量(包括通过订阅频道来接收消息的订阅者和通过模式来接收消息的订阅者) | PUBSUB CHANNELS 列出至少有一个订阅者的频道 | O(N) |
UNSUBSCRIBE | O(N) N为被退订频道数量 | UNPSUBSCRIBE | O(N) N为服务器中被订阅模式的数量 | PUBSUB NUMSUB 返回指定频道的订阅者数量 | O(N) N为指定频道数量 | ||
PUBSUB NUMPAT 返回服务器当前被订阅的模式数量 | O(1) |
事务:Redis的事务功能允许将多个命令包裹起来,然后一次性的按顺序的执行被包裹的所有命令 (使用乐观锁来保证数据的正确性)
操作 | 作用 | 复杂度 |
MULTI | 开始一次新的事务 | O(1) |
DISCARD | 放弃事务,并取消对所有键的监视 | O(1) |
EXEC | 执行事务队列中所有命令 | 复杂度为队列中所有命令复杂度之和 |
WATCH KEY [KEY...] | 监听一个或多个键 | O(N) N为监听键的数量 |
UNWATCH | 取消对所有键的监视 | O(1) |
流水线:通过减少客户端与服务端之间的通行次数来提高程序的执行效率
功能 | 性质 |
流水线 | 确保多条命令会被一起发送 |
事务 | 确保多条命令会被一起执行 |
Lua脚本:Redis内置了Lua解释器,使用用户可以在服务端执行Lua脚本,从而实现一些复杂的功能
好处:1.使用脚本可以执行在服务端执行Redis命令,一般的数据处理操作可以直接使用Lua语言或Lua解释器提供的函数库来完成,不必再返回给客户端进行处理
2.所有脚本都是以实物的形式来执行的,脚本在执行过程中不会被其他工作打断,也不会引起任何竞争条件,完全可以使用Lua脚本来代替实物和乐观锁。
3.所有脚本都是可重用的,也即是说,重复执行相同的操作时,只要调用存储在服务器内部的脚本缓存即可,不用重新发送脚本,从而尽可能的节约网络资源。
执行脚本的命令 | 管理脚本的命令 | 作用 |
EVAL script numkeys key [key...] arg [arg...] | SCRIPT EXISTS | 检查脚本时候已经被加入脚本缓存 |
EVALSHA sha1 numkeys key [key...] arg [arg...] | SCRIPT LOAD | 将脚本存储到脚本缓存中 |
SCRIPT FLUSH | 清除脚本缓存中存储的所有脚本 | |
SCRIPT KILL | 杀死运行超时的脚本 |
数据库命令:
处理数据库中的键 | 复杂度 | 对键的值进行排序 | 复杂度 | 获取数据库包含的键 | 复杂度 | 对数据库本身进行操作 | 复杂度 |
TYPE | O(1) | SORT | O(N+M*log(M)) N为被排序键包含的值数量,M为要返回的值数量 | RANDOMKEY | O(1) | DESIZE | O(1) |
DEL | O(N) N为被删除的键的数量 | KEYS | O(N) N为所有键值对的数量 | FLUSHDB | O(N) N为被删除的键值对的数量 | ||
EXIST | O(1) | SCAN | 每次执行的复杂度为O(1),遍历整个数据库的总复杂度为O(N),N为数据库包含的键值对数量 | SELECT | O(1) | ||
RENAME | O(1) | MOVE | O(1) | ||||
RENAMENX | O(1) | FLUSHALL | O(N) N为所有键值对的数量 |
4.Redis AOF和RDB持久化原理
RDB持久化原理
Redis会遍历服务器中的所有数据库,访问数据库中的所有键值对,并且根据键值对的类型将这些键值对以及它们的过期时间写入到RDB文件中
AOF持久化原理
AOF持久化功能在每次命令执行后就将协议格式的命令写入到AOF缓冲区,然后服务器再定期将缓冲区的内容写到AOF文件,还原数据时只需要重新执行AOF文件中的命令即可
Redis命令处理方法和模式
Redis使用Reactor模式来连接多个客户端并处理命令请求,其中:
- 客户端发送的命令请求会被放到一个有序的队列中
- 服务器使用单线程命令来执行命令,服务器每次从队列里面取出一个请求并处理它,只有在当前的命令请求处理完毕后,服务器才会处理下一个命令请求
- 单线程的命令处理方式使得针对服务器和数据库的操作都不需要加锁,好处是极大的方便了功能的实现,减少了代码出错的可能性,而坏处则是不能最大化的使用硬件的多线程功能