Home

cuipf blog

06 Mar 2018

Redis常用数据结构总结

Redis数据结构

字符串

字符串类型是redis的最基本类型, 他能存储任何形式的字符串, 包括二进制数据; 一个字符类型键允许存储的数据的最大容量是512MB;

常用命令

  • set key value 设置键值;
  • get key 获取键值;
  • incr key 原子操作递增,当字符串为整数形式时;
  • incrby key num 增加指定的整数; 同理还有incrbyfloat增加指定的浮点数;
  • decr key 减少指定的整数;
  • decrby key num 减少指定的整数; 同理也有decrbyfloat;
  • append key value 想键值的末尾追加value,如不存在,则将该键值设置为value,返回的是追加之后的字符串的长度;
  • strlen key 获取字符串的长度;如果键值不存在返回0;
  • mget key [key ..] 同时获得多个键值;
  • mset key value [key value…] 同时设置多个键值;
  • getbit key offset 位操作;
  • setbit key offset value 位操作;
  • bitcount key start end 获取字符串中的值为1 的二进制个数
  • bitop op destkey key1 .. 可以对多个字符串进行or and xor not位运算并把结果保存在destkey;

散列类型

redis是采用字典结构以键值对的形式存储数据的, 而散列类型(hash)的键值也是一种字典结构, 保存了字段(field)和字段值的映射;字段值只能是字符串, 不支持其他数据类型;也就是说散列类型不能再嵌套其他的数据类型;

  • 一个散列类型键可以包含至少2^32 - 1个字段.
  • 除了散列类型, redis其他数据类型同样不支持数据类型嵌套;比如集合类型的每个元素都只能是字符串, 不能使另一个集合和散列表等;
  • 散列类型适合存储对象: 使用对象类别和ID构成键名, 使用字段表示对象的属性;而字段值则存储属性值;如下:

散列类型使用实例

常用的命令

  • hset、hget、hmset、hmget、hgetall、hexists、hdel、hincr等,格式如:hset key field value;具体格式不再赘述;
  • set相关指令使用前无需判断元素是否存在,存在更新、不存在插入
  • hsetnx key field value 字段不存在赋值,区别与hset,如果字段已经存在就不在更新。
  • hkeys key 获取字段名 hvals key获取字段值,hlen获取字段数量;

列表类型

列表类型可以存储有序的字符串列表,常用的操作是向列表两端添加元素或者获取列表的某一个片段。列表使用的是双向链表实现。向列表两端添加元素时间复杂度为O(1),取出两端的前十个速度极快; 不过使用链表代价就是通过索引访问元素比较慢.

常用的场景:社交网站的新鲜事情,我们关心的往往是最前面的内容;同时也非常适合来记录日志,保证新加入的日志不会受到已有日志数量的影响。

常用命令

  • lpush、rpush向列表两端添加元素;
  • lpop、rpop从列表两端弹出元素;
  • llen key 获取列表中元素的个数, 当键不存时候, llen会返回0; 时间复杂度为o(1);
  • lrange获取列表片段 lrange key start stop;
  • 其他命令:lrem,lindex获取指定索引的元素值。
  • lset key index value 设置指定索引的元素值
  • ltrim key start end 只保留指定的片段。
  • 向列表插入元素 linsert key before/after pivot value
  • rpoplpush src des 将元素从一个列表移动到另一个列表;

集合类型

  • 集合中每个元素都是不同的(唯一性),且没有顺序(无序);
  • 一个集合类型(set)键可以存储至多2^32 - 1个字符串;
  • 集合的常用操作是向集合中加入和删除元素, 判断某个元素是否存在等; 由于集合类型集合类型在redis内部是使用值为空的散列表实现,所以这些操作时间复杂度都是0(1);
  • 存储的内容无序且是唯一的。最方便的是多个集合之前还可以使用并集,交集,差集。

常用命令

  • sadd srem 添加删除;
  • smembers key 获取集合中的所有元素;
  • sismember key member 判断元素是否在集合中;
  • sdiff key key..差集 sunion 并集 sinter 交集;
  • scard key 获取集合中元素的个数;
  • 进行集合运算并将结构存储 sdiffstore sinterstore sunionstore dest key key..
  • srandmember key count 随机获取一个或者多个集合中的元素 count >0 获取count个随机元素且都不相同;count < 0获取 count 个元素这些元素有可能相同;
  • spop key 随机弹出一个元素;

有序集合

有序集合相比集合类型主要的特点就是”有序”, 在集合类型的基础上有序集合中的每一个元素都关联了一个分数;这使得我们不仅可以完成插入,删除,和判断元素是否存在等集合类型支持的操作, 还能够获得分数最高(最低)的前N个元素, 获取指定分数范围内的元素等与分数有关的操作;虽然集合的每个元素都是不同的, 但是他们的分数却可以相同.

有序集合和列表类型的异同

  1. 二者都是有序的;
  2. 二者都可以获得某一范围的元素;
  3. 列表类型是通过链表实现的, 获取靠近两端的数据速度极快, 而当元素增多后, 访问中间数据的速度会较慢, 所以更加的适合”新鲜事”或”日志”这样很少访问中间元素的应用;
  4. 有序集合类型是使用散列表和跳表实现的, 所以即使读取位于中间的部分数据速度也很快(时间复杂度o(log(N)));
  5. 列表中不能简单地调整某个元素的位置, 但是有序集合可以(通过更改这个元素的元素);
  6. 有序集合要比列表类型更加消耗内存;

