(转载+整理)Redis底层设计与5大数据类型详析

【本文转载一名老师的整理,分享下大家一起学习进步~】

非常感谢这位老师,由于学习这个redis基础,我的****已经上涨。


什么是Redis

Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的高性能非关系型(NoSQL)的键值对数据库

与传统数据库不同的是 Redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向,每秒可以处理超过 10万次读写操作

Redis 是K-V型的数据库,整个数据库都是用字典来存储的,对Redis数据库的任何增删改查操作,实际上就是对字典中的数据进行增删改查

1. 可以存储海量数据,且可以根据键以O(1) 的时间复杂度取出或插入关联值

2. 键值对中键的类型可以是字符串,整型,浮点型等,且键是唯一的.

3. 键值对中的值类型可以是string, hash ,list, set, sorted set.

Redis的应用场景

(转载+整理)Redis底层设计与5大数据类型详析

计数器 

可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。

分布式ID生成

利用自增特性,一次请求一个大一点的步长如 incr 2000 ,缓存在本地使用,用完再请求。

海量数据统计

位图(bitmap):存储是否参过某次活动,是否已读谋篇文章,用户是否为会员, 日活统计。

会话缓存

可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。

分布式队列/阻塞队列

List 是一个双向链表,可以通过 lpush/rpush 和 rpop/lpop 写入和读取消息。可以通过使用brpop/blpop 来实现阻塞队列。

分布式锁实现

在分布式场景下,无法使用基于进程的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁。

热点数据存储

最新评论,最新文章列表,使用list 存储,ltrim取出热点数据,删除老数据。

社交类需求

Set 可以实现交集,从而实现共同好友等功能,Set通过求差集,可以进行好友推荐,文章推荐。

排行榜

ZSet 可以实现有序性操作,从而实现排行榜等功能。

延迟队列

使用sorted_set,使用 【当前时间戳 + 需要延迟的时长】做score, 消息内容作为元素,调用zadd来生产消息,消费者使用zrangbyscore获取当前时间之前的数据做轮询处理。消费完再删除任务 rem key member。

Redis 安装

1. Docker 安装

 

#拉取 redis 镜像
$ docker pull redis
#运行 redis 容器
$ docker run --name myredis -d -p6379:6379 redis
#执行容器中的 redis-cli,可以直接使用命令行操作 redis
$docker exec -it myredis redis-cli

2. 源码安装

 

#拉取 redis 镜像
$ docker pull redis
#运行 redis 容器
$ docker run --name myredis -d -p6379:6379 redis
#执行容器中的 redis-cli,可以直接使用命令行操作 redis
$docker exec -it myredis redis-cli

快速入门

1. string 字符串

string是Redis 使用最广泛,也是最简单的数据结构,Redis所有的key也是string类型,业务系统中通常会把业务数据序列化成一个json 字符串,然后存储到Redis中缓存起来,下次访问的时候,再取出来,反序列化供业务端使用。

(转载+整理)Redis底层设计与5大数据类型详析

可通过 help 查看字符串操作命令

127.0.0.1:6379> help @string since: 2.6.0

常用命令

K-V 缓存:

指定key, value 完成设置于取值操作:

127.0.0.1:6379> set  name guojia
OK
127.0.0.1:6379> get name
"guojia"
127.0.0.1:6379> set  some_key some_value
OK
127.0.0.1:6379> get   some_key
"some_value"

如上操作,至少进行了两次网络请求,网络的利用率不高,如果有多个键值对,可以用如下优化

127.0.0.1:6379> mset name1 zhangsan name2 lisi name3 wangwu
OK
127.0.0.1:6379> mget name1 name2 name3
1) "zhangsan"
2) "lisi"
3) "wangwu"

默认情况下,没有给key设置过期时间,如果配置了持久化的话,数据将永久存在,可以通过设置过期时间,

# 10s 后, name1 将被失效 127.0.0.1:6379> expire name1 10 (integer) 1

数值计算:

127.0.0.1:6379> set readCount 1
OK
127.0.0.1:6379> incr readCount
(integer) 2
127.0.0.1:6379> decr readCount
(integer) 1
127.0.0.1:6379> incrby readCount 10
(integer) 11
127.0.0.1:6379> decrby readCount 5
(integer) 6

位操作(BitMap)

bitmap通常被用来在极小空间消耗下通过位的运算(AND/OR/XOR/NOT)实现对状态的判断、统计,常见的使用场景例如:

1.通过bitmap来记录用户每天应用登录状态,例如用户登录,就SETBIT login:20200514 uid 1,表示用户uid 在20200514这一天登录了,通过BITCOUNT login:20200514可以得到这一天所有登录过的用户数量;通过对两天的记录求AND,可以判断哪个用户连续两天登录了;通过对两天的记录求OR ,可以判断用户两天内登录至少登录了一次的情况。

2.判断用户是否为VIP,用户是否阅读了谋篇文章,观看了某个视频等。

3.连续登陆加积分

4. 指定时间窗口登陆算活跃数

*还可以对String数据类型做部分运算,如修改其中的某一部分,从字符串尾部添加。

2. LIST 列表

操作列表命令查看

$ help @list

Redis的列表数据结构常用来做异步队列使用。一个线程将任务入队到列表中,另一个线程从这个队列轮询数据进行处理。

1. LIST 的操作很多都以 L 或者 R 开头,分别代表压入或弹出数据的方向,L代表左边,R代表右边, 意味着Redis的LIST 数据类型为一个双端链表, 队头和队尾都可以执行数据的压入和弹出。对同一端执行压入和弹出可以很容易实现一个栈的数据结构,对一端执行压入,另一端执行弹出实现的是队列的数据结构。

