目录
简介
Redis由意大利人Salvatore Sanfilippo(网名Antirez)开发,Redis得默认端口是6379,这个端口号也不是随机选的,而是由手机键盘字母“MERZ”的位置决定的;“MERZ”在Antirez 的朋友圈语言中是“愚蠢”的代名词,它由于意大利广告女郎“Alessia Merz”在电视节目上说了一堆愚蠢的话而被人熟知。
Redis应用场景
- 热点数据缓存、对象缓存、全页缓存、热点数据缓存——缓存(string)
- 分布式Session——数据共享分布式(string)
- 分布式锁(string)
- 全局ID(int,incrby)
- 文章的阅读量、微博点赞数——计数器(int, incrby)
- 限流(int,incrby)
- 位统计(string)
- 购物车(string或hash)
- 用户消息时间线timeline(list)
- 消息队列(list)
- 抽奖(自带一个随机获得值:spop myset)
- 点赞、签到、打卡
- 商品标签
- 商品筛选
- 用户关注、推荐模型
- 排行榜
Redis基础数据结构
Redis有5种基础数据结构,分别为: string(字符串)、list(列表)、hash(字典)、set(集合)和zset(有序集合)。
一、string(字符串)
字符串string是Redis最简单的数据结构,它的内部表示就是一个字符数组。Redis所有的数据结构都以唯一的key字符串作为名称,然后通过这个唯一key值来获取相应的value数据。不同类型的数据结构的差异就在于value的结构不一样。
Redis的字符串是动态字符串,是可以修改的字符串,内部结构的实现类似于Java的ArrayList,采用预分配冗余空间的方式减少内存的频繁分配,内部为当前字符串分配的实际空间capacity一般要高于实际字符串长度len。当字符串长度小于1MB时,扩容都是加倍现有的空间。如果字符串长度超过1MB,扩容时一次只会多扩1MB的空间。
注意:
字符串最大长度为512MB。
string类型相关指令
常用指令 | 说明 |
---|---|
get/set/del | 查询/设置/删除 |
set rekey data | 设置已经存在的key,会覆盖 |
setnx rekey data | 设置已经存在的key,不会覆盖 |
set key value ex time | 设置带过期时间的数据 |
expire key | 设置过期时间 |
ttl | 查看剩余时间,-1永不过期,-2过期 |
append key | 合并字符串 |
strien key | 字符串长度 |
incr key | 累加1 |
decr key | 累减1 |
incrby key num | 累加给定数值 |
decrby key num | 累减给定数值 |
getrange key start end | 截取数据,end=-1代表到最后 |
setrange key start newdata | 从start位置开始替换数据 |
mset | 连续设值 |
mget | 连续取值 |
msetnx | 连续设置,如果存在则不设置 |
其他指令 | 说明 |
---|---|
select index | 切换数据库,总共默认16个 |
flushdb | 删除当前下边db中的数据 |
flushall | 删除所有db中的数据 |
二、list(列表)
Redis的列表相当于Java语言里面的LinkedList,注意它时链表而不是数组。这意味着list的插入和删除操作非常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为O(n)。
列表中的每个元素都是用双向指针顺序,串起来可以同时支持前向后向遍历。当列表弹出了最后一个元素之后,该数据结构被自动删除,内存被回收。
Redis的列表结构常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串,塞进Redis的列表,另一个线程从这个列表中轮询数据进行处理。
list类型相关指令
指令 | 说明 |
---|---|
lpush userList 1 2 3 4 5 | 构建一个list,从左边开始存入数据 |
rpush userList 1 2 3 4 5 | 构建一个list,从右边开始存入数据 |
lrange list start end | 获得数据 |
lpop | 从左侧开始拿出一个数据 |
rpop | 从右侧开始拿出一个数据 |
llen list | list长度 |
lindex list index | 获取list下标的值 |
lset list index value | 把某个下标的值替换 |
linsert list before/after value | 插入一个新的值 |
lrem list num value | 删除几个相同数据 |
ltrim list start end | 截取值,替换原来的list |
三、hash(字典)
Redis的字典相当于Java语言里面的HashMap,它是无序字典,内部存储了很多键值对。实现结构上与Java的HashMap也是一样的,都是“数组+链表”二维结构。
第一维hash的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。
不同的是,Redis的字典的值只能是字符串,另外它们rehash的方式不一样,
- Java的HashMap在字典很大时,rehash是个耗时的操作,需要一次性全部rehash。
- Redis为了追求高性能,不能堵塞服务,所以采用了渐进式rehash策略。
渐进式rehash会在rehash的同时,保留新旧两个hash结构,查询时会同时查询两个hash结构,然后在后续的定时任务以及hash操作指令中,循序渐进地将旧hash的内容一点点地迁移到新的hash结构中。当搬迁完成了,就会使用新的hash结构取而代之。
当hash移除了最后一个元素之后,该数据结构被自动删除,内存被回收。
注意:存储对象(不能有嵌套对象)
hash类型相关指令
指令 | 说明 | 举例 |
---|---|---|
hset key property value | 创建hash对象 | hset user name xiaobai 创建一个user对象,这个对象中包含name属性,name值为xiaobai |
hget key property | 获取key对象中的property的值 | hget user name 获取用户对象中的name的值 |
hmset | 设置对象中的多个键值对 | hmset user age 18 phone 123123 |
hmsetnx | 设置对象中的多个键值对,存在则不添加 | hmsetnx user age 18 这种则不会添加 |
hmget | 获得对象中多个属性 | hmget user age phone 获取user对象中的age和phone属性 |
hgetall user | 获取整个对象的内容 | |
hincrby user age 2 | 累加属性 | |
hincrby user age 2.2 | 累加属性 | |
hien user | 有多少个属性 | |
hexists user age | 判断属性是否存在 | |
hkeys user | 获得所有属性 | |
hvals user | 获得所有值 | |
hdel user | 删除对象 |
四、set(集合)
Redis的集合相当于Java语言里面的HashSet,它内部的键值对是无序的、唯一的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值NULL。
当集合中最后一个元素被移除之后,数据结构被自动删除,内存被回收。
set结构可以用来存储在某活动中中奖的用户ID,因为有去重功能,可以保证同一个用户不会中奖两次。
set类型相关指令
指令 | 说明 |
---|---|
sadd set [value……] | 添加(构建)set内容 |
smembers set | 查询set中所有的内容 |
scard set | 查看set中包含的数量 |
sismember set value | 判断值是否存在,1存在 0 不存在 |
srem set value | 删除值 |
spop set | 随机获取值 |
spop set index | 指定index获取值 |
srandmember key [count] | 随机获取count个内容 |
smove source target value | 将source 中的value移动到target中 |
sdiff set1 set2 | 差集 |
sinter set1 set2 | 交集 |
sunion set1 set2 | 并集 |
五、zset(有序列表)
zset可能是Redis提供的最有特色的数据结构,它类似于Java的SortedSet和HashMap的结合体,一方面是一个set,保证了内部value的唯一性,另一方面可以给每个value赋予一个score,代表这个value的排序权重。它的内部实现用的是一种叫作“跳跃列表”的数据结构。
zset中最后一个value被移除后,数据结构被自动删除,内存被回收。
跳跃列表
zset内部的排序功能是通过“跳跃列表”数据结构来实现的,它的结构非常特殊,也比较复杂。
因为zset要支持随机的插入和删除,所以它不宜使用数据来表示。
我们需要这个链表按照score值进行排序。这意味着当有新元素需要插入时,要定位到特定位置的插入点,这样才可以继续保证链表是有序的。通常我们会通过二分查找来找到插入点,但是二分查找的对象必须是数组,只有数组才可以支持快速位置定位,链表做不到,那该怎么办?
跳跃列表就类似于层级制,最下面一层所有的元素都会串起来。然后每隔几个元素挑选出一个代表,再将这几个代表使用另外一级指针串起来。然后在这些代表里再挑出二级代表,再串起来。最终就形成了金字塔结构。
“跳跃列表”之所以“跳跃”,是因为内部的元素可能“身兼数职”,中间的这个元素,同时处于L0、L1和L2层中,可以快速在不同层次之间进行“跳跃”。
跳跃列表采取一个随机策略来决定新元素可以兼职到第几层。
首先其位于 LO 层的概率肯定是 100% ,而兼职到 Ll 层只有 50% 的概率,至IJ L2 层只有 25% 概率,到 L3 层只有 12.5% 的概率,以此类推, 直随机到最顶层 L31 绝大多数元素都过不了几层,只有极少数元素可以深入到顶层。列表中的元素越多, 能够深入的层次就越深,元素能进入到顶层的可能性就会越大。
zset类型相关指令
指令 | 说明 |
---|---|
zadd zset 10 value1 20 value2 30 value3 | 设置memeber和对应的分数 |
zrange zset 0 -1 | 查看所有zset中的内容 |
zrange zset 0 -1 withscores | 查看所有zset中的内容,带有分数 |
zrank zset value | 获取对应的下标 |
zscore zset value | 获得对应的分数 |
zcard zset | 统计个数 |
zrangebyscroe zset 分数1 分数2 | 查询分数之间的member(包含分数1和分数2) |
zrangebyscore zset (分数1 (分数2 | 查询分数之间的member(不包含分数1和分数2) |
zrangebyscore zset 分数1 分数2 limit start end | 查询分数之间的member(包含分数1 分数2),获得的结果集再次根据下标区间做查询 |
zrem zset value | 删除member |
参考文献
Redis常用命令手册http://c.biancheng.net/redis2/zrangebyscore.htmlRedis命令中文翻译版(Redis Command Reference/ Redis Documentation)
http://redisdoc.com/
通用规则
list、set、hash、zset这四种数据结构是容器型数据结构,它们共享下面两条通用规则。
- create if not exists:如果容器不存在,那就创建一个,再进行操作。比如rpush操作刚开始是没有列表的,Redis就会自动创建一个,然后再rpush进去新元素。
- drop if no elements:如果容器里的元素没有了,那么立即删除容器,释放内存。这意味着lpop操作到最后一个元素,列表就消失了。
过期时间
Redis所有的数据结构都可以设置过期时间,时间到了,Redis会自动删除相应的对象。需要注意的是,过期是以对象为单位的,比如一个hash结构的过期是整个hash对象的过期,而不是其中的某个字key的过期。
还有一个需要特别注意的地方,如果一个字符串已经设置了过期时间,然后你调用set方法修改了它,它的过期时间会消失。
作者:筱白爱学习!!
欢迎关注转发评论点赞沟通,您的支持是筱白的动力!