微服务架构下的分布式事务解决方案

1 微服务的发展

微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇,很多互联网行业巨头、开源社区等都开始了微服务的讨论和实践。Hailo有160个不同服务构成,NetFlix有大约600个服务。国内方面,阿里巴巴、腾讯、360、京东、58同城等很多互联网公司都进行了微服务化实践。当前微服务的开发框架也非常多,比较著名的有Dubbo、SpringCloud、thrift 、grpc等。

2 微服务落地存在的问题

虽然微服务现在如火如荼,但对其实践其实仍处于探索阶段。很多中小型互联网公司,鉴于经验、技术实力等问题,微服务落地比较困难。如著名架构师Chris Richardson所言,目前存在的主要困难有如下几方面:

1)单体应用拆分为分布式系统后,进程间的通讯机制和故障处理措施变的更加复杂。

2)系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并操作多个数据库实现,服务调用的分布式事务问题变的非常突出。

3)微服务数量众多,其测试、部署、监控等都变的更加困难。

随着RPC框架的成熟,第一个问题已经逐渐得到解决。例如dubbo可以支持多种通讯协议,springcloud可以非常好的支持restful调用。对于第三个问题,随着docker、devops技术的发展以及各公有云paas平台自动化运维工具的推出,微服务的测试、部署与运维会变得越来越容易。

而对于第二个问题,现在还没有通用方案很好的解决微服务产生的事务问题。分布式事务已经成为微服务落地最大的阻碍,也是最具挑战性的一个技术难题。

3 传统分布式事务解决方案与适用场景
3.1 基于 XA 协议的两阶段提交方案

交易中间件与数据库通过 XA 接口规范,使用两阶段提交来完成一个全局事务, XA 规范的基础是两阶段提交协议。
upload successful
第一阶段是表决阶段,所有参与者都将本事务能否成功的信息反馈发给协调者;第二阶段是执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或者回滚。

upload successful
XA 协议资源层更普适,并且对业务几乎没有侵入,但为了适应各种业务场景使用,需要严格遵循事务 ACID 特性;服务层更接近业务,可以针对不同业务做特定的优化处理,追求更高的极限性能。

当然,并不是说 XA 协议只能作用于单个服务内部的多资源场景,跨服务的多资源场景也是可以的,只不过同样需要额外的事务传递机制。
upload successful
XA 协议通过每个 RM(Resource Manager,资源管理器)的本地事务隔离性来保证全局隔离,并且需要通过串行化隔离级别来保证分布式事务一致性。但是,串行化隔离级别存在一定的性能问题,如下所示:
upload successful
在串行化隔离级别下,会为本来不加锁的 Select 快照读操作都加上读锁,导致锁持有时间增加,并发性能进一步降低。当实现了无锁的全局一致性读取后,比如分布式 MVCC,可以大幅减少锁持有时间,并发性能会获得较大提升。

但不管怎么优化实现,分布式事务的热点数据并发性能最高就是趋近于单机本地事务。所以,无论是基于 XA 协议实现的分布式事务,还是单机本地事务,都是存在热点数据并发性能极限的。

那么 XA 协议最大的作用是什么呢?其最大的作用在于数据库资源横向扩展时,能保证多资源访问的事务属性。

当单台 RM 机器达到资源性能瓶颈,无法满足业务增长需求时,就需要横向扩展 RM 资源,形成 RM 集群。通过横向扩展资源,提升非热点数据的并发性能,这对于大体量的互联网产品来说,是至关重要的。
upload successful
以上图为例,假设单台 RM 的非热点数据并发性能为 100 TPS,那么 5台 RM 就是 500 TPS,就算一个分布式事务平均涉及 2 台 RM,也有 250 TPS,提升了 2.5 倍的非热点并发能力。

综上所述,基于 XA 协议实现的分布式事务并不能提升热点并发性能,其意义在于横向扩展资源提升非热点数据并发性能时,能严格保证对多资源访问的事务 ACID 特性。

至于热点数据并发性能问题,对于一般的应用来说,经过 SQL 层面一定的性能优化之后,其并发性能基本能够满足业务的需求。如果经过优化,达到性能极限之后,还不能满足,就需要上升到业务层面,根据业务特点,通过专门的业务逻辑或业务架构优化来实现。

