請勿過度依賴 Redis 的過期監(jiān)聽
閱讀本文大概需要 5?分鐘。
來自:juejin.im/post/6844904158227595271
Redis 過期監(jiān)聽場景
測試情況
redis-benchmark -t set -r 100000 -n 1000000?結果如下:\======?SET?======
??1000000?requests?completed?in?171.03?seconds
??50?parallel?clients
??3?bytes?payload
??keep?alive:?1
??host?configuration?"save":?3600?1?300?100?60?10000
??host?configuration?"appendonly":?no
??multi-thread:?no
benchmark?線程不應該在 Docker 容器內部運行 . 跑分的時候大概 benchmark 和 redis 主線程各自持有 50%CPU@Service
@Slf4j
public?class?RedisJob?{
????@Autowired
????private?StringRedisTemplate?stringRedisTemplate;
????public?DateTimeFormatter?dateTimeFormatter?=?DateTimeFormatter.ofPattern("yyyy-MM-dd?HH:mm:ss");
????public?LocalDateTime?end?=?LocalDateTime.of(LocalDate.of(2020,?5,?12),?LocalTime.of(8,?0));
????@Scheduled(cron?=?"0?56?\*?\*?\*??")
????public?void?initKeys()?{
????????LocalDateTime?now?=?LocalDateTime.now();
????????ValueOperations?operations?=?stringRedisTemplate.opsForValue();
????????log.info("開始設置key");
????????LocalDateTime?begin?=?now.withMinute(0).withSecond(0).withNano(0);
????????for?(int?i?=?1;?i?17;?i++)?{
????????????setExpireKey(begin.plusHours(i),?8,?operations);
????????}
????????log.info("設置完畢:?"?+?Duration.between(now,?LocalDateTime.now()));
????}
????private?void?setExpireKey(LocalDateTime?expireTime,?int?step,?ValueOperations?operations) ?{
????????LocalDateTime?localDateTime?=?LocalDateTime.now().withNano(0);
????????String?nowTime?=?dateTimeFormatter.format(localDateTime);
????????while?(expireTime.getMinute()?55)?{
????????????operations.set(nowTime?+?"@"?+?dateTimeFormatter.format(expireTime),?"A",?Duration.between(expireTime,?LocalDateTime.now()).abs());
????????????expireTime?=?expireTime.plusSeconds(step);
????????}
????}
}
@Slf4j
@Component
public?class?RedisKeyExpirationListener?extends?KeyExpirationEventMessageListener?{
????public?RedisKeyExpirationListener(RedisMessageListenerContainer?listenerContainer)?{
????????super(listenerContainer);
????}
????public?DateTimeFormatter?dateTimeFormatter?=?DateTimeFormatter.ofPattern("yyyy-MM-dd?HH:mm:ss");
????@Autowired
????private?StringRedisTemplate?stringRedisTemplate;
????@Override
????public?void?onMessage(Message?message,?byte\[\]?pattern)?{
????????String?keyName?=?new?String(message.getBody());
????????LocalDateTime?parse?=?LocalDateTime.parse(keyName.split("@")\[1\],?dateTimeFormatter);
????????long?seconds?=?Duration.between(parse,?LocalDateTime.now()).getSeconds();
????????stringRedisTemplate.execute((RedisCallback
@Bean
public?RedisMessageListenerContainer?configRedisMessageListenerContainer(RedisConnectionFactory?connectionFactory)?{
????ThreadPoolTaskExecutor?executor?=?new?ThreadPoolTaskExecutor();
????executor.setCorePoolSize(100);
????executor.setMaxPoolSize(100);
????executor.setQueueCapacity(100);
????executor.setKeepAliveSeconds(3600);
????executor.setThreadNamePrefix("redis");
????// rejection-policy:當pool已經(jīng)達到max size的時候,如何處理新任務
????// CALLER\_RUNS:不在新線程中執(zhí)行任務,而是由調用者所在的線程來執(zhí)行
????executor.setRejectedExecutionHandler(new?ThreadPoolExecutor.CallerRunsPolicy());
????executor.initialize();
????RedisMessageListenerContainer?container?=?new?RedisMessageListenerContainer();
????//?設置Redis的連接工廠
????container.setConnectionFactory(connectionFactory);
????//?設置監(jiān)聽使用的線程池
????container.setTaskExecutor(executor);
????//?設置監(jiān)聽的Topic
????return?container;
}
2020-05-13?22:16:48.383??:?過期key:2020-05-13?11:56:02@2020-05-13?22:14:08?,當前size:57405?,滯后時間160
2020-05-13?22:16:49.389??:?過期key:2020-05-13?11:56:02@2020-05-13?22:14:32?,當前size:57404?,滯后時間137
2020-05-13?22:16:49.591??:?過期key:2020-05-13?10:56:02@2020-05-13?22:13:20?,當前size:57403?,滯后時間209
2020-05-13?22:16:50.093??:?過期key:2020-05-13?20:56:00@2020-05-13?22:12:32?,當前size:57402?,滯后時間258
2020-05-13?22:16:50.596??:?過期key:2020-05-13?07:56:03@2020-05-13?22:13:28?,當前size:57401?,滯后時間202
2020-05-13?22:16:50.697??:?過期key:2020-05-13?20:56:00@2020-05-13?22:14:32?,當前size:57400?,滯后時間138
2020-05-13?22:16:50.999??:?過期key:2020-05-13?19:56:00@2020-05-13?22:13:44?,當前size:57399?,滯后時間186
2020-05-13?22:16:51.199??:?過期key:2020-05-13?20:56:00@2020-05-13?22:14:40?,當前size:57398?,滯后時間131
2020-05-13?22:16:52.205??:?過期key:2020-05-13?15:56:01@2020-05-13?22:16:24?,當前size:57397?,滯后時間28
2020-05-13?22:16:52.808??:?過期key:2020-05-13?06:56:03@2020-05-13?22:15:04?,當前size:57396?,滯后時間108
2020-05-13?22:16:53.009??:?過期key:2020-05-13?06:56:03@2020-05-13?22:16:40?,當前size:57395?,滯后時間13
2020-05-13?22:16:53.110??:?過期key:2020-05-13?20:56:00@2020-05-13?22:14:56?,當前size:57394?,滯后時間117
2020-05-13?22:16:53.211??:?過期key:2020-05-13?06:56:03@2020-05-13?22:13:44?,當前size:57393?,滯后時間189
2020-05-13?22:16:53.613??:?過期key:2020-05-13?15:56:01@2020-05-13?22:12:24?,當前size:57392?,滯后時間269
2020-05-13?22:16:54.317??:?過期key:2020-05-13?15:56:01@2020-05-13?22:16:00?,當前size:57391?,滯后時間54
2020-05-13?22:16:54.517??:?過期key:2020-05-13?18:56:00@2020-05-13?22:15:44?,當前size:57390?,滯后時間70
2020-05-13?22:16:54.618??:?過期key:2020-05-13?21:56:00@2020-05-13?22:14:24?,當前size:57389?,滯后時間150
2020-05-13?22:16:54.819??:?過期key:2020-05-13?17:56:00@2020-05-13?22:14:40?,當前size:57388?,滯后時間134
2020-05-13?22:16:55.322??:?過期key:2020-05-13?10:56:02@2020-05-13?22:13:52?,當前size:57387?,滯后時間183
2020-05-13?22:16:55.423??:?過期key:2020-05-13?07:56:03@2020-05-13?22:14:16?,當前size:57386?,滯后時間159
總結
How Redis expires keys:https://redis.io/commands/expire#how-redis-expires-keys Timing of expired events:https://redis.io/topics/notifications#timing-of-expired-events
expired?events?are generated when the Redis server deletes the key?and not when the time to live theoretically reaches the value of zero.", 這兩個文章讀下來你會感覺 , 臥槽 Redis 的過期策略其實也挺'Low'的。推薦閱讀:
微信掃描二維碼,關注我的公眾號
朕已閱?

