0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Redis 过期监听怎么实现的

马哥Linux运维 来源:Hollis 作者:Hollis 2022-07-01 11:14 次阅读

目录

前言

Redis 过期监听

RabbitMQ 死信

时间轮

结论

前言

日前拜读阿牛老师的大作《领导:谁再用定时任务实现关闭订单,立马滚蛋!》发现其方案有若干瑕疵,特此抛砖引玉讨论一二。

在电商、支付等领域,往往会有这样的场景,用户下单后放弃支付了,那这笔订单会在指定的时间段后进行关闭操作。

细心的你一定发现了像某宝、某东都有这样的逻辑,而且时间很准确,误差在 1s 内,那他们是怎么实现的呢?

一般实现的方法有几种:

使用 RocketMQ、RabbitMQ、Pulsar 等消息队列的延时投递功能

使用 Redisson 提供的 DelayedQueue

有一些方案虽然广为流传但存在着致命缺陷,不要用来实现延时任务:

使用 Redis 的过期监听

使用 RabbitMQ的死信队列

使用非持久化的时间轮

Redis 过期监听

在 Redis 官方手册的 keyspace-notifications: timing-of-expired-events 中明确指出:

Basically 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 自动过期的实现方式是:定时任务离线扫描并删除部分过期键;在访问键时惰性检查是否过期并删除过期键。

Redis 从未保证会在设定的过期时间立即删除并发送过期通知。实际上,过期通知晚于设定的过期时间数分钟的情况也比较常见。

此外键空间通知采用的是发送即忘(fire and forget)策略,并不像消息队列一样保证送达。当订阅事件的客户端会丢失所有在断线期间所有分发给它的事件。

这是一种比定时扫描数据库更 “LOW” 的解决方案,请不要使用。

RabbitMQ 死信

死信(Dead Letter)是 RabbitMQ 提供的一种机制。

当一条消息满足下列条件之一那么它会成为死信:

消息被否定确认(如 channel.basicNack)并且此时 requeue 属性被设置为 false。

消息在队列的存活时间超过设置的 TTL 时间

消息队列的消息数量已经超过最大队列长度

若配置了死信队列,死信会被 RabbitMQ 投到死信队列中。

在 RabbitMQ 中创建死信队列的操作流程大概是:

创建一个交换机作为死信交换机

在业务队列中配置 x-dead-letter-exchange 和 x-dead-letter-routing-key,将第一步的交换机设为业务队列的死信交换机

在死信交换机上创建队列,并监听此队列

死信队列的设计目的是为了存储没有被正常消费的消息,便于排查和重新投递。死信队列同样也没有对投递时间做出保证,在第一条消息成为死信之前,后面的消息即使过期也不会投递为死信。

为了解决这个问题,Rabbit 官方推出了延迟投递插件 rabbitmq-delayed-message-exchange ,推荐使用官方插件来做延时消息。

这里说点题外话,使用 Redis 过期监听或者 RabbitMQ 死信队列做延时任务都是以设计者预想之外的方式使用中间件,这种出其不意必自毙的行为通常会存在某些隐患,比如缺乏一致性和可靠性保证,吞吐量较低、资源泄漏等。

比较出名的一个事例是很多人使用 Redis 的 List 作为消息队列,以致于最后作者看不下去写了 Disque 并最后演变为 Redis Stream。工作中还是尽量不要滥用中间件,用专业的组件做专业的事。

时间轮

时间轮是一种很优秀的定时任务的数据结构,然而绝大多数时间轮实现是纯内存没有持久化的。

运行时间轮的进程崩溃之后其中所有的任务都会灰飞烟灭,所以奉劝各位勇士谨慎使用。

| Redisson DelayQueue

Redisson DelayQueue 是一种基于 Redis Zset 结构的延时队列实现。DelayQueue 中有一个名为 timeoutSetName 的有序集合,其中元素的 score 为投递时间戳。

DelayQueue 会定时使用 zrangebyscore 扫描已到投递时间的消息,然后把它们移动到就绪消息列表中。

DelayQueue 保证 Redis 不崩溃的情况下不会丢失消息,在没有更好的解决方案时不妨一试。

在数据库索引设计良好的情况下,定时扫描数据库中未完成的订单产生的开销并没有想象中那么大。

在使用 Redisson DelayQueue 等定时任务中间件时可以同时使用扫描数据库的方法作为补偿机制,避免中间件故障造成任务丢失。

结论

总结了几点如下:

首先推荐使用 RocketMQ、Pulsar 等拥有定时投递功能的消息队列。

在不方便获得专业消息队列时可以考虑使用 Redisson DelayQueue 等基于 Redis 的延时队列方案,但要为 Redis 崩溃等情况设计补偿保护机制。

在无法使用 Redisson DelayQueue 等方案时可以考虑使用时间轮。由于时间轮重启远比 Redis 重启要频繁,定时扫库等保护机制更为重要。

永远不要使用 Redis 过期监听实现定时任务。

原文标题:永远不要使用Redis过期监听实现定时任务!

文章出处:【微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

审核编辑:彭静

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储
    +关注

    关注

    13

    文章

    4245

    浏览量

    85624
  • 数据库
    +关注

    关注

    7

    文章

    3754

    浏览量

    64255
  • Redis
    +关注

    关注

    0

    文章

    371

    浏览量

    10834

