Redis底层与5大数据类型
Posted 写程序的小王叔叔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis底层与5大数据类型相关的知识,希望对你有一定的参考价值。
主页:写程序的小王叔叔的博客欢迎来访
支持:点赞收藏关注
什么是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的应用场景
计数器
可以对 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中缓存起来,下次访问的时候,再取出来,反序列化供业务端使用。
可通过 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"
⚠️注意 ~
以上是关于Redis底层与5大数据类型的主要内容,如果未能解决你的问题,请参考以下文章
Redis_17_Redis服务器中的数据库(五种基本类型底层存放)