了解一下Mysql分布式事务及优缺点、使用案例(php+mysql)

了解一下Mysql分布式事务及优缺点、使用案例(php+mysql),第1张

概述在开发中,为了降低单点压力,通常会根据业务情况进行分表分库,将表分布在不同的库中(库可能分布在不同的机器上),但是一个业务场景可能会同时处理两个表的 *** 作。在这种场景下,事务的提交会变得相对复杂,因为多个节点(库)的存在,可能存在部分节点提交失败的情况,即事务的ACID特性需要在各个不同

在开发中,为了降低单点压力,通常会根据业务情况进行分表分库,将表分布在不同的库中(库可能分布在不同的机器上),但是一个业务场景可能会同时处理两个表的 *** 作。在这种场景下,事务的提交会变得相对复杂,因为多个节点(库)的存在,可能存在部分节点提交失败的情况,即事务的ACID特性需要在各个不同的数据库实例中保证。比如更新db1库的A表时,必须同步更新db2库的B表,两个更新形成一个事务,要么都成功,要么都失败。
那么我们如何利用MysqL实现分布式数据库的事务呢?

MysqL好像是从5.0开始支持分布式事务

这里先声明两个概念:

资源管理器(resource manager):用来管理系统资源,是通向事务资源的途径。数据库就是一种资源管理器。资源管理还应该具有管理事务提交或回滚的能力。
事务管理器(transaction manager):事务管理器是分布式事务的核心管理者。事务管理器与每个资源管理器(resource
manager)进行通信,协调并完成事务的处理。事务的各个分支由唯一命名进行标识。
MysqL在执行分布式事务(外部XA)的时候,MysqL服务器相当于xa事务资源管理器,与MysqL链接的客户端相当于事务管理器。

分布式事务原理:分段式提交
分布式事务通常采用2PC协议,全称Two Phase Commitment Protocol。该协议主要为了解决在分布式数据库场景下,所有节点间数据一致性的问题。分布式事务通过2PC协议将提交分成两个阶段:

prepare;
commit/rollback
阶段一为准备(prepare)阶段。即所有的参与者准备执行事务并锁住需要的资源。参与者ready时,向transaction manager报告已准备就绪。
阶段二为提交阶段(commit)。当transaction manager确认所有参与者都ready后,向所有参与者发送commit命令。

事务协调者transaction manager
因为XA 事务是基于两阶段提交协议的,所以需要有一个事务协调者(transaction manager)来保证所有的事务参与者都完成了准备工作(第一阶段)。如果事务协调者(transaction manager)收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。MysqL 在这个XA事务中扮演的是参与者的角色,而不是事务协调者(transaction manager)。

MysqL的XA事务分为外部XA和内部XA
外部XA用于跨多MysqL实例的分布式事务,需要应用层作为协调者,通俗的说就是比如我们在PHP中写代码,那么PHP书写的逻辑就是协调者。应用层负责决定提交还是回滚,崩溃时的悬挂事务。MysqL数据库外部XA可以用在分布式数据库代理层,实现对MysqL数据库的分布式事务支持,例如开源的代理工具:网易的DDB,淘宝的TDDL等等。
内部XA事务用于同一实例下跨多引擎事务,由binlog作为协调者,比如在一个存储引擎提交时,需要将提交信息写入二进制日志,这就是一个分布式内部XA事务,只不过二进制日志的参与者是MysqL本身。binlog作为内部XA的协调者,在binlog中出现的内部xID,在crash recover时,由binlog负责提交。(这是因为,binlog不进行prepare,只进行commit,因此在binlog中出现的内部xID,一定能够保证其在底层各存储引擎中已经完成prepare)。

MysqL xa事务的语法

1、首先要确保MysqL开启XA事务支持

SHOW VARIABLES liKE '%xa%'@H_502_43@

如果innodb_support_xa的值是ON就说明MysqL已经开启对XA事务的支持了。 

如果不是就执行:

SET innodb_support_xa = ON@H_502_43@

主要有:

XA START 'any_unique_ID'; // 'any_unique_ID' 是用户给的,全局唯一在一台MysqL中开启一个XA事务@H_502_43@
XA END 'any_unique_ID '; //标识XA事务的 *** 作结束@H_502_43@
XA PREPARE 'any_unique_ID'; //告知MysqL 准备提交这个xa事务@H_502_43@
XA COMMIT 'any_unique_ID'; //告知MysqL提交这个xa事务@H_502_43@
XA RolLBACK 'any_unique_ID'; //告知MysqL回滚这个xa事务@H_502_43@
XA RECOVER;//查看本机MysqL目前有哪些xa事务处于prepare状态@H_502_43@

XA事务恢复
如果执行分布式事务的MysqL crash了,MysqL 按照如下逻辑进行恢复:
a. 如果这个xa事务commit了,那么什么也不用做
b. 如果这个xa事务还没有prepare,那么直接回滚它
c. 如果这个xa事务prepare了,还没commit, 那么把它恢复到prepare的状态,由用户去决定commit或rollback
当MysqL crash后重新启动之后,执行“XA RECOVER;”查看当前处于prepare状态的xa事务,然后commit或rollback它们。

使用限制
a. XA事务和本地事务以及锁表 *** 作是互斥的
开启了xa事务就无法使用本地事务和锁表 *** 作

MysqL> xa start 't1xa';query OK, 0 rows affected (0.04 sec)@H_502_43@
MysqL> begin;ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state@H_502_43@
MysqL> lock table t1 read;ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state@H_502_43@

