PHP队列场景以及实现代码实例详解 |
本文标签:PHP队列场景,PHP队列 为了降低单点压力,通常会根据业务情况进行分表分库,将表分布在不同的库中(库可能分布在不同的机器上),但是一个业务场景可能会同时处理两个表的操作 。在这种场景下,事务的提交会变得相对复杂,因为多个节点(库)的存在,可能存在部分节点提交失败的情况,即事务的ACID特性需要在各个不同的数据库实例中保证 。比如更新db1库的A表时,必须同步更新db2库的B表,两个更新形成一个事务,要么都成功,要么都失败 。 那么我们如何利用mysql实现分布式数据库的事务呢? mysql是从5.0开始支持分布式事务 这里先声明两个概念:资源管理器(resource manager):用来管理系统资源,是通向事务资源的途径 。数据库就是一种资源管理器 。资源管理还应该具有管理事务提交或回滚的能力 。 分布式事务原理:分段式提交
阶段一为准备(prepare)阶段 。即所有的参与者准备执行事务并锁住需要的资源 。参与者ready时,向transaction manager报告已准备就绪 。 事务协调者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%' 如果 SET innodb_support_xa = ON 主要有: XA START 'any_unique_id'; // 'any_unique_id' 是用户给的,全局唯一在一台mysql中开启一个XA事务 XA END 'any_unique_id '; //标识XA事务的操作结束 XA PREPARE 'any_unique_id'; //告知mysql 准备提交这个xa事务 XA COMMIT 'any_unique_id'; //告知mysql提交这个xa事务 XA ROLLBACK 'any_unique_id'; //告知mysql回滚这个xa事务 XA RECOVER;//查看本机mysql目前有哪些xa事务处于prepare状态 XA事务恢复 使用限制 mysql> xa start 't1xa'; Query OK, 0 rows affected (0.04 sec) mysql> begin; ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state mysql> lock table t1 read; ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state 开启了本地事务就无法使用xa事务 mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> xa start 'rrrr'; ERROR 1400 (XAE09): XAER_OUTSIDE: Some work is done outside global transaction
所以如果在执行xa事务过程中有语句出错了,你也需要先 注意事项 a. mysql只是提供了xa事务的接口,分布式事务中的mysql实例之间是互相独立的不感知的 。 所以用户必须自己实现分布式事务的调度器 参考的那篇blog中给出的办法是修改mysql代码,这个无法在DBScale中使用 。 所以可选的替代方案是不使用 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; //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; php代码 $username = '公众号PHP开源社区'; $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(); 到此这篇关于PHP队列场景以及实现代码实例详解的文章就介绍到这了,更多相关PHP队列场景以及实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! |