02.Redis_数据类型
Redis_数据类型
Redis支持5种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)以及HyperLogLog、Geo、Pub/Sub。
数据类型及应用场景
1)String
常用命令
a)set
- NX:当数据库中key不存在时,可以将key-value添加数据库
- XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥
- EX:key的超时秒数
- PX:key的超时毫秒数,与EX互斥
b)get
c)append
d)strlen
e)setnx
f)incr
- 将 key 中储存的数字值增1,只能对数字值操作,如果为空,新增值为1
g)decr
- 将 key 中储存的数字值减1,只能对数字值操作,如果为空,新增值为-1
h)incrby / decrby
- 自定义步长。
Redis 中的 String 不仅仅表示 字符串,还可以表示 整型、浮点型。
String 的编码可以是 int、raw 或者 embstr;单说普通的字符串,就有 raw 和 embstr 两种实现方式,embstr 是 Redis 3.0 新增的数据结构:
字符串长度小于 39 字节,就用 embstr 对象,否则用传统的raw对象(Redis 3.2版本之后,这里变成了以 44 字节为分界)。
embstr 的优势在于创建时少分配一次空间(RedisObject 和 sds 是连续的),删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便;
当然缺点也非常明显,如果字符串的长度增加,需要重新分配内存的时候,整个 RedisObject 和 sds 都需要重新分配空间。
修改 embstr 对象的时候,Redis 会将其转换成 raw 格式再进行修改,所以 embstr 对象修改之后的对象,一定是 raw 的。
应用场景:
1)缓存功能。Redis 作为缓存层,MySQL 作为存储层,首先从 Redis 获取数据,如果失败就从 MySQL 获取并将结果写回 Redis 并添加过期时间。2)计数。Redis可以实现快速计数功能,例如视频每播放一次就用 incy 把播放数加 1。3)共享Session。一个分布式 Web 服务将用户的 Session 信息保存在各自服务器,但会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问负载到不同服务器上,用户刷新一次可能会发现需要重新登陆。为解决该问题,可以使用 Redis 将用户的 Session 进行集中管理,在这种模式下只要保证 Redis 是高可用和扩展性的,每次用户更新或查询登录信息都直接从 Redis 集中获取。4)限速。例如为了短信接口不被频繁访问会限制用户每分钟获取验证码的次数或者网站限制一个 IP 地址不能在一秒内访问超过 n次。可以使用键过期策略和自增计数实现。
2)Hash
常用方法:
设置值
hset key field value,如果设置成功会返回 1,反之会返回 0,此外还提供了 hsetnx 命令,作用和 setnx 类似,只是作用于由键变为 field。
获取值
hget key field,如果不存在会返回 nil。
删除 field
hdel key field [field...],会删除一个或多个 field,返回结果为删除成功 field 的个数。
计算 field 个数
hlen key
批量设置或获取 field-value
hmget key field [field...]
hmset key field value [field value...]
判断 field 是否存在
hexists key field,存在返回 1,否则返回 0。
获取所有的 field
hkeys key,返回指定哈希键的所有 field。
获取所有 value
hvals key,获取指定键的所有 value。
获取所有的 field-value
hgetall key,获取指定键的所有 field-value。
Hash 对象的底层实现可以是 ziplist 或者 hashtable。
ziplist:在这个数据结构中,是按照 key1, value1, key2, value2 这样的顺序存放来存储的;
hashTable:是由 dict 这个结构来实现的。(这个结构比较复杂,后面单写一篇来说)
Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
应用场景:缓存用户信息,每个用户属性使用一对 field-value,但只用一个键保存。
优点:简单直观,如果合理使用可以减少内存空间使用。
缺点:要控制哈希在 ziplist 和 hashtable 两种内部编码的转换,hashtable 会消耗更多内存。
3)List
常用命令
添加
从右边插入元素:rpush key value [value...]
从左到右获取列表的所有元素:lrange 0 -1
从左边插入元素:lpush key value [value...]
向某个元素前或者后插入元素:linsert key before|after pivot value,会在列表中找到等于 pivot 的元素,在其前或后插入一个新的元素 value。
查找
获取指定范围内的元素列表:lrange key start end,索引从左到右的范围是 0~N-1,从右到左是 -1~-N,lrange 中的 end 包含了自身。
获取列表指定索引下标的元素:lindex key index,获取最后一个元素可以使用 lindex key -1。
获取列表长度:llen key
删除
从列表左侧弹出元素:lpop key
从列表右侧弹出元素:rpop key
删除指定元素:lrem key count value,如果 count 大于 0,从左到右删除最多 count 个元素,如果 count 小于 0,从右到左删除最多个 count 绝对值个元素,如果 count 等于 0,删除所有。
按照索引范围修剪列表:ltrim key start end,只会保留 start ~ end 范围的元素。
修改
修改指定索引下标的元素:lset key index newValue。
阻塞操作
阻塞式弹出:blpop/brpop key [key...] timeout,timeout 表示阻塞时间。
当列表为空时,如果 timeout = 0,客户端会一直阻塞,如果在此期间添加了元素,客户端会立即返回。
如果是多个键,那么brpop会从左至右遍历键,一旦有一个键能弹出元素,客户端立即返回。
如果多个客户端对同一个键执行 brpop,那么最先执行该命令的客户端可以获取弹出的值。
-
ziplist 压缩列表:跟哈希的 zipilist 相同,元素个数和大小小于配置值(默认 512 个和 64 字节)时使用。
-
linkedlist 链表:当列表类型无法满足 ziplist 的条件时会使用linkedlist。
-
Redis 3.2 提供了 quicklist 内部编码,它是以一个 ziplist 为节点的 linkedlist,它结合了两者的优势,为列表类提供了一种更为优秀的内部编码实现。
应用场景:1)消息队列。Redis 的 lpush + brpop 即可实现阻塞队列,生产者客户端使用 lpush 从列表左侧插入元素,多个消费者客户端使用 brpop 命令阻塞式地抢列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。2)文章列表。每个用户有属于自己的文章列表,现在需要分页展示文章列表,就可以考虑使用列表。因为列表不但有序,同时支持按照索引范围获取元素。每篇文章使用哈希结构存储。lpush + lpop = 栈、lpush + rpop = 队列、lpush + ltrim = 优先集合、lpush + brpop = 消息队列。
通常网站上的消息列表,可以使用 List 来进行存储;另外 lrange 命令,从某个元素开始,读取多少个元素,可以看做是分页查询,比如很多网站上那种不断下拉,不断分页的效果。
4)Set
常用方法
添加元素
sadd key element [element...],返回结果为添加成功的元素个数。
删除元素
srem key element [element...],返回结果为成功删除的元素个数。
计算元素个数
scard key,时间复杂度为 O(1),会直接使用 Redis 内部的遍历。
判断元素是否在集合中
sismember key element,如果存在返回 1,否则返回 0。
随机从集合返回指定个数个元素
srandmember key [count],如果不指定 count 默认为 1。
从集合随机弹出元素
spop key,可以从集合中随机弹出一个元素。
获取所有元素
smembers key
求多个集合的交集/并集/差集
sinter key [key...]
sunion key [key...]
sdiff key [key...]
保存交集、并集、差集的结果
sinterstore/sunionstore/sdiffstore destination key [key...]
集合间运算在元素较多情况下比较耗时,Redis 提供这三个指令将集合间交集、并集、差集的结果保存在 destination key 中。
Set数据结构是dict字典,字典是用哈希表实现的。
intset 整数集合:当集合中的元素个数小于配置值(默认 512 个时),使用 intset。
hashtable 哈希表:当集合类型无法满足 intset 条件时使用 hashtable。当某个元素不为整数时,也会使用 hashtable。
intset:是一个整数集合,支持三种长度的整数:int16_t、int32_t、int64_t;集合中的数据长度必须是一致的,比如一个 int16_t 长度的 Set,当插入了一条 int32_t 长度的数据,那么所有的数据都会转成 int32_t 长度(不支持降级)。
hashTable:对于 Set 来说,hashTable 的 value 永远为 NULL。
应用场景:1)标签。set 比较典型的使用场景是标签,例如一个用户可能与娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。这些数据对于用户体验以及增强用户黏度比较重要。sadd = 标签、spop/srandmember = 生成随机数,比如抽奖、sadd + sinter = 社交需求。
如果要存储一个列表,同时又需要做数据排重的时候,可以使用 set ;另外,Redis 还为 Set 提供了求交集、并集、差集等操作,比如微博上面的【共同关注】这个功能,就可以用 Set 实现。
5)ZSet / Sorted Set
常用方法
- zadd
…将一个或多个 member 元素及其 score 值加入到有序集 key 当中。 - zrange
[WITHSCORES] 返回有序集 key 中,下标在 之间的元素,带WITHSCORES,可以让分数一起和值返回到结果集。 - zrangebyscore key minmax [withscores] [limit offset count]返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max)的成员。有序集成员按 score 值递增(从小到大)次序排列。
- zrevrangebyscore key maxmin [withscores] [limit offset count] 同上,改为从大到小排列。
- zincrby
为元素的score加上增量 - zrem
删除该集合下,指定值的元素 - zcount
统计该集合,分数区间内的元素个数 - zrank
返回该值在集合中的排名,从0开始。
和 Set 相比,ZSet 增加了一个参数 score,集合中的元素按照 score 进行有序排列。
有序集合的编码可能两种,一种是 ziplist,另一种是 skipList 与 hashTable 的结合。
ziplist:和 Hash 类似,元素 和 score 都是按顺序存放的;比较适合用于元素内容不大的场景。
skipList + hashTable:是一种添加,移除,更新元素等操作更高效的数据结构,这个跳跃表的数据结构,我近期会发一篇文章单独介绍。
应用场景:有序集合的典型使用场景就是排行榜系统,例如用户上传了一个视频并获得了赞,可以使用 zadd 和 zincrby。如果需要将用户从榜单删除,可以使用 zrem。如果要展示获取赞数最多的十个用户,可以使用 zrange。
6)Bitmaps
常用方法:
a)setbit
b)getbit
c)bitcount
d)bitop and(or/not/xor)
使用场景:用户周活跃、统计活跃用户、用户在线状态、用户签到
7)HyperLogLog
常用方法:
a) PFADD key element [element ...]
b)PFCOUNT key [key ...]
c)PFMERGE destkey sourcekey [sourcekey ...]
使用场景:统计APP或网页的一个页面,每天有多少用户点击进入的次数。同一个用户的反复点击进入记为 1 次。
8)Geospatial
常用方法:
a) geoadd
b)geopos
c)geodist
d) georadius
e) georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
f) geohash:返回一个或多个位置对象的 geohash 值。
应用场景:自如、蛋壳、链家、还有美团都有根据距离找房源或者商铺的功能,都是用的空间索引。但是RedisGEO 适合精度不是很高的场景,微信附近的人用Redis GeoHash比较合适。但是滴滴打车精度高的场景一般用的是Google S2.
首先上传房源和店铺的时候,通过高德地图可以获取到目标物的经纬度,然后上传到Redis中。查询的时候根据自身所在位置的经纬度,然后加上范围,就能查询到附近的房源和店铺列表名称和经纬度,放到高德地图中,就能完美展现出列表来。
浙公网安备 33010602011771号