注:《Redis入门指南》作者:李子骅
- redis是一个开源的高性能键值对数据库,它通过提高多种键值数据类型来适应不同场景下的存储需求,并借助许多高层级的接口使其可以胜任如缓存,队列系统等不同的角色
特性
1. 存储结构
- 字符串,哈希,列表,集合,有序集合
- 字典结构的存储方式和对多种键值数据类型的支持使开发者可以将程序中的数据直接映射到redis中
- 对不同的数据类型提供了方便的操作方式
2. 内存存储与持久化
- 数据都存在内存中,内存的读写远快于硬盘
- 将数据异步写入硬盘中,防止内存数据丢失
3. 功能丰富
- 可用作缓存系统,因为
- 可以为每个键设置TTL,到期后自动删除
- 支持持久化和丰富数据类型
- 还可限定数据占用的最大内存空间,达到空间限制后可按照一定规则自动淘汰不需要的键
- 列表类型可用来实现队列系统,并且支持阻塞式读取,高性能的优先级队列
- 支持发布订阅的消息模式
4. 简单稳定
- 直观的存储结构
- 简单的命令
数据类型
- 命令
- keys命令需要遍历redis中的所有键,当键的数量较多时会影响性能
- 字符串类型大小:512MB
- 散列类型适合存储对象,一个散列类型的键可以包含至多2^32-1个字段
- redis的其他数据类型(除了字符串类型)不支持数据类型嵌套
- 列表类型可以存储一个有序的字符串列表,列表内部是使用双向链表实现的,向两端添加元素时间复杂度o(1),获取两端的元素速度快,意味着获取头部或尾部的一段数据也是极快的(比如社交网站只关心最新的事),通过索引访问元素比较慢
- 集合类型可以存储文章的标签。集合类型的键可以存储至多2^32-1个字符串
- 集合类型在redis内部是使用值为空的散列表实现的,所以向集合加入,删除元素,判断某个元素存在的时间复杂度O(1). 多个集合类型之间可以进行并集,交集,差集运算
- 集合类型采用的存储结构(散列表)会造成SRANDMEMBER命令返回的数据并不是非常的随机
- 散列表使用散列函数将元素映射到不同的存储位置上以实现O(1)时间复杂度的元素查找
- 存储b时使用散列函数计算b的散列值存入相应的桶中,查找时用同样的散列函数再次计算b的散列值在相应的桶中找到b.
- 当两个不同的元素的散列值相同时会出现冲突,redis使用拉链法解决
- 将散列值冲突的元素以链表的形式存入同一桶中,查找时先找到元素对应的桶,再从桶中的链表找对应的元素
- SRANDMEMBER命令从集合获得一个随机元素时,redis首先会从所有桶中随机选择一个非空桶,再从桶中随机选择一个元素,所以元素所在的桶中的元素数量越少,被随机选择的可能性越大
- 有序集合在集合的基础上为每个元素关联一个分数,支持获得分数最高(低)的前N个元素,分数范围内的元素,与分数有关的操作;元素不同分数可以相同
- 集合无序但是唯一,列表有序但是不唯一,有序集合有序且唯一
- 有序集合和列表的区别:
- 列表通过链表实现,获取两端数据速度极快但是访问中间数据速度慢(尤其元素增多的时候),更适合实现很少访问中间元素的应用(“新鲜事”,“日志”)
- 有序集合使用散列表和跳跃表实现,即使读取中间数据速度也快(O(logN))
- 列表不能简单调整某个元素位置,有序集合通过更改分数可以实现
- 有序集合比列表更耗费内存
事务
- 一组命令的集合,一个事务的命令要么都执行要么都不执行,每个命令的执行结果最后一起返回(所以无法将前一条命令的结果作为下一条命令的参数)
- 错误处理
- 语法错误则所有命令都不处理
- 运行错误,事务的其他命令依然会被继续执行
- WATCH命令可以监控一个或多个键,一旦其中一个键被修改(或删除),之后的事务就不会执行
- UNWATCH命令取消监控
生存时间
- EXPIRE(单位是秒)/PEXPIRE(单位是毫秒)/EXPIREAT(使用unix时间作为截止时间)/PEXPIREAT(使用unix时间作为截止时间单位是毫秒)/TTL(单位是秒)/PTTL(单位是毫秒)/PERSIST
- SET或GETSET命令为键值赋值会同时清除键的生存时间
- 如果使用WATCH命令监测了一个拥有生存时间的键。该键时间到期自动删除并不会被WATCH命令认为键被改变
- 通过设置过期时间限制一定时间内的访问次数
- 可以限制redis能够使用的最大内存,并按照一定规则淘汰不需要的缓存键
- LRU算法删除(最近最少使用)(区分是否设置生存时间)
- 随机删除(区分是否设置生存时间)
- 删除生存时间最近的一个键
- 不删除键只返回错误
排序
- sort进行较大数据量的排序会影响性能,时间复杂度O(n+mlogm)
- 尽可能减少待排序键中元素的数量(n尽可能小)
- 使用limit参数只获取需要的数据(m尽可能小)
- 如果排序数据量较大尽可能使用STORE参数将结果缓存
消息通知
- 任务队列
- 优先级队列
- 发布订阅模式
节省空间
- 精简键名和键值
- (时间换空间)内部编码优化(redis为每种数据类型提供了两种内部编码的方式,根据自身情况自动调整)
- redis的每个键值都是使用一个redisObject结构体保存的
- 字符串类型
- redis使用一个sdshdr类型的变量存储字符串,当键值内容可以用一个64位的有符号整数表示,redis会将键值转换成long类型存储(节省一半的空间)
- redis启动后会预先建立10000个分别存储从0到9999这些数字的redisObject类型变量作为共享对象,若要设置的字符串键值在这10000数字内可以直接引用共享对象不用再建立一个redisObject
- 当通过maxmemory配置了redis可用的最大空间大小,redis不会使用共享对象,因为对于每一个键值都需要使用一个redisObject来记录其LRU信息(那还会创建吗?)
- 散列类型
- 可以通过配置参数定义使用REDIS_ENCODING_ZIPLIST方式编码的时机,否则使用REDIS_ENCODING_HT方式
- 不宜参数设置过大,插入删除时候需要调整后面的元素位置,查找也是需要遍历,元素太多时性能差
- 列表类型
- 可以通过配置参数定义使用REDIS_ENCODING_ZIPLIST方式编码的时机,否则使用REDIS_ENCODING_LINKDLIST方式
- REDIS_ENCODING_ZIPLIST编码方式支持倒序访问,获取两端的数据依然较快
- 集合类型
- 可以通过配置参数定义使用REDIS_ENCODING_INTSET方式编码的时机,否则使用REDIS_ENCODING_HT方式
- 不宜参数设置过大,虽然可以二分查找,但是增加删除的时候需要调整后面的元素的内存位置,元素太多时性能差
- 转换成REDIS_ENCODING_HT方式后即使将集合的所有非整数元素删除也不会转换回REDIS_ENCODING_INTSET方式(每次删除元素都需要遍历判断是否可以转换回,使得删除操作时间复杂度O(n))
- 有序集合类型
- 可以通过配置参数定义使用REDIS_ENCODING_ZIPLIST方式编码的时机,否则使用REDIS_ENCODING_SKIPLIST方式
持久化 (保证redia在服务器重启的情况也不会损失数据)
- RDB方式(redis异常退出则会丢失最后一次快照以后更改的数据)
- 通过快照完成,当符合一定条件(配置文件的save参数)时,redis会自动将内存中的所有数据进行快照并存储在硬盘上
- 通过删除配置文件的所有save参数可以禁用自动快照
- 快照的过程
- redis使用fork复制一份当前进程的子进程(写时复制)
- 父进程继续处理客户端命令请求,子进程开始将内存的数据写入硬盘中的临时文件
- 子进程写完后用临时文件替换旧的RDB文件(保证RDB文件任何时候都是完整的,使我们可以通过定时备份RDB文件实现Redis数据库备份),快照完成
- 还可以手动发送SAVE(主进程执行会阻塞其他请求)或BGSAVE(fork子进程执行)让redis执行快照
- AOF方式(如果数据重要到无法承受任何损失,使用AOF方式)
- 通过配置文件参数开启AOF方式
- 开启AOF方式后每执行一条会更改redis数据的命令都会将该命令写入硬盘的AOF文件(RDB文件位置一样)
- 由于操作系统缓存机制,数据没有真正写入硬盘,而是进入系统的硬盘缓存,默认30s执行一次同步操作将硬盘缓存的数据真正写入硬盘,30s内如果系统异常退出会导致硬盘缓存的数据丢失
- 一般来讲AOF方式的应用无法忍受这样损失,需要redis写入文件后主动要求系统将缓存内容同步到硬盘,可以通过配置文件参数设置
- 每当达到一定条件(配置文件的参数设置)时redis就会自动重写AOF文件,优化掉冗余的命令
- 使用BGREWRITEAOF命令手动执行重写
- 启动时redis会逐个执行AOF文件的命令将硬盘的数据载入内存(比RDB慢一些)
- 可以同时开启AOF和RDB方式,此时重启redis会使用AOF文件恢复数据(AOF可能丢失数据更少)
复制(解决单点故障)
- 主从同步
- 从数据配置主数据库的地址
- 使用SLAVEOF命令在运行时修改为从数据库
- 使用SLAVEOF NO ONE是当前数据库转变成主数据库
- 当一个从数据库启动后会向主数据库发送SYNC命令,主数据库接收到后会开始在后台保存快照并将保存期间收到的命令缓存起来,快照完成后会将快照文件和所有缓存的命令发送给从数据库,从数据库接收到后载入快照文件并执行命令
- 只有执行复制就会进行快照及时关闭RDB方式
- 从数据库也可拥有从数据库
- 复制可以实现读写分离(写主读从)
- 持久化耗时,可以通过建立一个从数据库并启用持久化而主数据库禁用持久化,提高性能
安全
- 可信的运行环境(只允许本机连接redis,通过修改配置文件的bind参数)
- 数据库密码
通信协议
- 二进制安全的统一请求协议(AOF文件和主从复制时向从数据库发送内容)
- 直观的简单协议
其他
- redis启动时(将数据从硬盘载入内存中)只会执行拥有REDIS_CMD_LOADING命令(INFO,SUBCSRIBE,PUBLISH…)
- 当数据库占用空间达到配置文件的maxmemory参数指定的值且根据maxmemory_policy参数的空间释放规则无法释放空间时,redis就会拒绝拥有REDIS_CMD_DENYOOM属性的命令(SET,APPEDN,RPUSH,LSET,INCR,HSET,SORT,ZADD…),因为这里=类命令有可能增加redis占用的存储空间