直接在资源层实现分布式事务的另一点好处是其普适性,可以对上层业务屏蔽底层实现细节。这一点在云服务时代特别有用,云服务面对的是大量的中小企业,甚至是个人开发者,业务诉求不尽相同,普适、标准的分布式事务产品是非常有必要的,可以让开发者从底层技术细节中脱离出来,更专注于业务逻辑的实现,从而获得更高效、快速的业务发展。

3.2 基于 TCC 模型的方案

TCC方案在电商、金融领域落地较多。TCC方案其实是两阶段提交的一种改进。其将整个业务逻辑的每个分支显式的分成了Try、Confirm、Cancel三个操作。Try部分完成业务的准备工作,confirm部分完成业务的提交,cancel部分完成事务的回滚。基本原理如下图所示。
upload successful

事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的try接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。

upload successful
TCC 分布式事务模型直接作用于服务层。不与具体的服务框架耦合,与底层 RPC 协议无关,与底层存储介质无关,可以灵活选择业务资源的锁定粒度,减少资源锁持有时间,可扩展性好,可以说是为独立部署的 SOA 服务而设计的。

  • 跨服务的分布式事务

这一部分的作用与 XA 类似,服务的拆分,也可以认为是资源的横向扩展,只不过方向不同而已。

横向扩展可能沿着两个方向发展:

  1. 功能扩展。根据功能对数据进行分组,并将不同的功能组分布在多个不同的数据库上,这实际上就是 SOA 架构下的服务化。

  2. 数据分片,在功能组内部将数据拆分到多个数据库上,为横向扩展增加一个新的维度。

下图简要阐释了横向数据扩展策略:
upload successful
横向扩展的两种方法可以同时进行运用:用户信息(Users)、产品信息(Products)与交易信息(Trans)三个不同功能组可以存储在不同的数据库中。另外,每个功能组内根据其业务量可以再拆分到多个数据库中,各功能组可以相互独立地进行扩展。

因此,TCC 的其中一个作用就是在按照功能横向扩展资源时,保证多资源访问的事务属性。

  • 两阶段拆分

TCC 另一个作用就是把两阶段拆分成了两个独立的阶段,通过资源业务锁定的方式进行关联。资源业务锁定方式的好处在于,既不会阻塞其他事务在第一阶段对于相同资源的继续使用,也不会影响本事务第二阶段的正确执行。
upload successful
可以发现 TCC 模型进一步减少了资源锁的持有时间。同时,从理论上来说,只要业务允许,事务的第二阶段什么时候执行都可以,反正资源已经业务锁定,不会有其他事务动用该事务锁定的资源。

这对业务有什么好处呢?拿支付宝的担保交易场景来说,简化情况下,只需要涉及两个服务,交易服务和账务服务。交易作为主业务服务,账务作为从业务服务,提供 Try、Commit、Cancel 接口:

  1. Try 接口扣除用户可用资金,转移到预冻结资金。预冻结资金就是业务锁定方案,每个事务第二阶段只能使用本事务的预冻结资金,在第一阶段执行结束后,其他并发事务也可以继续处理用户的可用资金。

  2. Commit 接口扣除预冻结资金,增加中间账户可用资金(担保交易不能立即把钱打给商户,需要有一个中间账户来暂存)。

假设只有一个中间账户的情况下,每次调用支付服务的 Commit 接口,都会锁定中间账户,中间账户存在热点性能问题。

但是,在担保交易场景中,七天以后才需要将资金从中间账户划拨给商户,中间账户并不需要对外展示。因此,在执行完支付服务的第一阶段后,就可以认为本次交易的支付环节已经完成,并向用户和商户返回支付成功的结果,并不需要马上执行支付服务二阶段的 Commit 接口,等到低锋期时,再慢慢消化,异步地执行。
upload successful
可能部分读者认为担保交易比较特殊,其实直付交易(直接把钱打到商户账户的交易模式,Commit 接口扣除预冻结资金以后,不是转移到中间账务,而是直接转移到商户账户)也可以这样使用,只要提前告知商户,高峰期交易资金不是实时到账,但保证在一定时间之内结算完成,商户应该也是可以理解的。

这就是 TCC 分布式事务模型的二阶段异步化功能,从业务服务的第一阶段执行成功,主业务服务就可以提交完成,然后再由框架异步的执行各从业务服务的第二阶段。

通用型 TCC 解决方案

