《Redis内存淘汰策略及分布式锁应用详解》
Redis内存淘汰策略及分布式锁应用详解
Redis内存淘汰策略
淘汰策略类型
Redis提供了多种内存淘汰策略,用于管理当内存使用达到上限时如何处理数据。以下是六种常见的淘汰策略:
- noeviction:当内存使用达到阈值时,所有导致内存增加的命令将返回错误。
- allkeys-lru:从所有键中优先移除最近最少使用的键。(推荐)
- volatile-lru:仅从设置了过期时间的键中优先移除最近最少使用的键。
- allkeys-random:从所有键中随机移除键。
- volatile-random:仅从设置了过期时间的键中随机移除键。
- volatile-ttl:仅从设置了过期时间的键中移除具有更早过期时间的键。
配置方法
要配置Redis的内存淘汰策略,首先需要在redis.conf
文件中设置最大内存限制,例如:
maxmemory 300mb
然后,指定内存淘汰策略:
maxmemory-policy allkeys-lru
Redis中的自动过期机制与订单过期自动取消
自动过期机制
Redis允许为键设置一个生存时间(TTL),一旦键过期,它会被自动删除。这一特性可以用来实现诸如订单过期自动取消的功能。
实现方案
- 使用Redis Key自动过期触发事件通知:配置Redis以通知过期事件。
- 使用定时任务检查:设定一个定时任务定期检查过期订单。
- 按分钟轮询检查:每隔一段时间轮询检查是否有订单过期。
示例:Spring Boot整合Redis键过期监听
假设有一个订单表order_number
,当订单30分钟内未支付时,应自动将订单状态更改为已取消。
表结构
CREATE TABLE `order_number` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_name` varchar(255) DEFAULT NULL,
`order_status` int(11) DEFAULT NULL,
`order_token` varchar(255) DEFAULT NULL,
`order_id` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;
Redis配置
在redis.conf
中启用键过期通知:
notify-keyspace-events "Ex"
Spring Boot配置
@Configuration
public class RedisListenerConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
private static final Integer ORDER_STAYPAY = 0;
private static final Integer ORDER_INVALID = 2;
@Autowired
private OrderMapper orderMapper;
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = message.toString();
OrderEntity order = orderMapper.getOrderNumber(expiredKey);
if (order != null && order.getOrderStatus().equals(ORDER_STAYPAY)) {
orderMapper.updateOrderStatus(expiredKey, ORDER_INVALID);
}
}
}
控制器
@RestController
public class OrderController {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RedisUtils redisUtils;
@RequestMapping("/saveOrder")
public String saveOrder() {
String orderToken = UUID.randomUUID().toString();
String orderId = System.currentTimeMillis() + "";
redisUtils.setString(orderToken, orderId, 30L); // 30分钟
OrderEntity orderEntity = new OrderEntity(null, "蚂蚁课堂永久会员", orderId, orderToken);
int result = orderMapper.insertOrder(orderEntity);
return result > 0 ? "success" : "fail";
}
}
Redis事务操作
Redis事务通过MULTI
、EXEC
、WATCH
和DISCARD
命令来实现。事务确保一系列命令要么全部执行,要么都不执行,但Redis并不支持事务回滚。
事务与回滚的区别
- 取消事务:不提交事务,事务中的所有操作都不会被执行。
- 回滚:事务中的所有操作被撤销,恢复到事务开始前的状态。Redis不支持此功能。
Redis实现分布式锁
分布式锁原理
分布式锁是一种在分布式系统中实现互斥访问共享资源的机制。Redis通过SETNX
命令实现分布式锁,该命令只有在键不存在时才会设置键。
核心代码示例
public class RedisUtil {
private static String IP = "192.168.212.148";
private static int PORT = 6379;
private static int MAX_ACTIVE = 100;
private static int MAX_IDLE = 20;
private static int MAX_WAIT = 3000;
private static int TIMEOUT = 3000;
private static boolean TEST_ON_BORROW = true;
private static boolean TEST_ON_RETURN = true;
private static JedisPool jedisPool = null;
public final static int EXRP_HOUR = 60 * 60;
public final static int EXRP_DAY = 60 * 60 * 24;
public final static int EXRP_MONTH = 60 * 60 * 24 * 30;
private static void initialPool() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, IP, PORT, TIMEOUT, "123456");
}
private static synchronized void poolInit() {
if (jedisPool == null) {
initialPool();
}
}
public synchronized static Jedis getJedis() {
if (jedisPool == null) {
poolInit();
}
return jedisPool.getResource();
}
public static void returnResource(Jedis jedis) {
if (jedis != null && jedisPool != null) {
jedisPool.returnResource(jedis);
}
}
public static Long sadd(String key, String... members) {
Jedis jedis = null;
Long res = null;
try {
jedis = getJedis();
res = jedis.sadd(key, members);
} catch (Exception e) {
e.printStackTrace();
} finally {
returnResource(jedis);
}
return res;
}
}
应用场景
- 分布式任务调度:确保同一任务不会被重复执行。
- 分布式全局ID生成:生成唯一标识符。