一、Redis数据类型使用场景
1. 两个小细节
(1)命令不区分大小写,而 key
区分大小写。
(2)help @
可以快速查看命令。
1 | 1: 命令不区分大小写,而 `key` 区分大小写 |
2. String(字符串)
2.1. 常用命令
1 | 1: 最常用 |
2.2. 分布式锁
1 | 1: SETNX key value |
EX
:key在多少秒后过期。PX
:key在多少毫秒后过期。NX
:当 key 不存在时,才创建key,效果等同于 SETNX。XX
:当 key 存在的时候,覆盖 key。
2.3. 应用场景
(1)商品编号、订单号采用 INCR 命令生成。
(2)喜欢/踩的人数统计。
3. Hash(散列)
3.1. Hash–>java?
1 | // Redis的Hash对应的就是 Java 中的Map |
3.2. 常用命令
1 | 1:HSET key field value [field value ...] |
3.3. 应用场景
(1)购物车、点餐、送快递 数量统计。

4. List(列表)
4.1. 常用命令
1 | 1: LINDEX key index |
4.2. 应用场景
(1)微信订阅文章推送。
1 | 用户 1024 订阅的公众号, 推送了 35 66 77 号三篇文章 |
5. Set(无序不重复集合)
5.1. 常用命令
1 | Set中的值是无序的并且不能重复的! |
5.2. 应用场景
(1)微信抽奖小程序。
1 | 1: 创建抽奖名字 chouj:10.1 |
(2)微信朋友圈点赞。
1 | 1: 发布文章 id1 id2 id3 id4 id5 五个人点赞 |
(3)微博好友共同关注。
1 | 1: person1 关注了 p2 p3 p4 p5 p6 |
(4)QQ内推可能认识的人。
1 | 1: s1 认识 1 2 3 4 5 |
6. Zset(带分数的set)
6.1. 常用命令
1 | 1: Add one or more members to a sorted set, or update its score if it already exists |
- XX: 仅仅更新存在的成员,不添加新成员。
- NX: 不存在就添加新成员。
- CH: 返回多少个值被修改了。注:
ZADD
默认返回新添加元素的数量。 - INCR: 当
ZADD
指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作。
6.2. 应用场景
(1)抖音、微博热搜。
1 | 1: 两条热搜 |
二、分布式锁
1. 搭建超卖程序
1.1. nginx安装和命令
为了方便测试,该案例使用的是 nginx/windows
。下载地址
注意:
windows/nginx
下载之后是压缩包,无需安装,解压即可使用。- 使用
windows/nginx
需要到安装目录下,打开 cmd 窗口。- 配置文件位置
conf/nginx.conf
。
1 | nginx/window 命令 |
1.2. nginx负载均衡
1 | # nginx.conf 配置文件 |
1.3. 搭建应用
1 | <!-- pom.xml --> |
service
: com.ymy.boot.service.SaleService。controller
: com.ymy.boot.controller.SaleController。
注意:
8081 和 8082 代码完全一样,只是端口不一样, 以上路径只写一次代表两个应用!
1.4. Redis序列化配置
1 | /** |
1.5. 多线程压测工具
2. 分布式锁的演变
2.1. 单机版加锁v1.0
1 | // 单机版我们的业务 |
单机版业务出现的问题?
单机版没有加锁。
多个线程并发的访问同一个资源类,没有任何控制手段不能保证数据的一致性。
那么加锁 synchronized 还是 ReentrantLock?????
1 | // java.util.concurrent.locks.Lock 中有 tryLock() 方法 |
单机版加锁改进代码
1 | // v1.0 单机版没有加锁 synchronized |
2.2. 分布式架构v2.0
1 | public static final String REDIS_LOCK = "REDIS_LOCK"; |
当前问题:
这样设置的 REDIS_LOCK 默认是用不过期的,除非要求 redisTemplate.delete(REDIS_LOCK);
如果当前服务器挂了,那么永远都无法删除 REDIS_LOCK 。
因此,需要为 REDIS_LOCK 设置过期时间。
1 | // 注意: 加锁 和 设置过期时间必须是原子操作! |
2.3. 释放锁(lua)v3.0
redisTemplate.delete(REDIS_LOCK);
只能释放自己的锁,不能动别人的锁!
这里不能直接释放锁。
注意:判断和释放锁也必须是原子操作,不能被打断!
1 | 解决办法: Redis 事务、Watch乐观锁机制。 |
使用 lua 脚本删除Key, 释放锁。
https://redis.io/commands/set。
2.4. Redisson v4.0
如何保证锁的过期时间大于业务的执行时间?
这个过期时间不能写死,需要动态变化的!
Redis 有集群模式,分布式锁加在 Redis master,如果 master 没有同步到 slaver 但是 master挂了?那怎么办?锁就会丢失了!
解决方案:Redisson
1 | <!-- pom 依赖 --> |
1 | // Redisson 配置 |
1 | // Redis 锁 |
三、Redis内存
1. 设置Redis内存
1 | # redis.conf |
如果不设置最大内存大小或者设置最大内存为0, 在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存。
一般生产上如何配置最大内存?
一般推荐Redis设置内存为最大物理内存的四分之三。
1 | 查看 redis 最大内存 |
2. Redis内存打满OOM
3. 内存淘汰策略
3.1. 3种key删除策略
如果一个 key 是过期的,那它到了过期时间之后是不是马上就从内存中删除?
答案是否定的,Redis有三种不同的过期 key 删除策略。
删除策略:
- 立即删除:立即删除能保证内存中数据的最大新鲜度,key 过期后立马会被删除,其所占的内存就会被释放。但是,当CPU忙的时候,就会给CPU造成额外的压力(对CPU不友好,拿时间换空间)。
- 惰性删除:数据到达过期时间,不做处理,等下次访问该数据时,如果未过期,返回数据,发现已经过期,删除,返回不存在(对内存不友好,用空间换时间)。
- 定期删除:定期删除是对以上两种删除策略的折中。定期删除策略每隔一段时间执行一次删除过期 key 操作,并通过限制删除操作执行的时常和频率来减少删除操作对CPU时间的影响(定期抽样key,判断是否过期,但是抽查还是有落网之鱼)!
3.2. Redis内存淘汰策略
1 | # redis.conf |
1 | 命令查看 Redis 内存淘汰策略 |
4. LRU算法
LinkedHashMap复用了HashMap的put()为什么插入后还能保证有序?
4.1. LinkedHashMap
1 | // 测试代码: |
1 | // 输出结果: |
1 | // LinkedHashMap 构造方法 |
4.2. LRUCache
以下是使用 LinkedHashMap 实现的一个 LRU 缓存:
- 设定最大缓存空间 maxEntries;
- 使用 LinkedHashMap 的构造函数将 accessOrder 设置为 true,开启 LRU 顺序;
- 覆盖 removeEldestEntry() 方法实现,在节点多于 MAX_ENTRIES 就会将最近最久未使用的数据移除。
1 | public class LRUCache<K, V> extends LinkedHashMap<K, V> { |
1 | // 输出结果 |