本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Redis - 核心数据结构
五种数据类型
注:Redis是以键值对{key,value}形式储存的
- 对于redis来说,所有的key(键) 都是字符串类型
- value 可以支持上图五种数据类型
字符串String
是redis中最基本的数据类型,一个key对应一个value。
String类型是二进制安全的,意味着 redis的string可以包含任何数据。如数字,字符串,jpg图片或者序列化对象
- 常用命令有:
序号 命令及描述 1 SET key value 设置指定 key 的值。 2 GET key 获取指定 key 的值。 3 SETNX key value 只有在 key 不存在时设置 key 的值。 4 SETEX key seconds value
将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。5 INCR key
将 key 中储存的数字值增一。6 INCRBY key increment 将 key 所储存的值加上给定的增量值(increment) 。 7 DECR key
将 key 中储存的数字值减一。8 DECRBY key decrement
key 所储存的值减去给定的减量值(decrement) 。9 APPEND key value 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。
-
应用场景
-
缓存:经典实用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存,mysql作为持久化层,降低mysql的读写压力;
-
单值缓存
SET Key Value GET Key
-
对象缓存
用法1: SET user:userId value(json格式数据) set user:1 {"name": "zhangsan","age":"18"} 用法2: SET user:userId:属性名 value(属性值) set user:1:name zhangsan get user:1:name 用法3: MSET user:1:name zhangsan user:1:age 18 MGET user:1:name user:1:age
-
-
计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源
命令实现: 127.0.0.1:6379> get counter "2" 127.0.0.1:6379> incr counter (integer) 3 代码实现: String key = "code_" + shopCode; Long codeNum; //key存在则加1,key不存在则创建后加1; 最终返回value codeNum = codeRedisTemplate.opsForValue().increment(key, 1);
-
session:常见方案 spring session + redis实现session共享
-
分布式锁:
SETNX item:10001 true //返回1代表获取锁成功 SETNX item:10001 true //返回0代表获取锁失败 。。。执行业务操作 DEL product:10001 //执行完业务释放锁 SET item:10001 true ex 100 nx //防止程序意外终止导致死锁
-
哈希 hash
是一个Mapmap,指值本身又是一种键值对结构,如 value={{field1,value1},......fieldN,valueN}}
-
使用:所有hash的命令都是 h 开头的 hget 、hset 、 hdel 等
模板: HMSET user {userId}:name zhangsan {userId}:age 18 实战命令: 127.0.0.1:15>HMset user 1:name zhuangsan 1:age 18 "OK" 127.0.0.1:15>hmget user 1:name 1) "zhuangsan"
-
应用场景: 对象缓存 - 电商购物车
-
{ userId , {product , productNum} }
-
以用户id 为 key
-
商品id为 field
-
商品数量为 value
-
购物车操作:
1.添加商品 127.0.0.1:15>hset cart:1001 10088 1 "1" 127.0.0.1:15>hset book:1001 10089 1 "1" 2.增加数量 127.0.0.1:15>hincrby cart:1001 10088 1 "2" 3.商品总数(不是sku) 127.0.0.1:15>hlen cart:1001 "1" 4.删除商品 127.0.0.1:15>hdel cart:1001 10088 "1" 5.获取购物车所有商品 127.0.0.1:15>hgetall book:1001 1) "10089" 2) "1"
-
- Hash结构的优缺点:
- 优点:
- 同类数据归类整合储存,方便数据管理
- 如果存储的都是比较结构化的数据,比如用户数据缓存,或者经常需要操作数据的一个或者几个, 特别是如果一个数据中如果filed比较多,但是每次只需要使用其中的一个或者少数的几个,使用hash是一个好的选择, 因为它提供了hget 和 hmget,而无需取出所有数据再在代码中处理。
- 反之, 如果数据差异较大,操作时常常需要把所有数据都读取出来再处理,使用string 是一个好的选择。
- 相比String操作消耗内存和cpu更小
- 相比String存储更节省空间
- 同类数据归类整合储存,方便数据管理
- 缺点:
- 过期功能不能使用在 field上,只能用在key上
- 在Redis集群架构下,不适合大规模使用
- 优点:
列表list
Redis 使用双端链表实现的List,是有序的,value可以重复,可以通过下标取出对应的value值,左右两边都能进行插入和删除数据。
- List常用操作:
-- 实战:
测试 :15>rpush list no1
"1"
测试 :15>rpush list no2
"2"
测试 :15>lpush list no3
"3"
测试 :15>lpop list
"no3"
测试 :15>rpop list
"no2"
-
列表使用技巧
- 先进后出: lpush + lpop = Stack(栈)
- 先进后出: lpush+rpop=Queue(队列)
- lpush+ltrim = Capped Collection(有限集合)
- lpush + brpop = Blocking MQ(阻塞队列)
-
**List应用场景:**微博消息和微信公众号消息
- 我关注了 大V,大V的新动态,就会推送到我的缓存List中
- 微博、微信公众号都有关注人的时间轴;最新更新的在最前面
- 有人发布微博,用 lpush加入时间轴
- 展示新的列表信息,使用 LRANGE命令
集合set
- 集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中
- 不允许有重复的元素,
- 集合中的元素是无序的,不能通过索引下标获取元素,
- 支持集合间的操作,可以取多个集合取交集、并集、差集。
使用:命令都是以s开头的 sset 、srem、scard、smembers、sismember
测试 :15>SADD SET M1
"1"
测试 :15>SADD SET M1
"0"
测试 :15>SADD SET M2
"1"
测试 :15>SADD SET [1,2,3]
"1"
测试 :15>SADD SET [1,2,3]
"0"
测试 :15>SREM SET M2
"1"
测试 :15>SMEMBERS SET
1) "M1"
2) "[1,2,3]"
测试 :15>SCARD SET
"2"
测试 :15>SISMEMBER SET M2
"0"
测试 :15>SISMEMBER SET M1
"1"
测试 :15>SRANDMEMBER SET 1
1) "[1,2,3]"
测试 :15>SRANDMEMBER SET 1
1) "M1"
测试 :15>SRANDMEMBER SET 1
1) "[1,2,3]"
测试 :15>SRANDMEMBER SET 1
1) "M1"
测试 :15>SPOP SET 1
1) "[1,2,3]"
测试 :15>SMEMBERS SET
1) "M1"
测试 :15>
-
SET 应用场景:
-
微信抽奖小程序
-
-
微信微博点赞,收藏,标签
Set数据结构的集合操作: 交集、并集、差集
测试 :15>SMEMBERS SET
1) "M1"
测试 :15>SADD SET2 M1
"1"
测试 :15>SADD SET2 M2
"1"
测试 :15>SINTER SET SET2
1) "M1"
测试 :15>SUNION SET SET2
1) "M2"
2) "M1"
测试 :15>SDIFF SET2 SET
1) "M2"
重点:集合操作交并差
-
集合操作实现微博微信关注模式
-
集合操作实现电商商品筛选(最好还是用ES做,这里只提供一个redis解决思路)
有序集合 zset
有序集合和集合有着必然的联系,保留了集合不能有重复成员的特性,区别是,有序集合中的元素是可以排序的,它给每个元素设置一个分数,作为排序的依据。
(有序集合中的元素不可以重复,但是score 分数 可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相同)。
使用: 有序集合的命令都是 以 z 开头 zadd 、 zrange、 zscore
测试 :15>zadd myzset 44 m4
"1"
测试 :15>zadd myzset 55 m5
"1"
测试 :15>zadd myzset 66 m6 77 m7
"2"
测试 :15>zrange myzset 0 -1
1) "m4"
2) "m5"
3) "m6"
4) "m7"
5) "m2"
6) "m1"
7) "m3"
测试 :15>zscore myzset m2
"99"
测试 :15>
- 有序集合应用场景:
- 排行榜: 有序集合经典使用场景。
- 例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。
其他高级命令:
keys * :全量遍历键;当redis数据量比较大时,性能比较差,要避免使用;
测试 :15>keys *
1) "SET"
2) "cart:1001"
3) "list"
4) "myzset"
5) "SET2"
6) "user"
7) "book:1001"
scan:渐进式遍历键
todo 单线程与高性能 todo
-
redis 6 以前的版本,严格来说也是多线程, 只不过执行用户命令的请求时单线程模型,还有一些线程用来执行后台任务, 比如 unlink 删除 大key,rdb持久化等。
-
redis 6.0 提供了多线程的读写IO, 但是最终执行用户命令的线程依然是单线程的,这样,就没有多线程数据的竞争关系,依然很高效。