2. Redis提供了类似数组通过下标取数据的命令 LINDEX, 由于Redis LIST 数据类型底层采用链表实现,时间复杂度为 O(n),在数据量比较大的场景,不建议使用

3. 使用 blpop 实现阻塞队列

127.0.0.1:6379> rpush list1 1 (integer) 1 127.0.0.1:6379> blpop list1 5 # 队列中有值,立即返回 1) "list1" 2) "1" 127.0.0.1:6379> blpop list1 5# 队列中没有数据, 阻塞等待5s 钟,

另开一个窗口

再5s 内,往队列中添加一个数据,阻塞队列被唤醒

127.0.0.1:6379> rpush list1 2 (integer) 1

127.0.0.1:6379> blpop list1 5 1) "list1" 2) "2"

4. LIST 还提供了 LTRIM 的命令,用来截取指定数据段的数据,指定数据段范围外的数据将被删除。通常可以用来做最新数据的获取,如最新文章,最新帖子

3.HASH字典

操作hash命令查看

127.0.0.1:6379> help @hash

Redis的HASH数据结构使用链表来解决HASH 冲突,是一种 数组+ 链表的结构,Redis HASH数据结构的值只能是字符串,当HASH扩容 rehash时,Redis为了提升性能使用的时渐进性 rehash策略。

rehash 会在rehash的同时,保留两个新旧的hash结构,查询时会同时查询两个hash结构,然后再后续的定时任务中以及hash的子指令中,循序渐进地将旧的内容逐步迁移到新得hash结构中。

HASH 通常用于聚合数据,如商品详情页, 数据来源可能多样, 为了减少复杂查询带来的性能损耗,通常直接把第一次查询的结果聚合到HASH中,提升查询速度,保护db,同时减少了分散多个key,对外层key的消耗【 外层key过多,容易导致频繁的rehash,消耗较大的内存】。

主要操作和string比较类似,需要注意的时 hash不能对field 设置过期时间

4.SET 集合

操作set命令查看

127.0.0.1:6379> help @set

1. 这是一个无序且唯一的集合类型数据结构,自带去重功能,可以用来存储具有唯一约束的数据。

 

127.0.0.1:6379> SADD some_key a a b b c d e    // 往 some_key 中添加元素
(integer) 5
127.0.0.1:6379> SMEMBERS some_key //查询集合中所有元素,无序的数据,且自动去重
1) "c"
2) "b"
3) "a"
4) "d"
5) "e"
127.0.0.1:6379> SRANDMEMBER some_key 3  // 随机选择 3 个元素
1) "c"
2) "a"
3) "b"
127.0.0.1:6379> SRANDMEMBER some_key 3 // 随机选择 3 个元素
1) "b"
2) "d"
3) "e"
127.0.0.1:6379> SRANDMEMBER some_key 3 // 随机选择 3 个元素
1) "b"
2) "d"
3) "e"
127.0.0.1:6379> 

2. 可以对两个集合执行 并集/交集/差集 操作。

127.0.0.1:6379> SADD some_set1 a b c d 
(integer) 4
127.0.0.1:6379> SADD some_set2 c d e f 
(integer) 4
127.0.0.1:6379> SUNION some_set1 some_set2   // 并集
1) "c"
2) "f"
3) "a"
4) "b"
5) "d"
6) "e"
127.0.0.1:6379> SINTER some_set1 some_set2  // 交集
1) "c"
2) "d"
127.0.0.1:6379> SDIFF some_set1 some_set2   // 差集, 差集有顺序要求,SDIFF 
1) "b"                                      // 第一个参数为基准,找出在第一个  
2) "a"                                      // 集合中的元素没有在后面集合中
127.0.0.1:6379>                             // 出现过的元素 

 SDIFF some_set1 some_set2 // 差集, 差集有顺序要求,SDIFF 1) "b" // 第一个参数为基准,找出在第一个 2) "a" // 集合中的元素没有在后面集合中 127.0.0.1:6379> // 出现过的元素

5.sorted_set/ zset 有序集合

操作zset 命令查看

127.0.0.1:6379> help @sorted_set

ZSET是个有序,且唯一的集合数据类型。可以用来做排行榜,具有时序需求的场景,如基于zset实现一个延迟队列

延迟队列应用场景

如下单后,30分钟内未付款就自动取消订单; 支付后,24小时未评论自动好评

实现逻辑:

1. 生产者生成 延迟消息 序列化后 放入 zset 中

zadd delay_queue now_time_stamp + 5 min task1 zadd delay_queue now_time_stamp + 5 min task2 zadd delay_queue now_time_stamp + 5 min task3

2.用多个线程轮询 zset 获取到期的任务进行处理。

ZRANGEBYSCORE delay_queue 0 now_time_stamp withscores limit 0 1

3. 处理完消息,删除延迟消息

ZREM delay_queue task1

排行榜

127.0.0.1:6379> zadd rank_board 100 a  101 b 99 c 
(integer) 3
127.0.0.1:6379> ZRANGE  rank_board 0 -1 
1) "c"
2) "a"
3) "b"
127.0.0.1:6379> ZRANGE  rank_board 0 -1  withscores
1) "c"
2) "99"
3) "a"
4) "100"
5) "b"
6) "101"
127.0.0.1:6379> ZINCRBY rank_board  1 a     // 如投票. 给指定元素做数值运算
"101"
127.0.0.1:6379> ZRANGE  rank_board 0 -1  withscores
1) "c"
2) "99"
3) "a"
4) "101"
5) "b"
6) "101"
127.0.0.1:6379> ZINCRBY rank_board  1 a 
"102"
127.0.0.1:6379> ZRANGE  rank_board 0 -1  withscores
1) "c"
2) "99"
3) "b"
4) "101"
5) "a"
6) "102"

 

上一篇:[ARC111D] Orientation


下一篇:剑10 - 剑15 精听记录