Redis 集合对象
type
status
date
slug
summary
tags
category
icon
password
Property
目录
目录

Set 类型和 List 类型的区别如下:
  • List 可以存储重复元素,Set 只能存储非重复元素;
  • List 是按照元素的先后顺序存储元素的,而 Set 则是无序方式存储元素的。
 
集合对象的编码可以是 intset 或者 hashtable 。
intset 编码的集合对象使用整数集合作为底层实现, 集合对象包含的所有元素都被保存在整数集合里面。
创建一个intset编码集合对象:
Redis 有序集合对象
type
status
date
slug
summary
tags
category
icon
password
Property
目录
目录

Zset 类型(有序集合类型)相比于 Set 类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序集合的元素值,一个是排序值。
有序集合保留了集合不能有重复成员的特性(分值可以重复),但不同的是,有序集合中的元素可以排序。
 
有序集合的编码可以是 ziplist 或者 skiplist 。
ziplist 编码的有序集合对象使用压缩列表作为底层实现, 每个集合元素使用两个紧挨在一起的压缩列表节点来保存, 第一个节点保存元素的成员(member), 而第二个元素则保存元素的分值(score)。
压缩列表内的集合元素按分值从小到大进行排序, 分值较小的元素被放置在靠近表头的方向, 而分值较大的元素则被放置在靠近表尾的方向。
如果执行以下 ZADD 命令, 那么服务器将创建一个有序集合对象作为 price 键的值:
二进制数组 bitmap
type
status
date
slug
summary
tags
category
icon
password
Property
目录
目录

Redis使用字符串对象来表示位数组,因为字符串对象使用的SDS数据结构是二进制安全的,所以程序可以直接使用SDS结构来保存位数组,并使用SDS结构的操作函数来处理位数组
在平时开发过程中,经常会有一些 bool 类型数据需要存取。比如记录用户一年内签到的次数,签了是 1,没签是 0。如果使用 key-value 来存储,那么每个用户都要记录 365 次,当用户成百上亿时,需要的存储空间将非常巨大。为了解决这个问题,Redis 提供了位图结构。
位图(bitmap)同样属于 string 数据类型。Redis 中一个字符串类型的值最多能存储 512 MB 的内容,每个字符串由多个字节组成,每个字节又由 8 个 Bit 位组成。位图结构正是使用“位”来实现存储的,它通过将比特位设置为 0 或 1来达到数据存取的目的,这大大增加了 value 存储数量,它存储上限为
 
位图本质上就是一个普通的字节串,也就是 bytes 数组。可以使用getbit/setbit命令来处理这个位数组,位图的结构如下所示:
notion image
位图适用于一些特定的应用场景,比如用户签到次数、或者登录次数等。上图是表示一位用户 10 天内来网站的签到次数,1 代表签到,0 代表未签到,这样可以很轻松地统计出用户的活跃程度。相比于直接使用字符串而言,位图中的每一条记录仅占用一个 bit 位,从而大大降低了内存空间使用率。
 
布隆过滤器
type
status
date
slug
summary
tags
category
icon
password
Property
 
布隆过滤器(Bloom Filter)是 Redis 4.0 版本提供的新功能,它被作为插件加载到 Redis 服务器中,给 Redis 提供强大的去重功能。
相比于 Set 集合的去重功能而言,布隆过滤器在空间上能节省 90% 以上,但是它的不足之处是去重率大约在 99% 左右,也就是说有 1% 左右的误判率,这种误差是由布隆过滤器的自身结构决定的。俗话说“鱼与熊掌不可兼得”,如果想要节省空间,就需要牺牲 1% 的误判率,而且这种误判率,在处理海量数据时,几乎可以忽略。
 
应用场景
布隆过滤器是 Redis 的高级功能,虽然这种结构的去重率并不完全精确,但和其他结构一样都有特定的应用场景,比如当处理海量数据时,就可以使用布隆过滤器实现去重。
百度爬虫系统每天会面临海量的 URL 数据,我们希望它每次只爬取最新的页面,而对于没有更新过的页面则不爬取,因策爬虫系统必须对已经抓取过的 URL 去重,否则会严重影响执行效率。但是如果使用一个 set(集合)去装载这些 URL 地址,那么将造成资源空间的严重浪费。
垃圾邮件过滤功能也采用了布隆过滤器。虽然在过滤的过程中,布隆过滤器会存在一定的误判,但比较于牺牲宝贵的性能和空间来说,这一点误判是微不足道的。
 

