简介
Redis是一个使用 C语言编写的开源的、支持网络的、基于内存的、可选持久化的高性能键值对数据库,读写速度非常的快,也正因为它运行在内存中,其存储量有限而且分布式集群成本非常的高
西西里岛的意大利人 Salvatore Sanfilippo(Github:antirez)在07年和朋友创建了网站 LLOOGG.com,很快对 Mysql 的性能感到失望,于是在09年开发了Redis数据库
↓↓↓↓↓↓↓↓↓↓↓↓↓照片有劳动改造的感觉 ↓↓↓↓↓↓↓↓↓↓↓↓↓
优秀的开源项目离不开大公司的支持,在2013年5月之前,其开发由VMware赞助,而2013年5月至2015年6月期间,其开发由毕威拓赞助,从2015年6月开始,Redis的开发由Redis Labs赞助
Redis 支持的数据结构
Redis支持的常用5种数据类型指的是value类型,分别为:字符串String、列表List、哈希Hash、集合Set、有序集合ZSet,但是Redis后续又丰富了几种数据类型分别是Bitmaps、HyperLogLogs、GEO
由于Redis是基于标准C写的,只有最基础的数据类型,因此Redis为了满足对外使用的5种数据类型,开发了属于自己独有的一套基础数据结构,使用这些数据结构来实现5种数据类型。
Redis底层的数据结构包括:简单动态数组SDS、链表、字典、跳跃链表、整数集合、压缩列表、对象
下图可以看到对外数据结构对应的底层数据结构实现
下图表述了 Redis 支持的五种数据类型,对于Redis 来说,所有的 Key 都是字符串
常见数据结构与应用场景
Redis 常见应用场景:缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用) 分布式集群架构中的session分离。 聊天室的在线好友列表。 任务队列。(秒杀、抢购、12306等等) 应用排行榜。 网站访问统计。 数据过期处理(可以精确到毫秒)
String
String 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 简单动态字符串(simple dynamic string,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出
应用场景: 一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等
常用命令:
set key value:设置 key-value 类型的值del key:删除对应的keyget key:根据 key 获取 value,如果不存在会返回 nullstrlen key:返回 key 对应的 value 字符串的长度exists key:判断某个 key 是否存在incr key:自增 value,第一次执行 value 为 1,每执行一次 value + 1decr key:自减,与 incr 作用相反,0的时候执行会变成负数setex key seconds value:设置一个有过期时间的 key-value 类型的值,如setex key 60 value,该键值对将在60s后过期expire key seconds:设置已存在的键值对的过期时间mset key1 value1 key2 value2/ mget key1 key2: 批量设置 / 获取 key-value 类型的值
List
List 即是 链表。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 LinkedList,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
应用场景: 发布与订阅或者说消息队列、慢查询,可以用来当做简单的消息队列实现不过肯定比不过专业的中间件
常用命令:
lpush / rpush key value1 value2:向 list 头部(最左边)/ list 尾部(最右边)添加元素lpop / rpop key:取出 list 头部(最左边)/ list 尾部(最右边)的值lrange key start end:查看列表中对应下标 start end 的值,end 为 -1 就是倒数第一llen key:查看链表长度
Hash
hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。不过,Redis 的 hash 做了更多优化。另外,hash 是一个 string 类型的 field和 value 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值,比如我们可以 hash 数据结构来存储用户信息,商品信息等等
应用场景: 系统中对象数据的存储
常用命令:
hset object key value:设置一个名为object对象,并赋值一个属性,key是属性名,value 是属性值hexists object key:查看对象中的属性是否存在hget object key:获取对象的指定属性的属性值hgetall object:获取对象的所有属性和属性值hkeys object:查询对象有多少个属性hvals object:查询对象有多少个属性值
Set
Set 类似于 Java 中的 HashSet 。Redis 中的 set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的
**应用场景:**可以基于 set 轻易实现交集、并集、差集的操作。比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程
常用命令:
sadd myset value1 value2 value3:向名为 myset 的集合中添加三个值,如果值已存在,会添加失败,返回0smembers myset:查看 myset 中有多少值sismembers myset value1:检查 value1 是否存在于 myset 中scard myset:产看 myset 的长度sinterstore myset3 myset1 myset2: 将 myset1 和 myset2 的交集取出,存放到 myset3 中
ZSet
和 set 相比,zset 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
**应用场景:**需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息
常用命令:
zadd myzset 3.0 value:添加元素到 myzset 中,权重设置为 3.0,值为 value,也可以添加多个不同权重的值zcard myzset:查看 myzset 中有多少元素zscore myzset value: 查看 value 在 myzset 中的权重zrange myzset 0 -1:遍历 myzset 中的元素,从 0 到第 N 位,-1 代表遍历所有zrevrange myzset 0 -1:逆序输出,从 0 到第 N 位,-1 代表遍历所有
BitMap
bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间
**应用场景:**适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)
常用命令:
-
setbit mykey 7 1:对 mykey 所存储的字符串值,设置或清除指定偏移量上的位(bit),是将字符串 mykey 位图上第七位设为 1- 返回值为该位在setbit之前的值
- value只能取0或1
- offset从0开始,即使原位图只能10位,offset可以取1000
-
getbit mykey 7:获取 mykey 指定偏移量上的位 -
bitcount mykey:统计被设置为 1 的位的数量 -
bitop operation dest key1 key2 key3:做多个 bitmap 的and | or | not | xor的操作结果并保存到 dest 中
缓存过期机制
由于 Redis 是运行在内存中的,内存资源是有限的,如果每条缓存都不设置过期时间,容易导致内存被冲烂
Redis 可以给缓存设置过期时间,例:
> set name 狗蛋
OK
> expire name 60 #狗蛋将在60S后过期,只对存在的key生效
1
> setex name 狗蛋 60 # 创建一个过期时间为 60S 的狗蛋
OK
> ttl name #查看狗蛋还能活多久
52
如何判断数据是否过期
Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)
过期数据的删除策略
常用的过期数据删除策略有两个,Redis 采用了 惰性删除 + 定期删除
- 惰性删除:在取出 Key 的时候对 Key 进行检查,确认 Key 是否过期
- 定期删除:每隔一段时间抽取一批 Key 执行删除过期 Key 的操作
内存淘汰策略
光是删除过期数据不一定能阻止内存溢出,因为数据还在不停的增加,这个时候就要引入内存淘汰机制了
Redis 提供 8 种数据淘汰策略:
noeviction:默认策略,当内存达到设置的最大值时,所有申请内存的操作都会报错,只读操作正常执行volatile-lru:设置了过期时间的key使用LRU算法淘汰allkeys-lru:所有key使用LRU算法淘汰volatile-lfu:设置了过期时间的key使用LFU算法淘汰allkeys-lfu:所有key使用LFU算法淘汰volatile-random:设置了过期时间的key使用随机淘汰allkeys-random:所有key使用随机淘汰volatile-ttl:设置了过期时间的key根据过期时间淘汰,越早过期越早淘汰
LRU算法:挑选出最近最少使用的 key
LFU算法:挑选出最不经常使用的 key
对于 Root 用户来说,可以使用以下命令来更改 Redis 的内存淘汰策略:
> config set maxmemory-policy allkeys-random
OK
> config get maxmemory-policy
maxmemory-policy
allkeys-random