开启了本地事务就无法使用xa事务

MysqL> begin;query OK, 0 rows affected (0.00 sec)@H_502_43@
MysqL> xa start 'rrrr';ERROR 1400 (XAE09): XAER_OUTSIDE: Some work is done outsIDe global transaction@H_502_43@

b. xa start 之后必须xa end, 否则不能执行xa commit 和xa rollback

所以如果在执行xa事务过程中有语句出错了,你也需要先xa end一下,然后才能xarollback。

注意事项
a. MysqL只是提供了xa事务的接口,分布式事务中的MysqL实例之间是互相独立的不感知的。 所以用户必须
自己实现分布式事务的调度器
b. xa事务有一些使用上的BUG, 参考http://www.MysqLops.com/2012/02/24/MysqL-xa-optimize.HTML
主要是
“MysqL数据库的主备数据库的同步,通过binlog的复制完成。而binlog是MysqL数据库内部XA事务的协调者,并且MysqL数据库为binlog做了优化——binlog不写prepare日志,只写commit日志。
所有的参与节点prepare完成,在进行xa commit前crash。crash recover如果选择commit此事务。由于binlog在prepare阶段未写,因此主库中看来,此分布式事务最终提交了,但是此事务的 *** 作并未 写到binlog中,因此也就未能成功复制到备库,从而导致主备库数据不一致的情况出现。
而crash recover如果选rollback, 那么就会出现全局不一致(该分布式事务对应的节点,部分已经提交,无法回滚,而部分节点回滚。最终导致同一分布式事务,在各参与节点,最终状态不一致)”

参考的那篇blog中给出的办法是修改MysqL代码,这个无法在DBScale中使用。 所以可选的替代方案是不使用
主从复制进行备份,而是直接使用xa事务实现同步写来作为备份。

PHP+MysqL实现分布式事务案例

保证数据表是innodb的

//db_finance库下CREATE table `t_user_account` (  `ID` int(11) NOT NulL auto_INCREMENT COMMENT 'ID',  `username` varchar(255) NOT NulL DEFAulT '' COMMENT '用户名',  `money` int(11) NOT NulL DEFAulT '0' COMMENT '账户金额',  PRIMARY KEY (`ID`)) ENGINE=InnoDB auto_INCREMENT=2 DEFAulT CHARSET=utf8;@H_502_43@
//db_order库下CREATE table `t_user_orders` (  `ID` int(11) NOT NulL auto_INCREMENT COMMENT '主键',  `username` varchar(255) NOT NulL DEFAulT '',  `money` int(11) NOT NulL DEFAulT '0' COMMENT '订单扣款金额',  PRIMARY KEY (`ID`)) ENGINE=InnoDB auto_INCREMENT=44 DEFAulT CHARSET=utf8;@H_502_43@

PHP代码

$username = '温柔的风';$order_money = 100;$addOrder_success = addOrder($username,$order_money);$upAccount_success = updateAccount($username,$order_money);if($addOrder_success['state'] =="yes" && $upAccount_success['state']=="yes"){   commitdb($addOrder_success['xa']);   commitdb1($upAccount_success['xa']);}else{   rollbackdb($addOrder_success['xa']);   rollbackdb1($upAccount_success['xa']);}dIE;function addOrder ($username, $order_money){    $xa = uniqID("");    $sql_xa = "XA START '$xa'";    $db = Yii::app()->dborder_Readonly;    $db->createCommand($sql_xa)->execute();    $insert_sql = "INSERT INTO t_user_orders (`username`,`money`) VALUES ($username,$order_money)";    $ID = $db->createCommand($insert_sql)->execute();    $db->createCommand("XA END '$xa'")->execute();    if ($ID) {        $db->createCommand("XA PREPARE '$xa'")->execute();        return ['state' => 'yes', 'xa' => $xa];    }else {        return ['state' => 'no', 'xa' => $xa];    }}function updateAccount($username, $order_money){    $xa = uniqID("");    $sql_xa = "XA START '$xa'";    $db = Yii::app()->db_finance;    $db->createCommand($sql_xa)->execute();    $sql = "update t_user_account set money=money-".$order_money." where username='$username'";    $ID = $db->createCommand($sql)->execute();    $db->createCommand("XA END '$xa'")->execute();    if ($ID) {        $db->createCommand("XA PREPARE '$xa'")->execute();        return ['state' => 'yes', 'xa' => $xa];    }else {        return ['state' => 'no', 'xa' => $xa];    }}//提交事务!function commitdb($xa){    $db = Yii::app()->dborder_Readonly;    return $db->createCommand("XA COMMIT '$xa'")->execute();}//回滚事务function rollbackdb($xa){    $db = Yii::app()->dborder_Readonly;    return $db->createCommand("XA COMMIT '$xa'")->execute();}//提交事务!function commitdb1($xa){    $db = Yii::app()->db_finance;    return $db->createCommand("XA COMMIT '$xa'")->execute();}//回滚事务function rollbackdb1($xa){    $db = Yii::app()->db_finance;    return $db->createCommand("XA RolLBACK '$xa'")->execute();}@H_502_43@

 

总结

以上是内存溢出为你收集整理的了解一下Mysql分布式事务及优缺点、使用案例(php+mysql)全部内容,希望文章能够帮你解决了解一下Mysql分布式事务及优缺点、使用案例(php+mysql)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1001635.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存