Redis数据类型大全:“5种基础”数据类型+“3种特殊”数据类型

  Redis 有望所有的key(键)都是字符串。我们在谈基础数据结构时,讨论的是存储值的数据类型,主要包括常见的5种数据类型,分别是:String、List、Set、Zset、Hash

  

  Redis基础文章非常多,关于基础数据结构类型,我推荐你先看下官方网站内容 (opens new window),然后再看下面的小结

  首先对redis来说,所有的key(键)都是字符串。我们在谈基础数据结构时,讨论的是存储值的数据类型,主要包括常见的5种数据类型,分别是:String、List、Set、Zset、Hash。

  

  内容其实比较简单,我觉得理解的重点在于这个结构怎么用,能够用来做什么?所以我在梳理时,围绕图例,命令,执行和场景来阐述

  String是redis中最基本的数据类型,一个key对应一个value。

  String类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,字符串,jpg图片或者序列化的对象。

  图例

  下图是一个String类型的实例,其中键为hello,值为world

  

  命令使用

  

  命令执行

  127.0.0.1:6379> set hello worldOK127.0.0.1:6379> get hello"world"127.0.0.1:6379> del hello(integer) 1127.0.0.1:6379> get hello(nil)127.0.0.1:6379> get counter"2"127.0.0.1:6379> incr counter(integer) 3127.0.0.1:6379> get counter"3"127.0.0.1:6379> incrby counter 100(integer) 103127.0.0.1:6379> get counter"103"127.0.0.1:6379> decr counter(integer) 102127.0.0.1:6379> get counter"102"

  实战场景

  缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力。

  计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。

  session:常见方案spring session + redis实现session共享

  Redis中的List其实就是链表(Redis用双端链表实现List)。

  使用List结构,我们可以轻松地实现最新消息排队功能(比如新浪微博的TimeLine)。List的另一个应用就是消息队列,可以利用List的 PUSH 操作,将任务存放在List中,然后工作线程再用 POP 操作将任务取出进行执行。

  图例

  

  命令使用

  

  使用列表的技巧

  lpush+lpop=Stack(栈)

  lpush+rpop=Queue(队列)

  lpush+ltrim=Capped Collection(有限集合)

  lpush+brpop=Message Queue(消息队列)

  命令执行

  127.0.0.1:6379> lpush mylist 1 2 ll ls mem(integer) 5127.0.0.1:6379> lrange mylist 0 -11) "mem"2) "ls"3) "ll"4) 5) "1"127.0.0.1:6379> lindex mylist -1127.0.0.1:6379> lindex mylist 10 # index不在 mylist 的区间范围内(nil)

  实战场景微博

  TimeLine: 有人发布微博,用lpush加入时间轴,展示新的列表信息。

  消息队列

  Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

  Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

  图例

  

  命令使用

  

  命令执行

  127.0.0.1:6379> sadd myset hao hao1 xiaohao hao(integer) 3127.0.0.1:6379> smember myset1) "xiaohao"2) "hao1"3) "hao"127.0.0.1:6379> sismember myset hao(integer) 1

  实战场景

  标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。

  点赞,或点踩,收藏等,可以放到set中实现

  Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。

  图例

  

  命令使用

  

  命令执行

  127.0.0.1:6379> hset user name1 hao(integer) 1127.0.0.1:6379> hset user email1 hao@163.com(integer) 1127.0.0.1:6379> hgetall user1) "name1"2) 3) "email1"4) "hao@163.com"127.0.0.1:6379> hget user user(nil)127.0.0.1:6379> hget user name1127.0.0.1:6379> hset user name2 xiaohao(integer) 1127.0.0.1:6379> hset user email2 xiaohao@163.com(integer) 1127.0.0.1:6379> hgetall user1) 2) 3) 4) 5) "name2"6) 7) "email2"8) "xiaohao@163.com"

  实战场景

  缓存: 能直观,相比string更节省空间,的维护缓存信息,如用<typo id="typo-2936" data-origin="能" ignoretag="true">户</typo>信息,视频信息等

  Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。

  有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

  图例

  

  命令使用

  127.0.0.1:6379> zadd myscoreset 100 hao 90 xiaohao(integer) 2127.0.0.1:6379> ZRANGE myscoreset 0 -11) 2) 127.0.0.1:6379> ZSCORE myscoreset hao"100"

  实战场景

  排行榜:有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行

  Redis除了上文中5种基础数据类型,还有三种特殊的数据类型,分别是 HyperLogLogs(基数统计), Bitmaps (位图) 和 geospatial (地理位置)

  Redis 2.8.9 版本更新了 Hyperloglog 数据结构!

  什么是基数?

  举个例子,A = {1, 2, 3, 4, 5}, B = {3, 5, 6, 7, 9};那么基数(不重复的元素)= 1, 2, 4, 6, 7, 9; (允许容错,即可以接受一定误差)

  HyperLogLogs 基数统计用来解决什么问题?

  这个结构可以非常省内存的去统计各种计数,比如注册 IP 数、每日访问 IP 数的页面实时UV、在线用户数,共同好友数等。

  它的优势体现在哪?

  一个大型的网站,每天 IP 比如有 100 万,粗算一个 IP 消耗 15 字节,那么 100 万个 IP 就是 15M。而 HyperLogLog 在 Redis 中每个键占用的内容都是 12K,理论存储近似接近 2^64 个值,不管存储的内容是什么,它一个基于基数估算的算法,只能比较准确的估算出基数,可以使用少量固定的内存去存储并识别集合中的的唯一元素。而且这个估算的基数并不一定准确,是一个带有 0.81% 标准错误的近似值(对于可以接受一定容错的业务场景,比如IP数统计,UV等,是可以忽略不计的)。

  相关命令使用

  127.0.0.1:6379> pfadd key1 a b c d e f g h i # 创建第一组元素(integer) 1127.0.0.1:6379> pfcount key1 # 统计元素的基数数量(integer) 9127.0.0.1:6379> pfadd key2 c j k l m e g a # 创建第二组元素(integer) 1127.0.0.1:6379> pfcount key2(integer) 8127.0.0.1:6379> pfmerge key3 key1 key2 # 合并两组:key1 key2 -> key3 并集OK127.0.0.1:6379> pfcount key3(integer) 13

  Bitmap 即位图数据结构,都是操作二进制位来进行记录,只有0 和 1 两个状态。

  用来解决什么问题?

  比如:统计用户信息,活跃,不活跃! 登录,未登录! 打卡,不打卡! 两个状态的,都可以使用 Bitmaps!

  如果存储一年的打卡状态需要多少内存呢? 365 天 = 365 bit 1字节 = 8bit 46 个字节左右!

  相关命令使用

  使用bitmap 来记录 周一到周日的打卡! 周一:1 周二:0 周三:0 周四:1 ......

  127.0.0.1:6379> setbit sign 0 1(integer) 0127.0.0.1:6379> setbit sign 1 1(integer) 0127.0.0.1:6379> setbit sign 2 0(integer) 0127.0.0.1:6379> setbit sign 3 1(integer) 0127.0.0.1:6379> setbit sign 4 0(integer) 0127.0.0.1:6379> setbit sign 5 0(integer) 0127.0.0.1:6379> setbit sign 6 1(integer) 0

  查看某一天是否有打卡!

  127.0.0.1:6379> getbit sign 3(integer) 1127.0.0.1:6379> getbit sign 5(integer) 0

  统计操作,统计 打卡的天数!

  127.0.0.1:6379> bitcount sign # 统计这周的打卡记录,就可以看到是否有全勤!(integer) 3

  Redis 的 Geo 在 Redis 3.2 版本就推出了! 这个功能可以推算地理位置的信息: 两地之间的距离, 方圆几里的人

  添加地理位置

  127.0.0.1:6379> geoadd china:city 118.76 32.04 manjing 112.55 37.86 taiyuan 123.43 41.80 shenyang(integer) 3127.0.0.1:6379> geoadd china:city 144.05 22.52 shengzhen 120.16 30.24 hangzhou 108.96 34.26 xian(integer) 3

  规则

  两级无法直接添加,我们一般会下载城市数据(这个网址可以查询 GEO: www.jsons.cn/lngcode)!

  有效的经度从-180度到180度。

  有效的纬度从-85.05112878度到85.05112878度。

  # 当坐标位置超出上述指定范围时,该命令将会返回一个错误。127.0.0.1:6379> geoadd china:city 39.90 116.40 beijin(error) ERR invalid longitude,latitude pair 39.900000,116.400000

  获取指定的成员的经度和纬度

  127.0.0.1:6379> geopos china:city taiyuan manjing1) 1) "112.54999905824661255" 1) "37.86000073876942196"2) 1) "118.75999957323074341" 1) "32.03999960287850968"

  获得当前定位, 一定是一个坐标值!

  如果不存在, 返回空

  单位如下

  m

  km

  mi 英里

  ft 英尺

  127.0.0.1:6379> geodist china:city taiyuan shenyang m"1026439.1070"127.0.0.1:6379> geodist china:city taiyuan shenyang km"1026.4391"

  附近的人 ==> 获得所有附近的人的地址, 定位, 通过半径来查询

  获得指定数量的人

  127.0.0.1:6379> georadius china:city 110 30 1000 km 以 100,30 这个坐标为中心, 寻找半径为1000km的城市1) "xian"2) "hangzhou"3) "manjing"4) "taiyuan"127.0.0.1:6379> georadius china:city 110 30 500 km1) "xian"127.0.0.1:6379> georadius china:city 110 30 500 km withdist1) 1) "xian" 2) "483.8340"127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord withdist count 21) 1) "xian" 2) "483.8340" 3) 1) "108.96000176668167114" 2) "34.25999964418929977"2) 1) "manjing" 2) "864.9816" 3) 1) "118.75999957323074341" 2) "32.03999960287850968"

  参数 key 经度 纬度 半径 单位 [显示结果的经度和纬度] [显示结果的距离] [显示的结果的数量]

  显示与指定成员一定半径范围内的其他成员

  127.0.0.1:6379> georadiusbymember china:city taiyuan 1000 km1) "manjing"2) "taiyuan"3) "xian"127.0.0.1:6379> georadiusbymember china:city taiyuan 1000 km withcoord withdist count 21) 1) "taiyuan" 2) "0.0000" 3) 1) "112.54999905824661255" 2) "37.86000073876942196"2) 1) "xian" 2) "514.2264" 3) 1) "108.96000176668167114" 2) "34.25999964418929977"

  参数与 georadius 一样

  该命令返回11个字符的hash字符串

  127.0.0.1:6379> geohash china:city taiyuan shenyang1) "ww8p3hhqmp0"2) "wxrvb9qyxk0"

  将二维的经纬度转换为一维的字符串, 如果两个字符串越接近, 则距离越近

  geo底层的实现原理实际上就是Zset, 我们可以通过Zset命令来操作geo

  127.0.0.1:6379> type china:cityzset

  查看全部元素 删除指定的元素

  127.0.0.1:6379> zrange china:city 0 -1 withscores1) "xian"2) "4040115445396757"3) "hangzhou"4) "4054133997236782"5) "manjing"6) "4066006694128997"7) "taiyuan"8) "4068216047500484"9) "shenyang"1) "4072519231994779"2) "shengzhen"3) "4154606886655324"127.0.0.1:6379> zrem china:city manjing(integer) 1127.0.0.1:6379> zrange china:city 0 -11) "xian"2) "hangzhou"3) "taiyuan"4) "shenyang"5) "shengzhen"

  举报/反馈