通用型 TCC 解决方案就是最典型的 TCC 分布式事务模型实现,所有从业务服务都需要参与到主业务服务的决策当中。
upload successful
适用场景
由于从业务服务是同步调用,其结果会影响到主业务服务的决策,因此通用型 TCC 分布式事务解决方案适用于执行时间确定且较短的业务,比如互联网金融企业最核心的三个服务:交易、支付、账务:
upload successful
当用户发起一笔交易时,首先访问交易服务,创建交易订单;然后交易服务调用支付服务为该交易创建支付订单,执行收款动作,最后支付服务调用账务服务记录账户流水和记账。
为了保证三个服务一起完成一笔交易,要么同时成功,要么同时失败,可以使用通用型 TCC 解决方案,将这三个服务放在一个分布式事务中,交易作为主业务服务,支付作为从业务服务,账务作为支付服务的嵌套从业务服务,由 TCC 模型保证事务的原子性。
upload successful
支付服务的 Try 接口创建支付订单,开启嵌套分布式事务,并调用账务服务的 Try 接口;账务服务在 Try 接口中冻结买家资金。一阶段调用完成后,交易完成,提交本地事务,由 TCC 框架完成分布式事务各从业务服务二阶段的调用。
支付服务二阶段先调用账务服务的 Confirm 接口,解冻买家资金;增加卖家可用资金。调用成功后,支付服务修改支付订单为完成状态,完成支付。
当支付和账务服务二阶段都调用完成后,整个分布式事务结束。

异步确保型 TCC 解决方案

异步确保型 TCC 解决方案的直接从业务服务是可靠消息服务,而真正的从业务服务则通过消息服务解耦,作为消息服务的消费端,异步地执行。
upload successful
可靠消息服务需要提供 Try,Confirm,Cancel 三个接口。Try 接口预发送,只负责持久化存储消息数据;Confirm 接口确认发送,这时才开始真正的投递消息;Cancel 接口取消发送,删除消息数据。
消息服务的消息数据独立存储,独立伸缩,降低从业务服务与消息系统间的耦合,在消息服务可靠的前提下,实现分布式事务的最终一致性。
此解决方案虽然增加了消息服务的维护成本,但由于消息服务代替从业务服务实现了 TCC 接口,从业务服务不需要任何改造,接入成本非常低。
适用场景
由于从业务服务消费消息是一个异步的过程,执行时间不确定,可能会导致不一致时间窗口增加。因此,异步确保性 TCC 分布式事务解决方案只适用于对最终一致性时间敏感度较低的一些被动型业务(从业务服务的处理结果不影响主业务服务的决策,只被动的接收主业务服务的决策结果)。比如会员注册服务和邮件发送服务:
upload successful
当用户注册会员成功,需要给用户发送一封邮件,告诉用户注册成功,并提示用户激活该会员。但要注意两点:

  1. 如果用户注册成功,一定要给用户发送一封邮件;

  2. 如果用户注册失败,一定不能给用户发送邮件。

因此,这同样需要会员服务和邮件服务保证原子性,要么都执行,要么都不执行。不一样的是,邮件服务只是一种被动型的业务,并不影响用户是否能够注册成功,它只需要在用户注册成功以后发送邮件给用户即可,邮件服务不需要参与到会员服务的活动决策中。

对于此种业务场景,可以使用异步确保型TCC分布式事务解决方案,如下:
upload successful
由可靠消息服务来解耦会员和邮件服务,会员服务与消息服务组成 TCC 事务模型,保证事务原子性。然后通过消息服务的可靠特性,确保消息一定能够被邮件服务消费,从而使得会员与邮件服务在同一个分布式事务中。同时,邮件服务也不会影响会员服务的执行过程,只在会员服务执行成功后被动接收发送邮件的请求。

补偿型 TCC 解决方案

