Loading... ## Redis基础数据结构 在开发中一般都是用 `RedisTemplate/StringRedisTemplate`来操作Redis,两者的主要区别在于使用的**序列化类**不同。前者使用的是 `JdkSerializationRedisSerializer`,存入数据会将数据先序列化成字节数组然后在存入Redis数据库。而后者使用的是 `StringRedisSerializer`。 普遍用到的是 `StringRedisTemplate`,但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。 顺带一提,`redisTemplate`中存取数据都是字节数组。当redis中存入的数据是可读形式而非字节数组时,使用 `redisTemplate`取值的时候会无法获取导出数据,获得的值为 `null`。可以使用 `StringRedisTemplate`试试。 Redis的基础数据结构可以分为 `String`、`List`、`Hash`、`Set`和 `ZSet`五大类。其基本用法不再赘述,熟能生巧罢了。一般通过这两个网站学习:[Redis菜鸟教程](https://www.runoob.com/redis/redis-tutorial.html)、[Redis在线练习](https://try.redis.io/) ### 一、String **数据结构**:类似Java中的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。1M以下采用加倍扩容,1M以上一次扩1M,上限为512M。 **适用场景**:存储Json序列化成字符串的用户信息(每次需要访问大量信息);各种指标统计 **例子**: ```redis > setex name 5 hello # set+expire,5s后过期 > setnx name hello # 不存在则set创建,存在则不执行 > set age 30 # 范围为signed long > incr age # 31 > incrby age 5/-5 # 36/26 ``` ### 二、List **数据结构**:类似Java中的LinkedList链表(`QuickList`),进出分左右边。增删快 `O(1)`,查找慢 `O(n)` **适用场景**:做为异步队列来适用。 **例子**: ```redis > rpush demo a b c > lindex 1 # b O(n)慢操作 > lindex 0 -1 # a b c 获取所有元素 > lrange demo 0 -1 # a b c > ltrim demo 1 2 # b c 只保留[1,2],O(n)慢操作 > ltrim demo 1 0 # 长度为负,清空 ``` ### 三、Hash **数据结构**:无序字典,类似Java中的HashMap。优点在于部分信息读取节省了流量,且采用了渐进式的rehash策略;缺点在于只能存字符串,且hash结构的存储消耗要高于单个字符串。 **适用场景**:只需访问少量字段;自己始终知道哪些字段可用,防止mget时获取不到想要的数据。 同时,官方还建议[尽可能适用hash存储数据](https://redis.io/topics/memory-optimization):小散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。 **例子**: ```redis > hset demo a "is a" > hmset demo b "is b" c "is c" # 批量 set > hgetall demo # entries(),key和value间隔出现 ``` ### 四、Set **数据结构**:无序唯一,类似Java中的HashSet。 **适用场景**:去重 **例子**: ```redis > sadd demo a b c > smembers demo # b a c 无序 > sismember demo a # 1 查询某个 value 是否存在,相当于 contains(o) > scard demo # 3 获取长度相当于 count() > spop demo # b ``` #### Zset **数据结构**:有序列表,类似Java中的SortedSet+HashMap,内部由[跳表](http://www.tangsong.fun/index.php/ConcurrentSkipListMap.html)实现。一方面set保证了value的唯一性,另一方面可以给每个value一个排序权重score。 **适用场景**:存储排序各种排名;**延时队列** **例子**: ```redis > zadd books 9.0 "think in java" (integer) 1 > zadd books 8.9 "java concurrency" (integer) 1 > zadd books 8.6 "java cookbook" (integer) 1 > zrange books 0 -1 # 按 score 排序列出,参数区间为排名范围 1) "java cookbook" 2) "java concurrency" 3) "think in java" > zrevrange books 0 -1 # 按 score 逆序列出,参数区间为排名范围 1) "think in java" 2) "java concurrency" 3) "java cookbook" > zcard books # 相当于 count() (integer) 3 > zscore books "java concurrency" # 获取指定 value 的 score "8.9000000000000004" # 内部 score 使用 double 类型进行存储,所以存在小数点精度问题 > zrank books "java concurrency" # 排名 (integer) 1 > zrangebyscore books 0 8.91 # 根据分值区间遍历 zset 1) "java cookbook" 2) "java concurrency" > zrangebyscore books -inf 8.91 withscores # 根据分值区间 (-∞, 8.91] 遍历 zset,同时返 回分值。inf 代表 infinite,无穷大的意思。 1) "java cookbook" 2) "8.5999999999999996" 3) "java concurrency" 4) "8.9000000000000004" > zrem books "java concurrency" # 删除 value (integer) 1 > zrange books 0 -1 1) "java cookbook" 2) "think in java" ``` ## 容器型数据结构的通用规则 `list`、`hash`、`set`、`zset`这四种数据结构都是容器型数据结构,它们共享下面两条通用规则: 1. `create if not exists` 如果容器不存在,那就创建一个,再进行操作。比如 rpush 操作刚开始是没有列表的,Redis 就会自动创建一个,然后再 rpush 进去新元素。 2. `drop if no elements` 如果容器里元素没有了,那么立即删除元素,释放内存。这意味着 lpop 操作到最后一个元素,列表就消失了。 ## 过期时间 Redis所有数据结构都可以设置过期时间,默认是永不过期。需要注意的是如果字符串设置了过期时间,再调用set去修改它的时候,过期时间就会消失。 Last modification:August 22, 2022 © Allow specification reprint Like 0 喵ฅฅ
2 comments
(学习成本高对卷王来说肯定不值一提啦)
指正一下,RedisTemplate/StringRedisTemplate算是老技术了,只不过因为整合Redis简单,入手快,所以很多项目中一直沿用至今。
经历Jedis、Lettuce的过渡,个人觉得还是使用最新的Redisson更好。
Redisson作为Redis的分布式客户端,同样基于Netty采用异步非阻塞式IO,是线程安全的,优点是提供了很多Redis的分布式操作和高级功能,缺点是API抽象,学习成本高。