原文标题:永远不要使用Redis过期监听实现定时任务!

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Redis开源版与Redis企业版,怎么选用?

    点击“蓝字”关注我们数以千计的企业和数以百万计的开发人员Redis开源版来构建应用程序。但随着用户数量、数据量和地区性的增加,成本、可扩展性、运营和可用性等问题也随之而来。Redis企业版
    的头像 发表于 04-04 08:04 931次阅读
    <b class='flag-5'>Redis</b>开源版与<b class='flag-5'>Redis</b>企业版,怎么选用?

    Redis实现分布式多规则限流的方式介绍

    市面上很多介绍 Redis 如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流,比如 1 分钟访问 1 次或者 60 分钟访问 10 次这种,但是如果想一个接口两种规则都需要满足呢,我们的项目又是分布式项目,应该如何
    的头像 发表于 02-26 10:07 436次阅读
    <b class='flag-5'>Redis</b><b class='flag-5'>实现</b>分布式多规则限流的方式介绍

    Redis可以实现消息中间件MQ的功能

    是一种通信模式:发送者(PUBLISH)发送消息,订阅者(SUBSCRIBE)接收消息,可以实现进程间的消息传递   Redis可以实现消息中间件MQ的功能,通过发布订阅实现消息
    的头像 发表于 01-25 14:48 869次阅读
    <b class='flag-5'>Redis</b>可以<b class='flag-5'>实现</b>消息中间件MQ的功能

    Redis的LRU实现和应用

    在编程中,计数器是一种基本但强大的工具,用于跟踪和管理数据和资源。本文将深入探讨不同类型的计数器的应用,从Redis的LRU(最近最少使用)缓存淘汰算法的实现,到如何在内存受限的环境中有效地使用计数器,再到普通计数器的巧妙应用。
    的头像 发表于 12-15 09:24 564次阅读

    redis数据结构的底层实现

    Redis是一种内存键值数据库,常用于缓存、消息队列、实时数据分析等场景。它的高性能得益于其精心设计的数据结构和底层实现。本文将详细介绍Redis常用的数据结构和它们的底层实现
    的头像 发表于 12-05 10:14 583次阅读

    redis数据会自动清除吗

    Redis数据不会自动清除,但它提供了一些机制来管理数据的过期和淘汰。在默认情况下,Redis只会在内存中存储数据,并且没有自动清除机制。 Redis数据持久化和
    的头像 发表于 12-05 10:13 2240次阅读

    redis容器内怎么查看redis日志

    redis是一款流行的开源内存数据库,常用于缓存、消息队列、任务管理等场景。在使用redis时,了解如何查看redis日志对于排查问题、监控性能和分析应用程序行为非常重要。在本文中,我们将介绍在
    的头像 发表于 12-05 10:10 3430次阅读

    redis持久化机制和如何实现持久化

    Redis是一款高性能的非关系型数据库,其持久化机制是保证数据在重启后仍能够保存的关键。Redis提供了两种方式来实现持久化:RDB(Redis DataBase)和AOF(Appen
    的头像 发表于 12-05 10:02 435次阅读

    redis hash底层实现原理

    数据结构是如何实现的呢?本文将详细介绍Redis哈希底层的实现原理。 在Redis中,每个哈希都是由一个类似于字典(Dictionary)的结构实现
    的头像 发表于 12-04 16:27 554次阅读

    如何实现Redis分布式锁

    机制,下面将详细介绍如何实现Redis分布式锁。 一、引言 在分布式系统中,多个节点可能同时读写同一共享资源。如果没有实现互斥访问和同步机制,就会产生数据不一致和竞态条件等问题。解决这个问题的一种方法是使用分布式锁,在访问共享
    的头像 发表于 12-04 11:24 654次阅读

    java redis锁处理并发代码

    问题。 本文将详细介绍如何在Java代码中使用Redis实现并发代码的锁处理。我们将分为以下几个方面来讨论: Redis分布式锁的原理 Redis分布式锁的
    的头像 发表于 12-04 11:04 911次阅读

    Java redis锁怎么实现

    在Java中实现Redis锁涉及到以下几个方面:Redis的安装配置、Redis连接池的使用、Redis数据结构的选择、
    的头像 发表于 12-04 10:47 1102次阅读

    Redis工具集的实现和使用

    Redis 基本上是互联网公司必备的工具了,Redis的应用场景实在太多了,但是有很多相似的功能如果每个项目都要实现一遍就显得太麻烦了,所以为了方便,我打算开发一个基于 Redis
    的头像 发表于 12-03 17:32 1182次阅读
    <b class='flag-5'>Redis</b>工具集的<b class='flag-5'>实现</b>和使用

    Redis的分页+多条件模糊查询组合实现方案

    Redis是key-value类型的内存数据库,通过key直接取数据虽然很方便,但是并未提供像mysql那样方便的sql条件查询支持。因此我们需要借助Redis提供的结构和功能去自己实现模糊条件查询功能。
    的头像 发表于 11-20 14:26 861次阅读
    <b class='flag-5'>Redis</b>的分页+多条件模糊查询组合<b class='flag-5'>实现</b>方案

    使用Redis的有序集合实现排行榜功能和Spring Boot集成

    Redis 的有序集合(Sorted Set)是一个基于分数(score)排序的数据结构,它在 Redis 中非常重要,常用于实现排行榜、近似计数器等功能。
    的头像 发表于 11-20 10:15 720次阅读