补偿型 TCC 解决方案与通用型 TCC 解决方案的结构相似,其从业务服务也需要参与到主业务服务的活动决策当中。但不一样的是,前者的从业务服务只需要提供 Do 和 Compensate 两个接口,而后者需要提供三个接口。
upload successful
Do 接口直接执行真正的完整业务逻辑,完成业务处理,业务执行结果外部可见;Compensate 操作用于业务补偿,抵消或部分抵消正向业务操作的业务结果,Compensate操作需满足幂等性。
与通用型解决方案相比,补偿型解决方案的从业务服务不需要改造原有业务逻辑,只需要额外增加一个补偿回滚逻辑即可,业务改造量较小。但要注意的是,业务在一阶段就执行完整个业务逻辑,无法做到有效的事务隔离,当需要回滚时,可能存在补偿失败的情况,还需要额外的异常处理机制,比如人工介入。
适用场景
由于存在回滚补偿失败的情况,补偿型 TCC 分布式事务解决方案只适用于一些并发冲突较少或者需要与外部交互的业务,这些外部业务不属于被动型业务,其执行结果会影响主业务服务的决策,比如机票代理商的机票预订服务:
upload successful
该机票服务提供多程机票预订服务,可以同时预订多趟行程航班机票,比如从北京到圣彼得堡,需要第一程从北京到莫斯科,以及第二程从莫斯科到圣彼得堡。
当用户预订机票时,肯定希望能同时预订这两趟航班的机票,只预订一趟航班对用户来说没有意义。因此,对于这样的业务服务同样提出了原子性要求,如果其中一趟航班的机票预订失败,另外一趟需要能够取消预订。
但是,由于航空公司相对于机票代理商来说属于外部业务,只提供订票接口和取消预订接口,想要推动航空公司改造是极其困难的。因此,对于此类业务服务,可以使用补偿型 TCC 分布式事务解决方案,如下:
upload successful
网关服务在原有逻辑基础上增加 Compensate 接口,负责调用对应航空公司的取消预订接口。
在用户发起机票预订请求时,机票服务先通过网关 Do 接口,调用各航空公司的预订接口,如果所有航班都预订成功,则整个分布式事务直接执行成功;一旦某趟航班机票预订失败,则分布式事务回滚,由 TCC 事务框架调用各网关的 Compensate 补偿接口,其再调用对应航空公司的取消预订接口。通过这种方式,也可以保证多程机票预订服务的原子性。

总 结

对于现在的互联网应用来说,资源横向扩展提供了更多的灵活性,是一种比较容易实现的向外扩展方案,但是同时也明显增加了复杂度,引入一些新的挑战,比如资源之间的数据一致性问题。
横向数据扩展既可以按数据分片扩展,也可以按功能扩展。XA 与 TCC 模型在这一点上的作用类似,都能在横向扩展资源的同时,保证多资源访问的事务属性,只不过前者作用于数据分片时,后者作用于功能扩展时。
XA 模型另外一个意义在于其普适性,抛开性能问题的情况下,几乎可以适用于所有业务模式,这对于一些基础性的技术产品来说是非常有用的,比如分布式数据库、云服务的分布式事务框架等。

TCC 模型除了跨服务的分布式事务这一层作用之外,还具有两阶段划分的功能,通过业务资源锁定,允许第二阶段的异步执行,而异步化思想正是解决热点数据并发性能问题的利器之一。
当然TCC方案也有不足之处,集中表现在以下两个方面:

  • 对应用的侵入性强。业务逻辑的每个分支都需要实现try、confirm、cancel三个操作,应用侵入性较强,改造成本高。
  • 实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm和cancel接口必须实现幂等。
4 GTS-阿里分布式事务解决方案

GTS是一款分布式事务中间件,由阿里巴巴中间件部门研发,可以为微服务架构中的分布式事务提供一站式解决方案。

4.1 GTS的核心优势

  • 性能超强
    GTS通过大量创新,解决了事务ACID特性与高性能、高可用、低侵入不可兼得的问题。单事务分支的平均响应时间在2ms左右,3台服务器组成的集群可以支撑3万TPS以上的分布式事务请求。

  • 应用侵入性极低
    GTS对业务低侵入,业务代码最少只需要添加一行注解(@TxcTransaction)声明事务即可。业务与事务分离,将微服务从事务中解放出来,微服务关注于业务本身,不再需要考虑反向接口、幂等、回滚策略等复杂问题,极大降低了微服务开发的难度与工作量。

  • 完整解决方案
    GTS支持多种主流的服务框架,包括EDAS,Dubbo,Spring Cloud等。 有些情况下,应用需要调用第三方系统的接口,而第三方系统没有接入GTS。此时需要用到GTS的MT模式。GTS的MT模式可以等价于TCC模式,用户可以根据自身业务需求自定义每个事务阶段的具体行为。MT模式提供了更多的灵活性,可能性,以达到特殊场景下的自定义优化及特殊功能的实现。

  • 容错能力强
    GTS解决了XA事务协调器单点问题,实现真正的高可用,可以保证各种异常情况下的严格数据一致。

