redis中所有单个命令的执行都是原子性的,但是不保证多个命令间的原子性。

Redis的多数据库

在redis中一个redis实例提供了多个用来存储数据的字典(又称数据库),客户端可以指定将数据存储在哪个字典中。redis默认支持16个数据库,数据库命名从0开始递增。

Redis基本命令

介绍基本命令之前我们先来看下redis中常用的通配符。
Redis学习笔记

KEYS命令

查询符合匹配规则的键,但是keys命令需要遍历所有的键,当键较多时会影响性能,不建议在生成环境中使用。

EXISTS命令

判断一个键是否存在,存在返回1,否则返回0。

DEL命令

删除一个或多个键,返回值时删除的键的个数。
Redis学习笔记

TYPE命令

获得键值的数据类型。

HELP命令

查询其它命令。

Redis数据类型

字符串类型

能够存储任何形式的字符串,一个字符串类型键允许存储的数据的最大容量为512MB。字符串类型时其它四种数据类型的基础。

常用命令

set key value:设置键值对
get key: 获取指定键的值,不存在返回空
incr key:递增指定键的值,指定键的值必须是整数否则会报错
incrby key increment:指定的键增加increment
decr key:递减指定键的值
decrby key decrement:指定的键减小decrement
append key value:向指定键尾部追加值
strlen key:获取指定键的值的长度
mget key、mset key value:获取、设置多个键值

散列类型

散列类型(hash)的键值也是一种字典结构,存储了字段(field)和字段值的映射,字段值只能是字符串,不支持其他数据类型嵌套。一个散列类型键可以包含至多2^32 - 1(大约42亿)个字段,散列类型有散列组成。

常用命令

hset key field value:设置field-value键值对
hget key field:获取指定field字段的值
hmset / hmget key field value:批量设置/获取field-value
hgetall key:获得key键下所有的field-value
hexits key field:判断字段是否存在
hset key field:只有当field字段不存在时才会赋值
hincrby key field increment:给指定字段的值加increment
hdel key field:删除指定字段
hkeys key / hvals key:获取所有字段名 / 字段值
hlen key:获取字段数量

列表类型

列表类型(list)存储一个有序的字符串列表,列表类型内部是由一个双向链表实现的。**借助列表类型,redis还可以作为队列或者栈使用。**由于列表类型是基于双向链表实现的,所有访问链表两端的数据效率高,当时当数据很多时访问中间的元素效率不高。

常用命令

lpush / rpush key value:向列表左边 / 右边添加元素,支持一次性添加多个数据
lpop / rpop key:从列表左边 / 右边取出元素,同时将列表中的数据一处,并返回被移除的值
llen key:获取列表的长度,redis有记录列表的长度,无需获取时遍历列表
lrange key start stop:获取列表的子列表,包含两端的元素
lrem key count value:删除列表中指定的值,count表示要删除值为value的元素的个数,count.>0表示从左边开始,count<0表示从右边开始,count=0表示删除所有的
lindex key index:获得指定索引的值
lset key index value:设置指定索引的值
ltrim key start end:只保留指定范围列表的值
linsert key before | after pivot value:在列表中pivot元素前或后插入元素
rpoplpush source destination:从列表右边弹出一个元素到指定列表中(将元素从一个列表转移到另一个列表)

集合类型

一个集合类型(set)键可以存储至多2^32 - 1(大约42亿)个字符串,集合类型是无序性、唯一性的,适合需要单独添加或删除的场景。

常用命令

sadd key meumber:添加元素,支持添加多个元素
srem key member:删除元素,支持删除多个元素
smembers key:获取集合中所有的元素
sismember key member:判断集合中元素是否存在
sdiff setA setB:求多个集合的差集
sinter setA setB:求多个集合的交集
sunion setA setB:求多个集合的并集
scard key:获得集合中元素的个数
sdiffstore / sinterstore / sunionstore destination key:进行集合运算并将结果存储到目的集合
srandmember key [ count ]:随机获得集合中的元素,count>0获得count个不重复的元素,如果count大于集合长度返回集合全部元素,count<0获得count个元素,允许重复
spop key:从集合中随机弹出一个元素

有序集合

在集合元素的基础上有序集合为集合中每个元素都关联了一个分数,我们可以根据分数进行一些相关操作。有序集合和集合类型一样是有序的,但是有序集合是根据散列表和跳跃表实现的,所以即使读取位于中间的元素速度也很快。有序集合保证集合中元素的唯一性