常用命令

  • zadd key score Number [score member…] 用来向有序集合中加入一个元素和该元素的分数; 如果元素已经存在用新的分数替换原来的分数; 返回值是新加入到集合中的元素个数;
  • zscore key member 获取元素分数;
  • zrange key start stop withscores 获取排名在某个范围的元素列表(从小到大); 时间复杂度(O(logn+m), n为有序集合的基数, m为返回的元素个数);
  • zrevrange key start stop [withscores] 和上面唯一不同是, 元素是从大到小;
  • zrangebyscore key min max [withscores] [limit offset count] 按照元素分数从小到大的顺序返回分数在min和max之间的元素; 如果希望分数不包括端点值, 可以在分数前加上”(“符号; 如: zrangebyscore scoreboard 90 (100;
  • zincrby key increment member 增加某个元素的分数;返回值为更改后的分数;
  • zcard key 获取集合中元素的数量;
  • zcount key min max 获取指定分数范围内的元素个数;
  • zrem key member 删除一个或者多个元素
  • zremrangebyrank key start stop 按照排名范围删除元素; 删除指定范围内的所有元素, 并返回删除的元素数量; 时间复杂度(O(log(N)+M));
  • zrank key member 按从小到大的顺序获取指定元素的排名, 时间复杂度(O(log(N));
  • zrevrank key member 按从大到小的顺序获取指定元素的排名, 时间复杂度(O(log(N));
  • zinterstore destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE sum|min|max] 计算多个有序集合的交集,并将结果保存在destination键中, 返回结果是destination中的元素个数;weights参数设置每个集合的权重, 每个集合都在参与计算时元素的分数会被乘上该集合的权重. destination键中元素的分数是由aggregate参数决定:
    1. 当时sum, 那么destination键中元素分数是每个参与计算的集合中该元素分数的和;
    2. 当时min, 则是所有参与计算的集合中元素分数的最小值;
    3. 当时max, 则是所有参与计算的集合中该元素分数的最大值;

HyperLogLog

redis3.2版本加入了该数据结构; 该结构是用来做基数统计的算法, HyperLogLog的优点是: 他不仅能做去重处理, 更重要的是可以在海量的元素下, 占用的内存空间要比redis集合要少很多, 每个HyperLogLog键只需要花费12K的内存, 就可以计算2^64个不同元素的基数,

使用场景

这个结构可以非常省内存的去统计各种计数, 比如注册ip数, 每日访问IP数, 页面的实时UV, 在线用户等;但是这个结构可以比较准确的估算出你要统计的数量, 但是却无法知道统计的详细内容.

常用命令

  • PFADD key element [element …] 添加元素到HyperLogLog中;
  • PFCOUNT key [key…] 返回给定HyperLogLog的基数估算值;
  • PFMERGE destkey sourcekey [sourcekey …] 将多个HyperLogLog合并为一个HyperLogLog;

GEO(地理位置)

这个结构用于缓存用户给定的地理位置信息, 并对这些信息进行操作;

常用命令

  • geoadd location-set longitude latitude name [longitude latitude name …]

    记录具体的地理位置; GEOADD 命令每次可以添加一个或多个经纬度地理位置。 其中 location-set 为储存地理位置的集合, 而 longitude 、 latitude 和 name 则分别为地理位置的经度、纬度、名字。

redis> GEOADD Guangdong-cities 113.2099647 23.593675 Qingyuan
1    -- 成功添加一个位置

redis> GEOADD Guangdong-cities 113.2278442 23.1255978 Guangzhou 113.106308 23.0088312 Foshan 113.7943267 22.9761989 Dongguan 114.0538788 22.5551603 Shenzhen
4    -- 成功添加四个位置
  • geopos location-set name [name …]

    输入位置的名字, 并获取位置具体的经纬度;

redis> GEOPOS Guangdong-cities Qingyuan Guangzhou Foshan
1) 1) "113.20996731519699"    -- 清远的经度
   2) "23.593675019671288"    -- 清远的纬度
2) 1) "113.22784155607224"    -- 广州的经度
   2) "23.125598202060807"    -- 广州的纬度
3) 1) "113.10631066560745"    -- 佛山的经度
   2) "23.008831202413539"    -- 佛山的纬度
  • geodist location-set location-x location-y [unit]

    计算两个位置之间的距离, 调用该命令用户需要提供计算差距的地点location-x 和 location-y, 以及存储这两个位置的集合; uint的可选参数: m 米, 千米km, 英里mi, 英尺ft;

redis> GEODIST Guangdong-cities Qingyuan Guangzhou km
"52.094433840356309"    -- 两地相距 52 公里
  • georadius location-set longitude latitude radius m km ft mi [WITHCOORD] [WITHDIST] [ASC DESC] [COUNT count]

georadiusbymember location-set location radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

找出指定范围内的其他存在的地点, Redis 提供了 GEORADIUS 和 GEORADIUSBYMEMBER 两个命令来实现查找特定范围内地点的功能, 它们的作用一样, 只是指定中心点的方式不同: GEORADIUS 使用用户给定的经纬度作为计算范围时的中心点, 而 GEORADIUSBYMEMBER 则使用储存在位置集合里面的某个地点作为中心点。

* m|km|ft|mi 指定的是计算范围时的单位;
* 如果给定了可选的 WITHCOORD , 那么命令在返回匹配的位置时会将位置的经纬度一并返回;
* 如果给定了可选的 WITHDIST , 那么命令在返回匹配的位置时会将位置与中心点之间的距离一并返回;
* 在默认情况下, GEORADIUS 和 GEORADIUSBYMEMBER 的结果是未排序的, ASC 可以让查找结果根据距离从近到远排序, 而 DESC 则可以让查找结果根据从远到近排序;
* COUNT 参数指定要返回的结果数量。
redis> GEORADIUSBYMEMBER Guangdong-cities Guangzhou 50 km
1) "Foshan"
2) "Guangzhou"

cuipf

scribble