工作原理

布隆过滤器(Bloom Filter)是一个高空间利用率的概率性数据结构,由二进制向量(即位数组)和一系列随机映射函数(即哈希函数)两部分组成。
布隆过滤器使用exists()来判断某个元素是否存在于自身结构中。当布隆过滤器判定某个值存在时,其实这个值只是有可能存在;当它说某个值不存在时,那这个值肯定不存在,这个误判概率大约在 1% 左右。
HyperLoglog 基数统计
type
status
date
slug
summary
tags
category
icon
password
Property
 
Redis经常使用的数据类型有字符串、列表、散列、集合和有序集合,但这些类型并不能满足所有的应用场景,因此,Redis的后续版本不断的扩增其他数据类型来增强Redis适用能力。在Redis 2.8.9版本中新增了HyperLogLog类型。
 
HyperLoglogRedis重要的数据类型之一,它非常适用于海量数据的计算、统计,其特点是占用空间小,计算速度快。
HyperLoglog采用了一种基数估计算法,因此,最终得到的结果会存在一定范围的误差(标准误差为 0.81%)。每个 HyperLogLog key 只占用 12 KB 内存,所以理论上可以存储大约 个值,而 set(集合)则是元素越多占用的内存就越多,两者形成了鲜明的对比 。
 
基数定义:一个集合中不重复的元素个数就表示该集合的基数,比如集合 {1,2,3,1,2} ,它的基数集合为 {1,2,3} ,所以基数为 3。HyperLogLog正是通过基数估计算法来统计输入元素的基数。
HyperLoglog不会储存元素值本身,因此,它不能像set那样,可以返回具体的元素值。HyperLoglog只记录元素的数量,并使用基数估计算法,快速地计算出集合的基数是多少。
 
 
常用命令
GEO 地理位置
type
status
date
slug
summary
tags
category
icon
password
Property
 
在Redis 3.2版本中,新增了存储地理位置信息的功能,即GEO(英文全称 geographic),它的底层通过Redis有序集合(zset)实现。不过Redis GEO并没有与zset共用一套的命令,而是拥有自己的一套命令。
Redis GEO 有很多应用场景,举一个简单的例子,外卖或者打车软件上会显示“店家距离你有多少米”或者“司机师傅距离你有多远”,类似这种功能就可以使用 Redis GEO 实现。数据库中存放着商家所处的经纬度,位置则由手机定位获取,这样 APP 就计算出了最终的距离。再比如微信中附近的人、摇一摇、实时定位等功能都依赖地理位置实现。
GEO 提供以下操作命令:
命令
说明
GEOADD
将指定的地理空间位置(纬度、经度、名称)添加到指定的 key 中。
GEOPOS
从 key 里返回所有给定位置元素的位置(即经度和纬度)
GEODIST
返回两个地理位置间的距离,如果两个位置之间的其中一个不存在, 那么命令返回空值。
GEORADIUS
根据给定地理位置坐标(经纬度)获取指定范围内的地理位置集合。
GEORADIUSBYMEMBER
根据给定地理位置(具体的位置元素)获取指定范围内的地理位置集合。
GEOHASH
获取一个或者多个的地理位置的 GEOHASH 值。
Stream 消息队列
type
status
date
slug
summary
tags
category
icon
password
Property
目录
目录

 
Redis Stream是 Redis 5.0 版本引入的一种新数据类型,同时它也是 Redis 中最为复杂的数据结构。
在 Redis 5.0 Stream 没出来之前,消息队列的实现方式都有着各自的缺陷,例如:
  • 发布订阅模式,不能持久化也就无法可靠的保存消息,并且对于离线重连的客户端不能读取历史消息的缺陷;
  • List 实现消息队列的方式不能重复消费,一个消息消费完就会被删除,而且生产者需要自行实现全局唯一 ID。
