作者:幻好
起源:恒生 LIGHT 云社区
根本简介
Redis 是什么?
- Redis 是互联网技术畛域应用最为宽泛的存储中间件,它是「Remote Dictionary Service」的首字母缩写,也就是「近程字典服务」。Redis 以其超高的性能、完满的文档、简洁易懂的源码和丰盛的客户端库反对在开源中间件畛域广受好评。
- 国内外很多大型互联网 公司都在应用 Redis,比方 Twitter、YouPorn、暴雪娱乐、Github、StackOverflow、腾讯、阿里、京东、华为、新浪微博等等,很多中小型公司也都有利用。也能够说,对 Redis 的了 解和利用实际已成为当下中高级后端开发者绕不开的必备技能。
Redis 能够用来做什么?
- 「Redis 是一个开源(BSD 许可)的,内存中的数据结构存储系统,它能够用作 数据库 、 缓存 和消息中间件。
- 它 反对多种类型的数据结构,如 字符串(strings),散列(hashes),列表(lists),汇合(sets),有序汇合(sorted sets)与范畴查问,bitmaps,hyperloglogs 和 天文空间(geospatial)索引半径查问。Redis 内置了 复制(replication),LUA 脚本(Lua scripting),LRU 驱动事件(LRU eviction),事务(transactions)和不同级别的 磁盘长久化(persistence),并通过 Redis 哨兵(Sentinel)和主动 分区(Cluster)提供高可用性(high availability)。
Redis 能够做什么?
-
Redis 的业务利用范畴十分宽泛,以博客社区的功能模块为实例:
- 记录帖子的 点赞数 、 评论数 和点击数 (hash)。
- 记录用户的帖子 ID列表 (排序),便于疾速显示用户的帖子列表 (zset)。
- 记录帖子的 题目、摘要、作者和封面 信息,用于列表页展现 (hash)。
- 记录帖子的点赞用户 ID 列表,评论 ID 列表,用于 显示和去重计数 (zset)。
- 缓存 近期热帖 内容 (帖子内容空间占用比拟大),缩小数据库压力 (hash)。
Redis 的下载安装
- 参考网站:http://www.redis.cn/download.html
根底数据结构
Redis 有 5 种根底数据结构,别离为:string (字符串)、list (列表)、hash (哈 希)、set (汇合) 和 zset (有序汇合)。熟练掌握这 5 种根本数据结构的应用是 Redis 常识最根底也最重要 的局部。
字符串(string)
-string 的基本概念
-
字符串 string 是 Redis 最简略的数据结构。Redis 所有的数据结构都是以惟一的 key 字符串作为名称,而后通过这个惟一 key 值来获取相应的 value 数据。不同类型的数据结 构的差别就在于 value 的构造不一样。
-string 的利用
- 字符串构造应用十分宽泛,一个常见的用处就是缓存用户信息。咱们将用户信息结构体应用 JSON 序列化成字符串,而后将序列化后的字符串塞进 Redis 来缓存。同样,取用户信息会通过一次反序列化的过程。
-string 的构造原理
-
Redis 的字符串是动静字符串,是能够批改的字符串,内部结构实现上相似于 Java 的
ArrayList
,采纳预调配冗余空间的形式 来缩小内存的频繁调配。 - 如图中所示,外部为以后字 符串理论调配的空间
capacity
个别要高于理论字符串长度len
。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。须要留神的是 字符串最大长度 为512M。 - 字符串是由多个字节组成,每个字节又是由 8 个 bit 组成,如此便能够将一个字符串看 成很多 bit 的组合,这便是 bitmap「位图」数据结构。
-string 的操作命令
- 根本命令:
set
、get
、exists
、del
- 批量命令:
mset
、mget
-
其余命令:
expire
、setex
、setnx
、incr
、incrby
- 如果 value 值是一个整数,能够对它进行自增操作。自增是有范畴的,它的范畴是
signed long
的最大最小值,超过了这个值,Redis 会报错。
- 如果 value 值是一个整数,能够对它进行自增操作。自增是有范畴的,它的范畴是
列表(list)
-list 的基本概念
- Redis 的列表相当于 Java 语言外面的
LinkedList
,留神它是链表而不是数组。这意味着list
的插入和删除操作十分快,工夫复杂度为 O(1),然而索引定位很慢,工夫复杂度为 O(n)。 - 当列表弹出了最初一个元素之后,该数据结构主动被删除,内存被回收。
-list 的利用
- Redis 的列表构造罕用来做异步队列应用。将须要延后解决的工作构造体序列化成字符串塞进 Redis 的列表,另一个线程从这个列表中轮询数据进行解决。
-list 的构造原理
-
如果再深刻一点,会发现 Redis 的列表底层存储的还不是一个简略的
linkedlist
,而是称之为 疾速链表quicklist
的一个构造。 - 首先在 列表元素较少的状况 下会应用一块间断的内存存储,这个构造是
ziplist
,也即是 压缩列表。它将所有的元素紧挨着一起存储,调配的是一块间断的内存。 - 当 列表元素比拟多 时候会改成
quicklist
。因为一般的链表须要的附加指针空间太大,会比拟节约空间,而且会减轻内存的碎片化。比方这个列表里存的只是int
类型的数据,构造上还须要两个额定的指针prev
和next
。所以 Redis 将链表和ziplist
联合起来组成了quicklist
。也就是将多个ziplist
应用双向指针串起来应用。这样既满足了疾速的插入删除性能,又不会呈现太大的空间冗余。
-list 的操作命令
- 常用命令:
rpush
、lpush
、rpop
、lpop
- 查问命令:
llen
、lrange
、lindex
、ltirm
哈希 / 字典(hash)
-hash 的基本概念
- Redis 的字典相当于 Java 语言外面的
HashMap
,它是无序字典。外部实现构造上同 Java 的HashMap
也是统一的,同样的数组 + 链表二维构造。
-hash 的构造原理
-
第一维
hash
的数组地位碰撞时,就会将碰撞的元素应用链表串连接起来。 -
不同的是,Redis 的字典的值只能是字符串,另外它们
rehash
的形式不一样,因为 Java 的HashMap
在字典很大时,rehash
是个耗时的操作,须要一次性全副rehash
。Redis 为了高性能,不能梗塞服务,所以采纳了渐进式rehash
策略。 - 渐进式
rehash
会在rehash
的同时,保留新旧两个hash
构造,查问时会同时查问两个hash
构造,而后在后续的定时工作中以及hash
的子指令中,循序渐进地将旧hash
的内容 一点点迁徙到新的hash
构造中。 - 当
hash
移除了最初一个元素之后,该数据结构主动被删除,内存被回收。 hash
也有毛病,hash
构造的存储耗费要高于单个字符串,到底该应用hash
还是字符 串,须要依据理论状况再三衡量。
-hash 的利用
hash
构造也能够用来存储用户信息,不同于字符串一次性须要全副序列化整个对象,hash
能够对用户构造中的每个字段独自存储。- 当咱们须要获取用户信息时能够进行局部获取。而以整个字符串的模式去保留用户信息的话就只能一次性全副读取,这样就会比拟节约网络流量。
-hash 的操作命令
- 常用命令:
hset
、hget
、hgetall
、hlen
、hmet
- 其余命令:
hincr
、hincrbu
汇合(set)
-set 的基本概念
- Redis 的汇合相当于 Java 语言外面的
HashSet
,它外部的键值对是无序的惟一的。它的外部实现相当于一个非凡的字典,字典中所有的value
都是一个值NULL
。 - 当汇合中最初一个元素移除之后,数据结构主动删除,内存被回收。
-set 的利用
set
构造能够用来存储流动中奖的用户 ID,因为有去重性能,能够保障同一个用户不会中奖两次。
-set 的操作命令
- 常用命令:
sadd
、smembers
、sismember
、scard
、spop
-set 的构造原理
- 汇合对象 set 是 string 类型(整数也会转换成 string 类型进行存储)的无序汇合。留神汇合和列表的区别:汇合中的元素是无序的,因而不能通过索引来操作元素;汇合中的元素不能有反复。
-
编码
- 汇合对象的编码能够是
intset
或者hashtable
。 intset
编码的汇合对象应用整数汇合作为底层实现,汇合对象蕴含的所有元素都被保留在整数汇合中。- hashtable 编码的汇合对象应用 字典作为底层实现,字典的每个键都是一个字符串对象,这里的每个字符串对象就是一个汇合中的元素,而字典的值则全副设置为 null。这里能够类比 Java 汇合中 HashSet 汇合的实现. HashSet 汇合是由 HashMap 来实现的,汇合中的元素就是 HashMap 的 key,而 HashMap 的值都设为 null。
-
编码转换
-
当汇合同时满足以下两个条件时,应用 intset 编码:
- 汇合对象中所有元素都是整数
- 汇合对象所有元素数量不超过 512
- 不能满足这两个条件的就应用
hashtable
编码。第二个条件能够通过配置文件的set-max-intset-entries
进行配置。
-
- 汇合对象的编码能够是
有序汇合(zset)
-zset 的基本概念
zset
可能是 Redis 提供的最为特色的数据结构,它也是在面试中面试官最爱问的数据结 构。- 它相似于 Java 的
SortedSet
和HashMap
的结合体,一方面它是一个set
,保障了外部 value 的唯一性,另一方面它能够给每个 value 赋予一个score
,代表这个value
的排序权 重。它的外部实现用的是一种叫着「 跳跃列表」的数据结构。 zset
中最初一个value
被移除后,数据结构主动删除,内存被回收。
-zset 的利用
zset
能够用来存 粉丝列表,value
值是粉丝的用户 ID,score
是关注工夫。咱们能够对粉丝列表按关注工夫进行排序。zset
还能够用来存储学生的问题,value
值是学生的 ID,score
是他的考试成绩。咱们 能够对问题按分数进行排序就能够失去他的名次。
-zset 的操作命令
- 常用命令:
zadd
、zrange
、zrevrange
、zcard
、zscore
、zrank
、zrangebyscore
-zset 的构造原理
- 有序汇合对象是有序的。与列表应用索引下标作为排序根据不同,有序汇合为每个元素设置一个分数(score)作为排序根据。
-
编码
- 有序汇合的编码能够是
ziplist
或者skiplist
。 ziplist
编码的有序汇合对象应用压缩列表作为底层实现,每个汇合元素应用两个紧挨在一起的压缩列表节点来保留,第一个节点保留元素的成员,第二个节点保留元素的分值。- 并且压缩列表内的汇合元素按分值从小到大的程序进行排列,小的搁置在凑近表头的地位,大的搁置在凑近表尾的地位。
skiplist
编码的有序汇合对象应用 zset 构造作为底层实现,一个 zset 构造同时蕴含一个字典和一个跳跃表:- 字典的键保留元素的值,字典的值则保留元素的分值;跳跃表节点的 object 属性保留元素的成员,跳跃表节点的 score 属性保留元素的分值。
- 这两种数据结构会通过指针来共享雷同元素的成员和分值,所以不会产生反复成员和分值,造成内存的节约。
- 有序汇合的编码能够是
-
编码转换
-
当有序汇合对象同时满足以下两个条件时,对象应用
ziplist
编码:- 保留的元素数量小于 128;
- 保留的所有元素长度都小于 64 字节;
- 不能满足下面两个条件的应用
skiplist
编码。 - 以上两个条件能够通过 Redis 配置文件
zset-max-ziplist-entries
选项和zset-max-ziplist-value
进行批改。
-
容器型数据结构
通用规定
list/set/hash/zset
这四种数据结构是容器型数据结构,它们共享上面两条通用规定:
-
create if not exists
- 如果容器不存在,那就创立一个,再进行操作。比方 rpush 操作刚开始是没有列表的,Redis 就会主动创立一个,而后再 rpush 进去新元素
-
drop if no elements
- 如果容器里元素没有了,那么立刻删除元素,开释内存。这意味着 lpop 操作到最初一 个元素,列表就隐没了。
过期工夫
- Redis 所有的数据结构都能够设置过期工夫,工夫到了,Redis 会主动删除相应的对象。
- 须要留神的是过期是以对象为单位,比方一个
hash
构造的过期是整个hash
对象的过期,而不是其中的某个子key
。 - 特地留神的中央是如果一个字符串曾经设置了过期工夫,而后你调用了
set
办法批改了它,它的过期工夫会隐没。