4.2 GTS的应用场景

GTS可应用在涉及服务调用的多个领域,包括但不限于金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、手游、视频、物联网、车联网等。

4.3 GTS与微服务的集成

GTS包括客户端(GTS Client)、资源管理器(GTS RM)和事务协调器(GTS Server)三个部分。GTS Client主要用来界定事务边界,完成事务的发起与结束。GTS RM完成事务分支的创建、提交、回滚等操作。GTS Server主要负责分布式事务的整体推进,事务生命周期的管理。GTS和微服务集成的结构图如下所示,GTS Client需要和业务应用集成部署,RM与微服务集成部署。
upload successful

4.4 GTS的输出形式

GTS目前有三种输出形式:公有云输出、公网输出、专有云输出。

公有云输出
这种输出形式面向阿里云用户。如果用户的业务系统已经部署到阿里云上,可以申请开通公有云GTS。开通后业务应用即可通过GTS保证服务调用的一致性。这种使用场景下,业务系统和GTS间的网络环境比较理想,达到很好性能。
upload successful

公网输出
这种输出形式面向于非阿里云的用户,使用更加方便、灵活,业务系统只要能连接互联网即可享受GTS提供的云服务(与公有云输出的差别在于客户端部署于用户本地,而不在云上)。
在正常网络环境下,以包含两个本地事务的全局事务为例,事务完成时间在20ms左右,50个并发就可以轻松实现1000TPS以上分布式事务,对绝大多数业务来说性能是足够的。在公网环境,网络闪断很难完全避免,这种情况下GTS仍能保证服务调用的数据一致性。
upload successful

专有云输出
这种形式主要面向于已建设了自己专有云平台的大用户,GTS可以直接部署到用户的专有云上,为专有云提供分布式事务服务。目前已经有10多个特大型企业的专有云使用GTS解决分布式事务难题,性能与稳定性经过了用户的严格检测。

4.5 GTS的使用方式

GTS对应用的侵入性非常低,使用也很简单。下面以订单存储应用为例说明。订单业务应用通过调用订单服务和库存服务完成订单业务,服务开发框架为Dubbo。

订单业务应用
在业务函数外围使用@TxcTransaction注解即可开启分布式事务。Dubbo应用通过隐藏参数将GTS的事务xid传播到服务端。
upload successful

服务提供者
更新库存方法
upload successful

4.6 GTS的应用情况

GTS目前已经在淘宝、天猫、阿里影业、淘票票、阿里妈妈、1688等阿里各业务系统广泛使用,经受了16年和17年两年双十一海量请求的考验。某线上业务系统最高流量已达十万TPS(每秒钟10万笔事务)。

GTS在公有云和专有云输出后,已经有了100多个线上用户,很多用户通过GTS解决SpringCloud、Dubbo、Edas等服务框架的分布式事务问题。业务领域涉及电力、物流、ETC、烟草、金融、零售、电商、共享出行等十几个行业,得到用户的一致认可。
upload successful
上图是GTS与SpringCloud集成,应用于某共享出行系统。业务共享出行场景下,通过GTS支撑物联网系统、订单系统、支付系统、运维系统、分析系统等系各统应用的数据一致性,保证海量订单和数千万流水的交易。

4.7 GTS的工程样例

GTS的公有云样例可参考阿里云网站。在公网环境下提供sample-txc-simple和sample-txc-dubbo两个样例工程。

样例业务逻辑
该样例是GTS的入门sample,案例的业务逻辑是从A账户转账给B账户,其中A和B分别位于两个MySQL数据库中,使用GTS事务保证A和B账户钱的总数始终不变。

样例搭建方法

1) 准备数据库环境

安装MySQL,创建两个数据库db1和db2。在db1和db2中分别创建txc_undo_log表(SQL脚本见4.7.3)。在db1库中创建user_money_a表,在db2库中创建user_money_b表。

2) 下载样例

将sample-txc-simple文件下载到本地,样例中已经包含了GTS的SDK。

3) 修改配置

打开sample-txc-simple/src/main/resources目录下的txc-client-context.xml,将数据源的url、username、password修改为实际值。