常用命令

zadd key score:添加多个元素
zscore key member:获取集合中元素的分数
zrange key start stop [withscores]:获取指定范围内的元素从小到大的顺序返回,withscores表示是否返回分数
zrevrange key start stop [withscores]:和上面类型,不过是从大到小的顺序返回
zrangebyscore key min max [withscore] [limit offset count]:获取指定分数范围的元素,withscore是否返回分数,limit offset count表示在获得的元素列表基础上向后偏移offset个元素,并且只获取前count个元素
zincrby key increment member:增加某个元素的分数
zcard key:获取集合中元素的数量
zcount key min max:获取指定分数范围内元素的个数
zrem key member:删除元素,支持删除多个
zremrangebyrank key start stop:按照排名范围删除元素
zremrangebyscore key min max:按照分数范围删除元素
zrank / zrevrank key member:获得元素的排名

事务

我们知道redis的所有单条命令都是原子性的,但是不能保证多条命令间的原子性,为了解决这个问题,我们可以通过事务来实现。MULTI(开始)…EXEC(结束)。事务会将期间执行的命令暂存进事务队列,等到使用EXEC命令后依次顺序执行,保证了一个事务中的命令要么全成功,要么都不执行。对于redis客户端而言,在执行事务的时候,会延迟执行已经入队的命令,知道发送EXEC命令为止,一次性将所有命令发送给服务器,减少客户端与redis服务端之间的网络通讯来提高性能。

错误处理

1、语法错误。在执行EXEC命令后redis直接返回错误,语法正确的命令也不执行。
2、运行错误。错误的命令返回报错信息,其它正常的命令正常执行。

WATCH命令

监控一个或多个键,如果在监控期间一旦其中有一个键被修改(或删除),之后的事务就不会执行,事务将失败并返回一个错误,用户可以选择重新执行事务或放弃事务,监控持续到执行EXEC命令。

WATCH命令只会在其它客户端修改了数据的情况下通知执行这个命令的客户端,而不会阻止其它客户端对数据进行修改,所以这个命令被称为乐观锁
Redis学习笔记

非事务型流水线

在事务中Redis提供了队列,可以批量执行任务,从而获得性能上的提升,但是MULTI和EXEC也是有开销的。有时我们希望在没有任何附加条件的情况下也能够使用队列批量执行一系列命令,这时可以使用Redis流水线(pipelined),他和事务一样也是一次性提交命令,但是没有MULTI和EXEC的开销。

在非事务中执行多条命令,在使用流水线批量执行的命令的情况下,比不用流水线性能要高四到五倍的样子。

过期时间

我们可以使用EXPIRE key seconds命令来设置过期时间,单位是秒。
通过TTL key可以查看键的剩余时间。
PERSIST key取消键的过期时间。此外使用SET或GETSET命令为键赋值也会清除键的过期时间。
PEXPIRE key seconds同样也是设置过期时间,单位是毫秒。

实现缓存

我们在缓存访问频率较高的数据,提高网站的负载能力时,可能会因为缓存数据的过期时间设置的过长,导致redis内存被占满。但是缓存键的过期时间又很难合理的设置过期时间,设置的过短会导致缓存命中率过低且大量的内存白白闲置。
这时我们可以通过设置redis最大可用内存(maxmemory)来限制redis的可用内存,当redis内存超出限制时会依据maxmemory-policy参数指定的策略来删除不需要的键直到redis占用内存小于指定内存。下图是maxmemory-policy支持的淘汰策略。
Redis学习笔记

排序

SORT命令

sort命令可以对集合和有序集合以及列表类型进行排序,不过在给有序集合排序时会忽略元素的分数,值针对元素自身进行排序。

sort命令默认是将字符串转换成双精度浮点数来进行排序,不过我们可以通过 ALPHA参数让 sort命令按照字典顺序排序DESC参数倒序排列。LIMIT offset count跳过前面offset位后取count个元素。

BY参数

语法:BY 参考键。参考键可以是字符串类型键或者是散列类型键的某个字段(表示为键名->字段名),在这里参考键我们可以使用通配符。
如果sort命令使用了by参数,那么sort命令将不再依据集合中元素自身的值进行排序,而是对每个元素使用元素的值替换参考键中的第一个“*”并获取其值,然后依据该值对元素进行排序(当参考键不包含“*”时,即常量键名,与元素值无关,sort命令将不会执行排序,因为这样是无意义的,所有要比较的值都一样)。
Redis学习笔记

GET参数

get参数不影响排序,他的作用是使sort命令的返回结果不再是元素自身的值,而是get参数中指定的键值,get参数和by一样,也支持字符串类型和散列类型的键,并使用“*”作为占位符。在一个sort命令中可以有多个get参数,如果还想返回ID(即集合中元素的值)可以使用 GET #。

STORE参数

语法:store 键名。可以将排序的结果保存在指定键名中,保存后键的类型为列表类型。

SORT性能优化

SORT命令十分强大,但是实用不好很容易成为性能瓶颈。SORT命令的时间复杂度为O(n + mlog(m)),其中n表示要排序的列表(集合或有序集合)中元素的个数,m表示要返回的元素个数。因此当n较大的时候SORT命令的性能相对较低。

优化点:
1、尽可能减少待排序键中元素的数量(使N尽可能小)。
2、使用LIMIT参数只获取需要的数据(使M尽可能小)。
3、如果要排序的数据较大,尽可能使用STORE参数将结果缓存。

Redis队列

任务队列

redis是利用列表类型,通过LPUSH和RPOP命令实现队列的概念。生产者通过LPUSH命令将消息加入到的列表中,消费者利用RPOP命令从列表的另一端取出消息即可。
通过使用BRPOP命令取出消息更加符合实际情况,BRPOP相对于RPOP的区别是当列表中没有元素时BRPOP命令会一直阻塞,直到有新的元素加入。同时我们还可以通过给BRPOP命令设置阻塞超时时间,当一定时间仍没有获取到元素时会返回nil。

先进先出队列

通过列表的LPUSH和RPOP命令(或者RPUSH和LPOP命令)可以实现一个先进先出队列(FIFO)。

后进先出队列

通过列表的LPUSH和LPOP命令(或者RPUSH和RPOP命令)可以实现一个后进先出队列(LIFO)。

优先级队列

BRPOP命令可以同时检测多个键,在前面的队列中的元素先消费(即前面的队列优先级高),从而实现了优先级队列。
语法:BRPOP key [key] timeout

延迟队列

通过将所有需要未来执行的任务都添加到有序集合中,并将任务的执行时间设置为分值,另外再使用一个进程来查找有序集合中是否存在可以立即被执行的任务,如果有的话,就从有序集合里面移除那个任务,并将它添加到适当的任务丢列里面。

发布订阅模式

发布订阅模式中有发布者和订阅者两种角色。订阅者可以订阅一个或多个频道(channel),发布者可以向指定的频道发送消息,命令分别是(publish / subscribe)。

redis的发布订阅模式比较的简单,有很大的不足:
1、发布者发布的消息不会被持久化,即订阅者只能接收到订阅后发布的消息,对于之前发布的消息接收不到,所以当没有一个订阅者的时候,那部分消息相当于丢失了。
2、如果客户端订阅了频道,但自己读取消息的速度不够快的话,那么不断积压的消息会使redis输出缓冲区的体积变得越来越大,这可能使得redis本身的速度变慢,甚至崩溃。
3、如果订阅者在传输期间断线的话,那么它会丢失这期间所有发布者发布的消息。

管道

redis通过tcp协议和客户端进行连接,在连接过程中客户端向redis发送命令或redis向客户端返回命令执行结果,都要经过网络传输,这部分的耗时称为往返延时。往返延时耗时相当于一条简单的redis命令的耗时,所以当执行很多条命令的时候,这个问题会消耗一部分性能。
为了解决这个问题,redis对管道协议提供了支持。通过管道可以一次性发送多条命令并将执行完后的结果一次性返回,不过需要同一组命令中的每条命令都不依赖于之前的命令的执行结果。

脚本

redis支持使用Lua语言编写执行脚本。使用脚本有很多好处:
1、减少网络开销,脚本中的命令一次性发送给redis。
2、原子操作,脚本中的所有命令作为一个整体执行,中间不会被其他命令插入。
3、复用,客户端发送的脚本会永久存储在redis中,可以被其它客户端复用。

持久化

RDB(内存快照)

当符合一定条件时redis会自动将内存中的所有数据生成一份副本存储在硬盘上,默认保存在工作目录中的dump.rdb文件中,每一次快照操作都是用新的快照文件替换旧的RDB文件。

进行快照的几种情况:
1、根据配置规则进行自动快照(例如m秒内有n个键被修改时进行快照)。
2、用户手动进行快照(SAVE或BGSAVE命令,BGSAVE相对于SAVE命令的区别是,他是异步执行的)。
3、执行FLUSHALL命令。
3、执行复制时(如主从模式中的复制过程)。

快照的过程:
1、Redis使用fork函数复制一份当前进程(父进程)的副本(子进程)。
2、父进程继续接受并处理可客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件。
3、当子进程写入完所有的数据后会用该临时文件替换旧的RDB文件,至此一次快照完成。

缺点:
通过RDB方式实现持久化,一旦Redis异常退出,那么就会丢失最后一次内存快照以后更改的以后更改的数据。

AOF

AOF将Redis执行的每一条写命令追加到硬盘的AOF文件中(即Redis执行增加、修改、删除操作的命令会被记录到AOF文件中),可以防止数据丢失,但是这一过程会降低Redis的性能。Redis默认没有开启AOF持久化,AOF文件默认保存路径为当前Redis的工作目录。

随着Redis写命令的越来越多,AOF文件会越来越大,里面的冗余命令将会越来越多(例如对A键进行了三次修改操作,那么进行恢复的时候前面两条命令可以说是冗余的),这时一但Redis挂了,进行重启根据AOF恢复数据时将会耗费很长的时间。为了解决这个问题,Redis在AOF文件达到一定大小的时候会自动对AOF文件进行重写,去除文件中的冗余命令。当然我们也可以手动进行重写。

相对于RDB持久化,AOF进行数据恢复更加耗时速度慢一些,但是AOF不存在RDB方式数据丢失的问题。

事实上AOF也还是会存在数据丢失的情况,因为虽然每次执行更改Redis内容的操作的时候,AOF都会将命令记录到AOF文件中,但是事实上,由于操作系统的缓存机制,这些数据并没有写入到硬盘,而是进入到系统的硬盘缓存,在默认情况下系统每30秒会执行一次同步操作,这个期间如果系统异常退出则会导致硬盘缓存中的数据丢失。为了防止这种丢失我们可以通过appendfsync参数来设置让系统同步缓存的时机。默认情况采用everysec(每秒一次同步操作),always(每次执行写入操作后执行同步操作,最安全也是最慢的方式),no(不主动进行同步,交由操作系统来控制,即30秒一次)。

集群

复制模式(主从复制)

虽然Redis持久化功能保证了即使服务器重启的情况下数据也不会丢失,但是如果这台服务器的硬盘出现故障,还是可能会导致数据丢失。因此,为了避免单点故障,我们将数据复制多个副本部署在不同的服务器上。

在复制模式中,数据库分为两类,主数据库和从数据库。主数据库可以进行读写操作,从数据库只能进行读操作,当主数据发生变化时会自动将数据同步给从数据库,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库(即主数据库只能有一个)。
Redis学习笔记

复制过程

复制初始化:当一个从数据库启动后,会向主数据库发送SYNC命令,主数据库接收到SYNC命令后会开始在后台保存快照(即RDB持久化过程),并将保存快照期间接收到的命令缓存起来,当快照完成后,Redis会将快照文件和所有缓存的命令发送给从数据库。从数据库收到后,会载入快照文件并执行收到的缓存命令。

复制同步:在复制初始化阶段结束后,主数据会将任何导致数据变化的命令异步的传送给从数据库。复制同步阶段Redis采用了乐观复制策略,允许主从数据库在一定时间内内容时不同的(即复制同步阶段是异步的进行命令同步),这个策略保证了在复制阶段主数据的性能不会受到影响。

哨兵模式

在前面的复制模式中,一旦主数据库挂了,我们就需要人工介入,将一个从数据升级为主数据库,使得系统能够继续提供服务,这个过程比较麻烦,难以实现自动化。为了解决这个问题Redis提供了哨兵模式。

哨兵的作用

哨兵的作用就是监控Redis系统的运行状况,他的功能主要包括一下两个。

1、监控主数据库和从数据库是否正常允许。
2、主数据出现故障时自动将从数据库转换为主数据库。

Redis学习笔记

除了上图的一个哨兵进行监控外,我们也可以使用多个哨兵进行监控,保证系统足够稳健。哨兵不仅可以监视主数据和从数据库,哨兵和哨兵之间也可以相互监控,防止出现在单个哨兵节点的架构中哨兵挂掉的情况。
Redis学习笔记

Redis的实际运用

分布式锁

前面我们讲到WATCH命令是一个乐观锁,它不阻止其它客户端对它监控的键的修改,只会在其它客户端修改了它监控的键时,通知执行这个命令的客户端撤销本次操作,客户端可以根据自己的需要进行重试或者抛弃本次执行。所以从它的特性我们可以看出,在高负载竞争加剧的情况下WATCH命令会引起客户端进行多次重试,从而影响性能,导致执行效率低下。

为了解决这个问题,我们可以自己通过redis实现分布式锁,给Redis存储的数据增加排他性。我们可以通过SETNX命令来实现分布式锁,因为SETNX(只有在key不存在的情况下才会将key的值设为value,否则不做任何操作)的特性,所以天生适合用来实现获取锁的操作。通常分布式锁的value是通过uuid随机生成的一个数字。

锁的重要性

通过分布式锁来代替WATCH命令监控Redis中的数据,不仅可以实现对key的监控,还可以实现更细粒度的监控(例如监控集合中的某行数据),细粒度的锁在高负载的情况下可以有效的减少锁竞争的几率。另一方面通过分布式锁给数据增加排他性,可以将其它客户端也对统一数据进行修改而引起的重试次数置为零,而不像WACTH命令那样会导致重试次数剧增,分布式锁可以有效的减少等待时间。正是以上两点,所以自己实现分布式锁的性能相对于WATCH命令要高很多。

锁不正确引起的问题

虽然分布式锁实现起来比较简单,但是一旦使用不正确很容易导致难以预料的问题。下面列出了一些导致锁出现不正确行为的原因,以及锁在不正确时的症状。
Redis学习笔记

细粒度锁

在锁的重要性时已经提到,这里不做复述。

超时锁

前面在谈锁不正确时引发的问题时,提到过因锁的持有者崩溃而导致锁无法释放,为了解决这个问题。我们可以在获取锁之后,通过EXPIRE命令来为锁设置过期时间,实现Redis可以自动删除超时的锁。**为了确保锁在客户端已经崩溃的情况下仍能够自动被释放(客户端在执行SETNX和EXPIRE之间崩溃是最糟糕的),客户端会在尝试获取锁失败后检查锁的超时时间,并为未设置超时间的锁设置超时间。**经过上述操作,因此锁总会带有超时时间,并最终因超时被释放,而不会导致死锁。

计数信号量

技术信号量是一种可以让用户限制一项资源最多能够同时被多少线程访问的锁。前面提到的锁可以看成是同一时刻只能被一个线程访问的计数信号量。

简单信号量

计数信号量要考虑的情况和构建锁基本差不多。通过有序集合来实现计数信号量,将持有者的信息(唯一标识)存储到有序集合中,对应的分值为尝试获取信号量时的时间戳。每次获取信号量前先清除时间戳大于超时数值的标识符(一般当前时间减去锁的持有时间为超时阈值,小于这个阈值的时间戳对应的标识符都相当于超时了),然后将自己添加到有序集合中,查看自己的排名,如果排名小于可以同时访问的信号量总数表明获取信号量成功,否则将自己从有序集合中删除。

公平信号量

前面我们已经介绍了一个基本的信号量怎么设计,但是存在一个问题。那就是在多主机环境下,不同系统间的系统时间不一定相同,例如系统A和B,A的系统时间比B快十毫秒,当A取得了最后一个信号量的时候,B如果在这十毫秒呢尝试获取信号量,那么就会在A不知情的情况下偷走A已经取得的信号量。这可能会导致难以预料的情况,所以这个信号量说不公平的。

因此为了解决运行在系统时间较慢的系统上的客户端,偷走系统时间较快的系统上的客户端已经取得的信号量,引入了公平信号量这个概念。

公平信号量在基本信号量(基本信号量只有一个超时有序集合)的基础上增加了一个计数器和一个有序集合。通过计数器持续的执行自增操作(客户端每次获取信号量之前先对计数器执行自增),确保最先对计数器执行自增操作的客户端能够得到信号量。然后将计数器自增后的值作为分值存储到上面说到的信号量拥有者有序集合中,有序集合的值是信号量拥有者的标识符,通过在信号量拥有者有序集合的排名来判断客户端是否取得信号量(在集合中排名低的标识符表示获得了信号量)。

刷新信号量

前面我们已经完成了一个比较完整的信号量,但是有的时候我们可能需要长时间持有信号量,固定的持有时间根本不够,那么应该怎么增长信号量的持有时间,不让其在超时有序集合中过期呢,我们可以通过刷新持有信号量的超时时间从而延长持有信号量的时间

消除信号量竞争(带有锁的信号量)

虽然我们前面通过公平锁消除了不同系统上时间可能存在差异这一隐患因素,但是于此同时带来了两外一个问题。那就是会存在不同客户端竞争信号量的情况,比如进程A和B同时竞争最后一个信号量时,即使A首先对计数器进行自增操作,但是只要B能够抢在A之前将自己的标识符添加到有序集合中,并检查信号量拥有者有序集合的排名,那么B就可以获取到信号量,但是之后A将自己的标识符添加到有序集合后,会偷走B的信号量,而只有在尝试释放信号量或者刷新信号量的时候才会发现。

那么怎么消除不同客户端之间对信号量的竞争呢?我们可以借鉴前面提到的分布式锁,在程序想要获取信号量之前,通过SETNX命令构建短暂超时的分布式锁,如果构建成功代表该客户端成功的获取到这把锁,那就执行正常的信号量操作,否则就宣布获取信号量失败。

信号量的使用场景

信号量除了可以限制同时可运行的API数量之外,还可以用来限制数据库并发访问的数量,从而降低单个查询所需的时间。

降低内存占用

短结构

Redis为列表、集合、散列和有序集合提供了一组配置选项,这些选项可以让Redis以更节约空间的方式存储长度较短的结构(简称短结构)。

压缩列表

前面提到的几种数据类型。我们都可以通过list-max-ziplist-entrieslist-max-ziplist-value配置参数,设置其什么情况下可以以压缩列表(ziplist)的编码形式来存储数据,从而达到节省内存空间的目的。entries表示编码格式为压缩列表的情况下,允许包含的最大元素数量;value表示压缩列表每个节点的最大体积是多少字节。

因为以压缩列表的方式存储数据,每次读取的时候都要进行解码,每次写入的时候也要重新进行编码,甚至有可能要对内存里面的数据进行移动,所以如果压缩列表长度过大,会严重影响性能,所以通过上面两个参数进行限制。如果压缩列表存储的数据超过了上面两个参数的限制,那么压缩列表就会转换回基本数据类型,即使后面又达到了符合压缩列表的条件也不会重新转换成压缩列表。

字符串类型键

在字符串类型中是采用一个redisObject类型变量来存储字符串,为了节约空间,Redis在启动后会创建10000个分别存储0到9999这些数字的redisObject类型变量作为共享变量,因此若字符串键值在这个10000个数字内,则可以直接引用这些共享变量,不再需要创建一个redisObject。所以字符串类型键存储这种小数字是非常节约空间的。

集合的整数集合编码

储数据,每次读取的时候都要进行解码,每次写入的时候也要重新进行编码,甚至有可能要对内存里面的数据进行移动,所以如果压缩列表长度过大,会严重影响性能,所以通过上面两个参数进行限制。如果压缩列表存储的数据超过了上面两个参数的限制,那么压缩列表就会转换回基本数据类型,即使后面又达到了符合压缩列表的条件也不会重新转换成压缩列表。

字符串类型键

在字符串类型中是采用一个redisObject类型变量来存储字符串,为了节约空间,Redis在启动后会创建10000个分别存储0到9999这些数字的redisObject类型变量作为共享变量,因此若字符串键值在这个10000个数字内,则可以直接引用这些共享变量,不再需要创建一个redisObject。所以字符串类型键存储这种小数字是非常节约空间的。

集合的整数集合编码

如果集合中的成员都是十进制帧数,并且这些整数都处于平台的有符号整数范围之内(这里个人理解是用到了上面提到的字符串类型键那个10000个共享变量),并且集合成员的数量有足够的少的话,那么redis就会以有序整数数组的方式存储集合。和压缩列表一样,整数集合中的数据也有限制,可以通过set-max-ziplist-entries参数来设置,防止集合长度过长影响性能。

相关文章: