首页域名资讯 正文

MySQL分库分表-多实例INSERT的困扰

2024-09-09 1 0条评论

存在问题

分库分表是完成了,细心的朋友可能会发现。我们这边存在一个问题就是本来应该在一起的事务,现在因为分库事务将被分成了两个。如果第一个事务完成了提交,这时候应用程序服务器发生了问题导致第二个没有提交。这样就有问题了。

例如:在之前文章的示例中导购的销售订单的事务已经提交了,而这时由于某些原因导致购买者的订单没有提交。这时候就会出现导购能看得到订单而消费者却看不到。

解决办法

1、直接使用MySQL的XA(分布式事物)

2、从业务上去解决

 

使用MySQL的XA前需要知道的

在同一个实例中不能有相同的XID(分布式事务ID)出现。使用Python在同一实例创建多个相同的XA会报错”The XID already exists”。

使用MySQL的XA

由于之前为了方便只在一个实例中演示了分库分表的操作。如果使用多个XA会报错的。

这边我只使(用伪代码)的形式来说明使用XA。如果有想知道Python如何使用XA可以常考以下链接

http://stackoverflow.com/questions/18485895/python-distributed-transactions-like-java-transaction-api-jta

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 # 1.获取买家和卖家数据库连接描述符 buy_user_conn_info = get_conn_info ( ) sell_user_conn_info = get_conn_info ( ) # 比较 得到的两个数据库连接描述符 if buy_user_conn_info == sell_user_conn_info :    # 如果相等使用同只创建一个数据量连接进行数据库操作    conn = mysql . connector . connect ( * * sell_user_conn_info )    # 通过pysnowflask获得XID, order_id。XID和order_id是同一个    guid = snowflake . client . get_guid ( )    # 获得游标    cur = conn . cursor ( ) ;    # 开始一个XA事务    cur . execute ( “XA START ‘{xid}'” . format ( xid = guid ) ) ;    # 生成出售者订单信息    cur . execute ( “INSERT INTO test_x.sell_order_x VALUES(xx)” ) ;    cur . execute ( “INSERT INTO test_x.order_goods_x VALUES(xx)” ) ;    cur . execute ( “INSERT INTO test_x.order_goods_x VALUES(xx)” ) ;    # 生成购买者订单信息    cur . execute ( “INSERT INTO test_y.buy_order_y VALUES(xx)” ) ;    # 完成数据操作    cur . execute ( “XA END ‘{xid}'” . format ( xid = guid ) ) ;    # 要准备提交了    cur . execute ( “XA PREPARE ‘{xid}'” . format ( xid = guid ) ) ;    # 提交事务    cur . execute ( “XA COMMIT ‘{xid}'” . format ( xid = guid ) ) ; else :    # 如果连接描述符不相等就需要在不实例中使用XID相等的XA事务    conn_sell = mysql . connector . connect ( * * sell_user_conn_info )    conn_buy = mysql . connector . connect ( * * buy_user_conn_info )    # 获得游标    cur_sell = conn_sell . cursor ( ) ;    cur_buy = conn_buy . cursor ( ) ;    try :      # 通过pysnowflask获得XID, order_id。XID和order_id是同一个      cur_sell . execute ( “XA START ‘{xid}'” . format ( xid = guid ) ) ;      cur_buy . execute ( “XA START ‘{xid}'” . format ( xid = guid ) ) ;      # 生成出售者订单信息      cur_sell . execute ( “INSERT INTO test_x.sell_order_x VALUES(xx)” ) ;      cur_sell . execute ( “INSERT INTO test_x.order_goods_x VALUES(xx)” ) ;      cur_sell . execute ( “INSERT INTO test_x.order_goods_x VALUES(xx)” ) ;      # 生成购买者订单信息      cur_buy . execute ( “INSERT INTO test_y.buy_order_y VALUES(xx)” ) ;      # 要准备提交了      cur_sell . execute ( “XA PREPARE ‘{xid}'” . format ( xid = guid ) ) ;      cur_buy . execute ( “XA PREPARE ‘{xid}'” . format ( xid = guid ) ) ;      # 提交事务      cur_sell . execute ( “XA COMMIT ‘{xid}'” . format ( xid = guid ) ) ;      cur_buy . execute ( “XA COMMIT ‘{xid}'” . format ( xid = guid ) ) ;    except :      # 发生错误回滚XA事务      cur_sell . execute ( “XA ROLLBACK ‘{xid}'” . format ( xid = guid ) ) ;      cur_buy . execute ( “XA ROLLBACK ‘{xid}'” . format ( xid = guid ) ) ;    finally :      cur_sell . close ( )      cur_buy . close ( )      conn_sell . disconnect ( )      conn_buy . disconnect ( )

需要说明的是如果使用了XA事务在性能上肯定会比没有使用事务来的差。

MySQL XA事务额外注意事项

在使用MySQL 5.6版本之前(包括5.6)使用分布式事务的时候如果还没有COMMIT的事务是不会记录到binlog中的。因此如果在COMMIT之前如果发生了MySQL挂掉,一定要将之前还没有提交的XA事务给回滚了。如果提交了由于没有些binlog导致从库就同步不到这次事务,从而导致了主从不一致的情况。而在MySQL5.7中解决了这一个问题。

从业务上去解决

从业务上去解决说白了就是能用语言来掩饰会遇到的BUG。然后,让那些做产品的能够接受,并且最后能有一些友好的补救措施。当然,这种方式不能影响主要业务。

问题分析

我们现在担心的是。当用户下单的时候发生了只有出售者的用户订单是写入数据库的,而购买订单没有写入到数据库。从表现来看就是出售者能看到订单信息,而购买者就看不到。并且这样的事一般只会在应用程序或服务器出问题容易发生,平常都是能正常的。

认真想一下,购买者没看到订单,那就不能够付款。不能够付款,说明订单的状态始终是未付款状态。也就是成了一个僵尸单。买家没有经济上的损失,卖家也没有货物上的损失。那这种情况是不是双方都能接受的嘞?这就需要和产品沟通了。

当然,我们不能放着这些僵尸单不管的。我们需要定期的去监控这些僵尸单的存在。如果检测到了僵尸单应该做出相应的处理,这时候又要和产品沟通了,是恢复该僵尸单让购买者能看到并给相关提示呢?还是该僵尸单给干掉,或者其他更友好的办法。

对僵尸单的监控

很容易想到可以通过查询数据库来过滤订单僵尸订单,这其实是一个办法。还有另外一个办法,就是在提交订单事物完成和会写相关的日志。我们只要去分析日志就好了。

这边我以日志的形式进行讲解:

1、当卖家订单提交是将信息记入到文件(主要是记录订单ID)。

2、当买家订单提交是将信息记录到另一个文件(主要是记录订单ID)。

3、用程序分析两个文件取出订单是否产生差集。并记录下差集的订单ID。这个差集的订单ID有僵尸单的嫌疑,

4、当再寻找僵尸单的时候先对上次可以的僵尸单进行处理,如果没有在购买者的日志中那说明那就是僵尸单了。然后再接着进行分析对比。

需要注意的是要把控好监控的时间间隔,不能太短,比如 1s。这时购买者订单可能还没提交。这样就会错误判断为僵尸单。

 

 

文章转载来自:trustauth.cn

文章版权及转载声明

本文作者:亿网 网址:https://edns.com/ask/post/150759.html 发布于 2024-09-09
文章转载或复制请以超链接形式并注明出处。