4) 运行样例

在sample-txc-simple目录下执行build.sh编译本工程。编译完成后执行run.sh。

sample-txc-dubbo 样例

样例业务逻辑

本案例模拟了用户下订单、减库存的业务逻辑。客户端(Client)通过调用订单服务(OrderService)创建订单,之后通过调用库存服务(StockService)扣库存。其中订单服务读写订单数据库,库存服务读写库存数据库。由 GTS 保证跨服务事务的一致性。

样例搭建方法
1) 准备数据库环境
安装MySQL,创建两个数据库db1和db2。在db1和db2中分别创建txc_undo_log表。在db1库中创建orders表,在db2库中创建stock表。

2) 下载样例
将样例文件sample-txc-dubbo下载到本地机器,样例中已经包含了GTS的SDK。

3) 修改配置
打开sample-txc-dubbo/src/main/resources目录,将dubbo-order-service.xml、dubbo-stock-service.xml两个文件中数据源的url、username、password修改为实际值。

4) 运行样例

a. 编译程序

在工程根目录执行 build.sh 命令,编译工程。编译后会在 sample-txc-dubbo/client/bin 目录下生成 order_run.sh、stock_run.sh、client_run.sh 三个运行脚本对应订单服务、库存服务以及客户端。

b. 运行程序

在根目录执行run.sh,该脚本会依次启动order_run.sh(订单服务)、stock_run.sh(库存服务)和client_run.sh(客户端程序)。

其他说明

样例使用Multicast注册中心的声明方式。如果本机使用无线网络,dubbo服务在绑定地址时有可能获取ipv6地址,可以通过jvm启动参数禁用。 方法是配置jvm启动参数 -Djava.net.preferIPv4Stack=true。

SQL

建表 txc_undo_log

CREATE TABLE txc_undo_log (

id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',

gmt_create datetime NOT NULL COMMENT '创建时间',

gmt_modified datetime NOT NULL COMMENT '修改时间',

xid varchar(100) NOT NULL COMMENT '全局事务ID',

branch_id bigint(20) NOT NULL COMMENT '分支事务ID',

rollback_info longblob NOT NULL COMMENT 'LOG',

status int(11) NOT NULL COMMENT '状态',

server varchar(32) NOT NULL COMMENT '分支所在DB IP',

PRIMARY KEY (id),

KEY unionkey (xid,branch_id)

) ENGINE=InnoDB AUTO_INCREMENT=211225994 DEFAULT CHARSET=utf8 COMMENT='事务日志表';

建表 user_money_a

CREATE TABLE user_money_a (

id int(11) NOT NULL AUTO_INCREMENT,

money int(11) DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

建表 user_money_b

CREATE TABLE user_money_b (

id int(11) NOT NULL AUTO_INCREMENT,

money int(11) DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

建表 orders

CREATE TABLE orders (

id bigint(20) NOT NULL AUTO_INCREMENT,

user_id varchar(255) NOT NULL,

product_id int(11) NOT NULL,

number int(11) NOT NULL,

gmt_create timestamp NOT NULL,

PRIMARY KEY (id)

) ENGINE=MyISAM AUTO_INCREMENT=351 DEFAULT CHARSET=utf8

建表 stock

CREATE TABLE stock (

product_id int(11) NOT NULL,

price float NOT NULL,

amount int(11) NOT NULL,

PRIMARY KEY (product_id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

参考资料


上一篇
Redis缓存穿透、缓存雪崩的分析及解决方案 Redis缓存穿透、缓存雪崩的分析及解决方案
什么是缓存击穿缓存击穿是查询数据库中不存在的数据,如果有用户恶意模拟请求很多缓存中不存在的数据,由于缓存中都没有,导致这些请求短时间内直接落在了DB上,对DB产生压力,导致数据库异常。 解决方案布隆过滤器 最常见的则是采用布隆过滤器,将所有
2018-11-21
下一篇
Java项目性能瓶颈定位(转) Java项目性能瓶颈定位(转)
文章目标当Java项目出现性能瓶颈的时候,通常先是对资源消耗做分析,包括CPU,文件IO,网络IO,内存;之后再结合相应工具查找消耗主体的程序代码。本文主要介绍系统资源消耗的分析过程,以及常用的Java线程分析方法。 CPU分析在Linu
2018-11-12