SpringBoot 架构下如何保证对 redis 的操作是在事务提交之后? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Jonz
V2EX    Java

SpringBoot 架构下如何保证对 redis 的操作是在事务提交之后?

  •  
  •   Jonz 2020-06-23 18:57:28 +08:00 4582 次点击
    这是一个创建于 1982 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前提: 1.目前我们的项目内是配置了全局事务,通过方法的命名前缀来控制,如 insert/update 开头的都是 required 方式传播这样。

    2.对 Redis 的操作是将 jedis 封装了一层 Util 工具类。

    3.目前的写法都是把 redis 操作放在方法的最后执行来尽量规避报错导致事务回滚,缓存插入脏数据。

    伪代码:

    public void foo() { this.doSomething(); redisUtil.set(key, val); throw new RuntimeException(); }

    PS: 今天试着把 redis 的操作放在监听器里,但是发现对 list 类型的操作无法生效,求解大家的处理方案。

    第 1 条附言    2020-06-24 08:25:37 +08:00
    补充几点:
    1. 我们项目内对事务的控制是类似这个文章里的 [三、使用 AOP 全局处理事务] ,正文里描述的全局事务控制可能理解有偏差。https://www.cnblogs.com/wjqhuaxia/p/12148865.html

    2. 其实整体的项目是 SpringCloudAlibaba 那一套,所以下一阶段会引入 seata 做分布式事务控制,不知道到时候对 redis 的操作要如何优化?

    3. 直接用 jedis 属于偷懒了,之前项目有封装好的 redisUtil 就搬运过来了。
    20 条回复    2022-03-30 23:52:19 +08:00
    BBCCBB
        1
    BBCCBB  
       2020-06-23 18:58:56 +08:00
    TransactionSynchronizationManager 可以.
    BBCCBB
        2
    BBCCBB  
       2020-06-23 18:59:18 +08:00
    你百度一下的话肯定会有的.
    Jonz
        3
    Jonz  
    OP
       2020-06-23 20:14:38 +08:00
    @BBCCBB 可以用,感谢
    hantsy
        4
    hantsy  
       2020-06-23 20:52:30 +08:00
    我个人建议好好去读一下 Java EE 官方教程的有关 Transaction,Open XA 等文档,再回过头来看看 Spring 文档中的 Transaction 那一篇。

    从 V 的站一些帖子看,很多人用了很多年的 Spring, 根本就不理解 Spring 中的事务。默认情况下,Spring 的事务只启用 Local Transaction 。如果要同时管理 Redis 和数据库,必须启用 JTA ( Global transaction ),目前我记得 Spring 中的 Redis,Mongo(启用 Cluster 时) 仅支持 JTA (部分标准,Spring 中仅仅将它们支持事务是作了一种转换和映射,底层和 Java EE 世界的 Transaction 不能同等),关系型数据库可以用 JTA 和 Local Transaction 。在使用 Local transaction 时,Spring 做很多扩展。但是使用 JTA,一般依赖第三方 JTA Manager (比如 atomikos, bitronix, JBoss narayana ),Spring transaction Manager 都是 Delegate 到相应的 JTA Manager 上去。
    hantsy
        5
    hantsy  
       2020-06-23 20:54:22 +08:00
    那么问题来了,你说的全局事务是指 Global transaction 吗?你配置那个 JTA 方案?
    BBCCBB
        6
    BBCCBB  
       2020-06-23 21:11:13 +08:00
    不客气, 建议你们用 spring data redis 哈, 直接用 jedis 有点危险.
    jimrok
        7
    jimrok  
       2020-06-23 21:17:45 +08:00
    跨多个资源建立一个事务的,需要你做补偿。例如买了机票,酒店订不上,只能退机票。你要写代码来做反向的补偿操作,达到最终的一致性。XA 这种东西只是个理论,很多场景下建立不了这样的应用。
    jorneyr
        8
    jorneyr  
       2020-06-23 22:36:30 +08:00
    this.doSomething()

    可以用注解,使用 @Around 的 AOP,在 doSomething() 后再仔细 Redis 操作。
    wdlth
        9
    wdlth  
       2020-06-23 23:26:06 +08:00
    你们不用事务管理器的么?
    Jonz
        10
    Jonz  
    OP
       2020-06-24 08:28:13 +08:00
    @hantsy 补充了一些说明,可能我对全局事务的理解有点偏差。
    Jonz
        11
    Jonz  
    OP
       2020-06-24 08:30:02 +08:00
    @wdlth 用的,补充了一些说明,感谢回复
    hantsy
        12
    hantsy  
       2020-06-24 08:46:46 +08:00
    @jimrok Java EE 对于 OpenXA 有自己的一套接口封装,必须是实现 XAResource 那一套(一般需要在驱动层面实现,Java EE 标准下的 JDBC,JMS,服务器中额外的 Connectors,Resource Adapters 等),才可以实现 2 Phase Commit,实现多资源情况下,事务管理上的 ACID 。
    hantsy
        13
    hantsy  
       2020-06-24 08:52:31 +08:00
    @jimrok 补尝方案也就是通常说的 Compensation,是 Microservice 通用一措施。但是,可以说和我们日常说的事务没太大关系。
    ljzxloaf
        14
    ljzxloaf  
       2020-06-24 13:59:44 +08:00
    redis 事务?闻所未闻
    分布式事务一般有三种方式:消息、tcc 、XA,实用性依次递减
    yiyi11
        15
    yiyi11  
       2020-06-24 14:42:48 +08:00 via Android
    经典双写一致性。
    这是参考方案之一。
    1.写请求只删除缓存,不更新缓存。写请求先删除缓存,再更新数据。无论成功与否,写请求只会导致缓存失效,永不导致脏数据。

    2.读请求更新缓存。读写请求高并发时,有可能导致写请求删除缓存,读请求紧接着更新了旧数据缓存,写请求再完成了数据更新,产生双写不一致。

    3.读写串行化,适用于读多写少的场景。缓存有效时,读请求按一般流程处理。写请求发生时,缓存失效,同一 id 的并发的写请求和读请求进入同一队列(或同一加锁处理逻辑),保证双写一致性。串行化时,可能会导致大量并发的读请求超时,所以要在数据实时性(准确)还是系统可用性做下取舍。
    jimrok
        16
    jimrok  
       2020-06-24 17:08:00 +08:00
    @hantsy XA 这个东西设想的挺好了,05 年的时候,我还把整个协议的标准打印出来学习了一遍,但十几年过去了,没有成为主流的分布式事务技术,局限性太大了。
    hantsy
        17
    hantsy  
       2020-06-24 18:18:30 +08:00
    @jimrok 传统应用服务器的 JTA 的实现一个基础是 XA 标准,比如 Jdbc Driver 的 DataSource 一般都是提供两个版本,XA 版本专门用于 JTA 环境。如果自己从无到有去实现一套 XA Resource,再参与 JTA 事务,这一套下来挺复杂的(不是不可行)。但对于 Java EE 规范支持内的资源(特别是 Jdbc,EJB,JMS 这些)几乎不用太关注事务,代码反而不复杂,后面生产环境部署,主要集中在于服务器部署方案。

    当然今天 90 后很多工作中可能从来没用过应用服务器( Weblogic,Glassfish,Payara,Websphere 等)。我以前在上海帮朋友公司面试的时候,很多工作过 5 年以上的 Java 程序员都没有安装使用过 Tomcat manager ( Tomcat Web 管理界面), 应用服务器中 Console 能够管理优化很多使用的资源。实际我个人认为 Java EE 一些优秀的东西,比如 EJB remote, 还有就应用服务器的 Console 这些很好一部分现在全部丢掉了。反而最到了外部,再另外花大量时间开发什么大用户控制台(中台???)。

    今天的互联网应用很多比企业应用复杂得多,传统 ACID 那一套用在一个 Component (单独运行的服务)使用就行了,或者一个 Component 完全是无状态的(越来越多的东西都是考虑使用 Serverless,特别是一些成熟的云平台,如 AWS ),不同的 Component 之间使用消息通讯。传统的事务都是发生在 ms 级,而现在分布式体系一个任务的执行更多像走一个流程,可能要跨很长时间。分布式事务这个显然用这些场景不适合了,或者回到事务最基本的概念里, 用 Unit Of Work 比较适合。至于像 atomikos 这样 JTA 的厂商,在分布式系统中,非要将 JTA 套上 TCC,为了能用上事务那些概念,我就觉得没必要了。

    Redhat 的 MarkLittle 博士著有 https://www.amazon.com/Java-Transaction-Processing-Design-Implementation/dp/013035290X 记得不止一本,JBoss narayana 也算是一个专门的事务产品,除了在 Java EE,应用服务器使用外中,也可以单独使用。
    jimrok
        18
    jimrok  
       2020-06-26 11:57:05 +08:00
    @hantsy 你总结的很到位,JTA 这种东西也许就是时候银行内用,储蓄账户和基金账户做分布事务,理论上完备,产业界也支持,但不知道现在是否还真有地方在用。
    archer2ee
        19
    archer2ee  
       2022-03-30 19:56:13 +08:00
    @BBCCBB 好奇问一下,为什么说直接用 jedis 有点危险?
    BBCCBB
        20
    BBCCBB  
       2022-03-30 23:52:19 +08:00
    @archer2ee 你好, 我是基于 spring data redis 可以换底层驱动这一点来考虑的...
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5107 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 33ms UTC 09:30 PVG 17:30 LAX 01:30 JFK 04:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86