通用技术 Mysql 提交分布式事务报异常的解决过程

Ningxw · 2020年07月28日 · 2206 次阅读

好久没来 testerhome 了,今天刚好解决了一个怎么搜都搜不出解决方案的问题(可能是我搜索问题的姿势不对 hhh~)来社区记录下,希望能帮助到有此困惑的宝宝们!

问题缘由

初衷是想做个业务流程自动回归,涉及到数据库的操作,在此之前都是手动敲 SQL 在 Navicat 里执行,所以想在代码里面一把梭~
然后我啪嗒啪嗒把连接 mysql 的代码写好,开开心心的调试个 select 语句,perfect 通过,不愧是我!😎 😎 😎
然后我又啪嗒啪嗒把业务流处理逻辑写好,开开心心的 run 起来,结果却不是很开心,在执行到 update 语句时,编辑器抛出一个错误,如下:

pymysql.err.InternalError: (616, 'Proxy ERROR:In non xa transaction, this SQL use a diffent set')

不慌不慌,先上百度谷歌查查看~(PS:补充一下,本座对数据库和 MYSQL 确实不够熟悉,平时也就 select 用得比较多,drop 什么的看情况使用~)但我在百度谷歌深究了很久,一个解决办法都搜不出,太菜了……

第一回合

根据报错指示,“使用 pymysql 时出现了代理错误:在非 XA 事务中,此 SQL 使用了不同的 SET 集”(已经尽力翻译了)于是我查找了下有关 “XA 事务” 的描述,得出:

  • 什么是 XA 事务?

XA(eXtended Architecture)是指由 X/Open 组织提出的分布式交易处理的规范。XA 是一个分布式事务协议,由 Tuxedo 提出,所以分布式事务也称为 XA 事务。XA 协议主要定义了事务管理器 TM(Transaction Manager,协调者)和资源管理器 RM(Resource Manager,参与者)之间的接口。其中,资源管理器往往由数据库实现,如 Oracle、DB2、MySQL,这些商业数据库都实现了 XA 接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。XA 事务是基于两阶段提交(Two-phaseCommit,2PC)协议实现的,可以保证数据的强一致性,许多分布式关系型数据管理系统都采用此协议来完成分布式。阶段一为准备阶段,即所有的参与者准备执行事务并锁住需要的资源。当参与者 Ready 时,向 TM 汇报自己已经准备好。阶段二为提交阶段。当 TM 确认所有参与者都 Ready 后,向所有参与者发送 COMMIT 命令。

一文教你迅速解决分布式事务 XA 一致性问题

大家当做拓展知识阅读即可,直接跳过也问题不大~~~

第二回合

以上得知该数据库是一个分布式数据库,在存在多个 set 时,需要使用到阶段二的提交方式。所以能初步判断,这应该是连接和使用数据库的方式不对~就凭 pymysql 已经是大家多年的好朋友了,应该它自己能搞定自己的问题~(别问我为啥能这么快做初步判断,我靠的第六感~)

有关分布式事务的内容不在这边展开说明,有兴趣的直接翻上面的链接自行学习哦~!

第三回合

拓展知识时间又到了:MySQL 的 SELECT,INSERT,UPDATE 或 DELETE 都会开启事务。如果 AUTOCOMMIT 设置为 1(默认值),每一个 SQL 语句都被认为是一个完整的事务。 AUTOCOMMIT 设置为 0 时,在随后的一系列语句的作用就像一个事务,直到一个明确的 COMMIT 语句结束。这是我辛辛苦苦搜出的重要线索,重点圈起来,要考!

修改脚本如下:

connection = pymysql.connect(
               host=self.dbInfo["host"],
               user=self.dbInfo["user"],
               password=self.dbInfo["password"],
               db=dbName,
               charset="utf8mb4",
               cursorclass=pymysql.cursors.DictCursor,
               autocommit=True       #加上这一句!
           )

搞定啦,可以愉快的玩耍啦~!(好像太简单了,有点侮辱智商的感觉,唉…)

聊聊踩坑过程中的其它解决办法

在解决问题时,我在公司内部某平台上看到有个同学写了一个 “号称史上最快执行 SQL” 的小工具,也是使用跟我同一个数据库,咦,这个东西可以执行 update 语句喔,于是我开开心心找到这个天才同学让他分享他的代码给我,代码是用 java 写的,用的 JDBC 的连接方式~(果然,java 爸爸就是了不起~)

为了不影响脚本进度,于是我就照仿着 java 的实现方式,用 python 连接 JDBC,试试看能不能实现。简单描述如下:

Python 使用 JDBC 连接 Mysql

  • 先安装 jaydebeapi

    pip install jaydebeapi -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
    
  • 下载 mysql-connector-java-5.1.34.jar 包

传送门 - mysql-connector-java-5.1.34.jar 包下载地址(亲测有效)

  • 直接上代码
import jaydebeapi
class JdbcJoin():
    def __init__(self):
        self.dbInfo = {
            "host": "jdbc:mysql://xxx:3306/",     #JDBC地址
            "user": "admin",
            "password": "admin",
            "driver": "com.mysql.jdbc.Driver",       #jdbc driver
            "jar_file": "D:\mysql-connector-java-5.1.34.jar"     #jar包位置
        }

    def connect(self, sql):
        conn = jaydebeapi.connect(self.dbInfo["driver"], self.dbInfo["host"], [self.dbInfo["user"], self.dbInfo["password"]], self.dbInfo["jar_file"])
        cur = conn.cursor()
        cur.execute(sql)
        result = cur.fetchall()
        print(result)
        cur.close()
        conn.close()
  • 总结

乍一看,这不就是变相调用 jar 包的方式吗?跟我之前的那一篇 Python 调用 JAVA 包 - 从入门到放弃 不是一个意思吗?唉,说好要一起放弃,结果我却在 N 年后捡回来,555555555,还是要多学习多动脑,这样子在遇到问题时脑袋瓜才不会嗡嗡嗡的~

祝大家工作顺利!

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册