存在问题
分库分表是完成了,细心的朋友可能会发现。我们这边存在一个问题就是本来应该在一起的事务,现在因为分库事务将被分成了两个。如果第一个事务完成了提交,这时候应用程序或服务器发生了问题导致第二个没有提交。这样就有问题了。
例如:在之前文章的示例中导购的销售订单的事务已经提交了,而这时由于某些原因导致购买者的订单没有提交。这时候就会出现导购能看得到订单而消费者却看不到。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
解决办法
1、直接使用MySQL的XA(分布式事物)文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
2、从业务上去解决文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
使用MySQL的XA前需要知道的文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
在同一个实例中不能有相同的XID(分布式事务ID)出现。使用Python在同一实例创建多个相同的XA会报错"The XID already exists"。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
使用MySQL的XA文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
由于之前为了方便只在一个实例中演示了分库分表的操作。如果使用多个XA会报错的。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
这边我只使(用伪代码)的形式来说明使用XA。如果有想知道Python如何使用XA可以常考以下链接文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
http://stackoverflow.com/questions/18485895/python-distributed-transactions-like-java-transaction-api-jta文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
# 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事务在性能上肯定会比没有使用事务来的差。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
MySQL XA事务额外注意事项文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
在使用MySQL 5.6版本之前(包括5.6)使用分布式事务的时候如果还没有COMMIT的事务是不会记录到binlog中的。因此如果在COMMIT之前如果发生了MySQL挂掉,一定要将之前还没有提交的XA事务给回滚了。如果提交了由于没有些binlog导致从库就同步不到这次事务,从而导致了主从不一致的情况。而在MySQL5.7中解决了这一个问题。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
从业务上去解决
从业务上去解决说白了就是能用语言来掩饰会遇到的BUG。然后,让那些做产品的能够接受,并且最后能有一些友好的补救措施。当然,这种方式不能影响主要业务。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
问题分析文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
我们现在担心的是。当用户下单的时候发生了只有出售者的用户订单是写入数据库的,而购买订单没有写入到数据库。从表现来看就是出售者能看到订单信息,而购买者就看不到。并且这样的事一般只会在应用程序或服务器出问题容易发生,平常都是能正常的。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
认真想一下,购买者没看到订单,那就不能够付款。不能够付款,说明订单的状态始终是未付款状态。也就是成了一个僵尸单。买家没有经济上的损失,卖家也没有货物上的损失。那这种情况是不是双方都能接受的嘞?这就需要和产品沟通了。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
当然,我们不能放着这些僵尸单不管的。我们需要定期的去监控这些僵尸单的存在。如果检测到了僵尸单应该做出相应的处理,这时候又要和产品沟通了,是恢复该僵尸单让购买者能看到并给相关提示呢?还是该僵尸单给干掉,或者其他更友好的办法。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
对僵尸单的监控文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
很容易想到可以通过查询数据库来过滤订单僵尸订单,这其实是一个办法。还有另外一个办法,就是在提交订单事物完成和会写相关的日志。我们只要去分析日志就好了。文章源自运维生存时间-https://www.ttlsa.com/mysql/mysql-distributed-database-and-table-insert-trouble/
这边我以日志的形式进行讲解:
1、当卖家订单提交是将信息记入到文件(主要是记录订单ID)。
2、当买家订单提交是将信息记录到另一个文件(主要是记录订单ID)。
3、用程序分析两个文件取出订单是否产生差集。并记录下差集的订单ID。这个差集的订单ID有僵尸单的嫌疑,
4、当再寻找僵尸单的时候先对上次可以的僵尸单进行处理,如果没有在购买者的日志中那说明那就是僵尸单了。然后再接着进行分析对比。
需要注意的是要把控好监控的时间间隔,不能太短,比如 1s。这时购买者订单可能还没提交。这样就会错误判断为僵尸单。
当然,还有其他很多方法。只要思维活跃,东西都是靠人想出来的。
昵称:HH
QQ:275258836
ttlsa群交流沟通(QQ群②:6690706 QQ群③:168085569 QQ群④:415230207(新) 微信公众号:ttlsacom)
感觉本文内容不错,读后有收获?

1F
搞那么求麻烦干啥 mycat搞到。。。。