基于以上问题,Redis 5.0 便推出了 Stream 类型也是此版本最重要的功能,用于完美地实现消息队列,它支持消息的持久化、支持自动生成全局唯一 ID、支持 ack 确认消息的模式、支持消费组模式等,让消息队列更加的稳定和可靠。
 
 
类型检查与命令多态
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
Redis中用于操作键的命令基本上可以分为两种类型。
一种命令可以对任何类型的键执行, 比如说 DEL 命令、 EXPIRE命令、 RENAME命令、 TYPE命令、 OBJECT命令, 等等。
使用DEL命令来删除三种不同类型的键:
而另一种命令只能对特定类型的键执行, 比如说:
  • SET 、 GET 、 APPEND 、 STRLEN 等命令只能对字符串键执行
  • HDEL 、 HSET 、 HGET 、 HLEN 等命令只能对哈希键执行
  • RPUSH 、 LPOP 、 LINSERT 、 LLEN 等命令只能对列表键执行
  • SADD 、 SPOP 、 SINTER 、 SCARD 等命令只能对集合键执行
  • ZADD 、 ZCARD 、 ZRANK 、 ZSCORE 等命令只能对有序集合键执行
内存回收和对象共享
type
status
date
slug
summary
tags
category
icon
password
Property
 

内存回收

因为 C 语言并不具备自动的内存回收功能, 所以 Redis 在自己的对象系统中构建了一个引用计数技术实现的内存回收机制, 通过这一机制, 程序可以通过跟踪对象的引用计数信息, 在适当的时候自动释放对象并进行内存回收。
每个对象的引用计数信息由 redisObject 结构的 refcount 属性记录:
对象的引用计数信息会随着对象的使用状态而不断变化:
  • 在创建一个新对象时, 引用计数的值会被初始化为 1 ;
  • 当对象被一个新程序使用时, 它的引用计数值会被增一;
  • 当对象不再被一个程序使用时, 它的引用计数值会被减一;
  • 当对象的引用计数值变为 0 时, 对象所占用的内存会被释放。
 
修改对象引用计数的 API:
对象的空转时长
type
status
date
slug
summary
tags
category
icon
password
Property
 
除了type 、 encoding 、 ptr 和 refcount 四个属性之外, redisObject 结构包含的最后一个属性为 lru 属性, 该属性记录了对象最后一次被命令程序访问的时间:
OBJECT IDLETIME 命令可以打印出给定键的空转时长, 这一空转时长就是通过将当前时间减去键的值对象的 lru 时间计算得出的:
OBJECT IDLETIME 命令的实现是特殊的, 这个命令在访问键的值对象时, 不会修改值对象的 lru 属性。
除了可以被 OBJECT IDLETIME 命令打印出来之外, 键的空转时长还有另外一项作用: 如果服务器打开了 maxmemory 选项, 并且服务器用于回收内存的算法为 volatile-lru 或者 allkeys-lru , 那么当服务器占用的内存数超过了 maxmemory 选项所设置的上限值时, 空转时长较高的那部分键会优先被服务器释放, 从而回收内存。
配置文件的 maxmemory 选项和 maxmemory-policy 选项的说明介绍了关于这方面的更多信息。
 
数据库
type
status
date
slug
summary
tags
category
icon
password
Property

 

服务器中的数据库

Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中,数组的每个项都是一个redis.h/redisDb结构,每个redisdDb结构代表一个数据库:
 
在初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少个数据库:
dbnum属性的值由服务器配置的database选项决定,该选项的值默认为16,所以Redis服务器默认会创建16个数据库:
RDB持久化
type
status
date
slug
summary
tags
category
icon
password
Property
目录
目录

 
Redis是一个键值对数据库服务器,服务器中通常包含着任意个非空数据库,而每个非空数据库中又可以包含任意个键值对:
notion image
因为Redis 是内存数据库,它将自己的数据库状态存储在内存里,所以如果办法将存储在内存中的数据库状态保存到磁盘里,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。为了解决这个问题,Redis提供了RDB持久化功能,这个功能可以将Redis在内存中的数据库状态保存到磁盘里,避免数据意外丢失。
RDB持久化既可以手动执行,也可以根据服务器配置选项定期执行,可以将某个时间点上的数据库状态保存到一个RDB文件中: