文章目录
Redis常用数据结构操作与底层原理
一、库
Redis下有16个数据库,分别名为0~15,彼此保存的值互不干扰。
- select dbno:选择dbno数据库。
- flushdb:清空当前数据库的内容。
- flushall:清空所有数据库的内容。
二、键
- del key:删除键。
- exists key:查看键是否存在。
- keys:查看当前数据库下有哪些键。
- expire key seconds:给key设置seconds秒后过期。
- ttl key:查看键的剩余生存时间(以秒为单位),-1表示永久存活,-2表示已过期,过期的键会被删除。
- persist key:移除键的过期时间,让其永久存活。
- type key:查看键对应值的数据类型。
- randomkey:从当前数据库随机返回一个键。
- rename key newkey:给键改名。
三、核心数据类型
1、string
底层使用简单动态字符串(SDS)。
- set key value:设置键值对,如果键已存在,则修改其对应值。
- setnx key vlaue:只有键不存在时才设置键值对。
- get key:获取键对应的值。
- mget key1[ key2 …]:获取多个键对应的值。
- incr key:将key对应的整型值加一,如果是非整型值则报错。
- incrby key increment:将key对应的整型值加increment。
- decr key:将key对应的整型值减一。
- decrby key decrement:将key对应的整型值减decrement
- append key value:将value追加到key对应值的后面,如果key不存在,则先创建一个空值,再追加。
2、list
每个元素都小于64字节,且元素个数小于512时,使用压缩列表,否则使用双链表。
- lpush key value1[ value2 …]:将一个或多个值按顺序头插到key对应的列表中。
- rpush key value1[ value2 …]:将一个或多个值按顺序尾插到key对应的列表中。
- lpop key:移除并获取列表的第一个元素。
- rpop key:移除并获取列表的第一个元素。
- lrange key start stop:获取下标范围为[start , stop]的值。下标从0开始,-1表示最后一个元素,-2表示倒数第二个,以此类推。
- llen key:获取key对应列表的长度。
- lindex key index:获取列表中下标为index的元素。
- linsert key before/after pivot value:在值为pivot的节点前插入value。
3、set
当元素都是整型,且不超过512个时,使用整数集合,否则使用哈希表。
- sadd key value1[ value2, …]:将一个或多个值插入到key对应的集合中。
- sismember key value:判断value是否在集合key中。
- smembers key:返回集合key中的所有值。
- scard key:获取集合中的元素个数。
- sunion key1 [key2 …]:返回多个集合的并集。
- sinter key1 [key2 …]:返回多个集合的并集。
- sdiff key1 [key2 …]:返回第一个集合与其它集合的差集。
- spop key:移除集合key中的一个随机元素。
- smove source destination value:将集合source中的value转移到集合destination中。
- srem key value1 [value2 …]:将一个或多个值从集合中删除。
4、hash
当每个元素都小于64字节,且元素个数小于512时,使用压缩列表(键值对相邻存储),否则使用哈希表。
- hset/hmset key field value [field2 value … …]:将一个或多个key中filed字段的值设为value。
- hsetnx key field value [field2 value … …]:只有在field字段不存在时才将key中filed字段的值设为value。
- hget key field:获取field字段的值。
- hmget key field [field2 …]:获取多个字段的值。
- hkeys key:获取哈希表key中的所有字段名。
- hvals key:获取哈希表key中的所有值。
- hdel key field [field2 …]:删除key中的一个或多个字段。
- hexists key field:查看key中指定字段是否存在。
- hgetall key:获取key中所有字段及其对应值。
- hincrby key field increment:将field字段对应值加上increment。
5、zset
当每个元素都小于64字节,且元素个数小于128时,使用压缩列表,否则使用跳表。
此外,使用了一个哈希表存储
成员:分数
的映射,方便以O(1)的效率获取成员的对应分数。
- zadd key score1 member1 [score2 member2 … …]:将一个或多个成员及其分数添加至key对应的zset中,如果成员存在则更新它的分数。
- zcard key:获取成员数量。
- zcount key min max:计算分数在[min, max]的成员数量。
- zincrby key increment member:将member对应的分数加上increment。
- zrank key member:获取指定成员的下标。
- zrange key index1 index2 [withscores]:返回指定下标区间的成员[及其分数]。下标规则与list相同。
- zrangebyscore key min max [withscores]:返回指定分数区间的成员[及其分数]。
- zrem key member [member1 …]:删除一个或多个成员。
- zscore key member:返回成员的分数。
- zrevrange key index1 index2 [WITHSCORES]:返回指定索引区间内的成员,分数从高到低。
为什么zset使用跳表而非红黑树
- 跳表的代码更简单,容易实现。
- 对于区间查询,它们都可以通过O(logn)的效率找到起始点,但是链表的遍历比红黑树要方便,且效率略高。
- 跳表的插入删除只需要修改部分指针,不像红黑树那样需要调节颜色和左右旋转。
四、新增数据类型
1、bitmaps
bitmap,即位图,底层使用简单动态字符串来实现,可用作统计活跃用户等数据量大且只需标记的业务。
位图只能存储0/1值。
- getbit key offset:获取在偏移量为offset处的值。
- setbit key offset value:设置在偏移量为offset处的值。
- bitcount key [start end]:获取位图中值为1的二进制位的数量(计算汉明重量)。strat和end可以指明在哪个字节范围。
- bitop and destkey key1 [key2 …]:对key1 [key2 …]位图执行与操作,并将结果保存至destkey中。
- bitop or destkey key [key2 …]:对key1 [key2 …]位图执行或操作,并将结果保存至destkey中。
- bitop not destkey key:对key位图执行非操作,并将结果保存至destkey中。
- bitop xor destkey key1 [key2 …]:对key1 [key2 …]位图执行异或操作,并将结果保存至destkey中。
2、hyperloglog
hyperloglog用作基数统计,即一个集合中不重复元素的数量,在计算不重复用户页面访问量等情形时应用较多。
hyperloglog基于伯努利实验的概率统计算法,每个hyperloglog都占用12KB,但是能够对2^64个作基数统计,且误差率能控制在0.81%。
- pfadd key element [element2, …]:添加成员,时间复杂度O(1)。
- pfcount key [key2 …]:返回一个或多个hyperloglog并集的近似基数。key有多个时,该命令为O(n),单个key则为O(1)。
- pfmerge destkey key [key2 …]:将一个或多个hyperloglog合并到destkey中。
3、geo
geographic,底层使用zset实现,用来存储经纬度信息。
注:Redis使用geohash算法将经纬度转为score值存储到zset中。
- geoadd key longitude latitude member [longitude2 latitude2 member2 …]:添加一个或多个成员,并设置经度和纬度。
- geopos key member:获取指定成员的经纬度。
- geodist key member1 member2 [m|km|ft|mi]:获取两个成员的距离,单位可指定位米、千米、英尺、英里。
- georadius key longtitude latitude radius [m|km|ft|mi]:获取以指定经纬度为圆心,radius为半径内的成员。
- zrem key member:删除成员。
部分底层数据结构
一、压缩列表
压缩列表:
其中zlbytes记录了整个压缩列表的内存字节数,zltail记录了尾结点距离列表起始地址的偏移量,zllen记录了列表中有多少个节点。
注:由于保存前一个节点的偏移量,因此压缩列表的遍历是从后往前。
每个节点的构成如下:
通过当前节点起始地址 - prevoid_entry_length
即可算出上一个节点的起始地址。
压缩列表是一种以时间换空间的数据结构,它的插入删除平均效率为O(n),但是在最坏情况下触发连锁更新,则效率为O(N2)。
连锁更新
![嗨客网(www.haicoder.net)](https://i-blog.csdnimg.cn/blog_migrate/8c268d2d01d34aa90e2aa7d1b2ea70f1.png)
当前一个节点的长度小于254字节,则prevoid_entry_length
为1字节;
当前一个节点的长度大于等于254字节,则prevoid_entry_length
为5字节;
因此,如果插入一个较大的节点,则可能导致下一个节点的prevoid_entry_length
字节数增加。一旦增加的部分刚好使下一个节点超过254字节,则下下个节点又需要更改,以此类推。
如此一来,循环更新就会导致对压缩列表的增删效率降至O(n2)。
二、整数集合
整数集合以有序、无重复的方式保存集合元素。
数组元素的类型根据最大的元素而定,从int16_t
到int32_t
再到int64_t
。当新插入的元素大小超过当前数组类型时,则将整数集合升级成能存储新元素的数组类型,同时数组中的所有元素都要进行类型升级。
注:整数集合不支持降级。
由于要保持有序,因此对整数集合的增删皆为O(n),但是查找可以使用O(logn)的二分法。
相比于哈希表,整数集合是一种以时间换空间的做法。