MySQL嵌套事务所遇到的问题
MySQL是支持嵌套事务的,但是没多少人会这么干的….前段时间在国外看到一些老外在争论MySQL嵌套事务的场景必要性。逗死我了,这嵌套的鬼畜用法还有啥场景必要性。 跟以前的dba同事聊过,得知,在任何场景下都不要使用MySQL嵌套的事务。
那么使用MySQL嵌套事务会遇到什么问题?
mysql>select*fromceshi; +------+ |n| +------+ |1| +------+ 1rowinset(0.00sec) mysql>starttransaction; QueryOK,0rowsaffected(0.00sec) mysql>insertintoceshivalues(2); QueryOK,1rowaffected(0.00sec) mysql>starttransaction; QueryOK,0rowsaffected(0.00sec) mysql>insertintoceshivalues(3); QueryOK,1rowaffected(0.00sec) mysql>commit; QueryOK,0rowsaffected(0.00sec) mysql>rollback; QueryOK,0rowsaffected(0.00sec)
虽然我在最后rollback回滚了,但是数据显示是 123 . 原本大家以为我的事务虽然是嵌套的状态,但感觉最后rollback回滚了,其实我们希望看到的结果是子事务执行成功,外层事务的失败会回滚的。 但事实不是这样的,最后的结果是 123.
+-----+ |n| +-----+ |1| |2| |3| +-----+
当sql解释器遇到starttransaction时候会触发commit…!!!
begin_1 sql_1 begin_2 sql_2 sql_3commit_1 rollback_1 .
begin_2被执行的时候,sql_1已经就被提交了,当你再去执行commit_1的时候,那么sql_2和sql_3就被提交了. 这时候你再去rollback,一定用都没有…. 因为先前都提交完了,你能回滚啥…
前面说过在架构上一般很少很少有人会嵌套使用事务,但有时候不小心被嵌套了。我们拿python的项目来说,首先我们使用装饰器来实现事务的包装,接着数据处理defa()和 defb()函数都被事务被包装起来,单纯的用a和b都没关系,都是单事务。 如果a逻辑里又调用b,那么会发生什么? 对的,事务嵌套了… 我想这是绝大数业务开发都会遇到的问题。
那么怎么规避这风险? 可以加锁呀…. 设立一个全局锁,当子事务创建前会判断锁的状态….
如果你是flask的框架,可以使用flaskg全局变量。
如果是django框架,那么可以使用threadlocal使用全局变量。
如果是tornado、gevent这种异步io架构,可以使用fd做协程变量的关联。
@decorator defwith_transaction(f,*args,**kwargs): db=connection.get_db_by_table("*") try: db.begin() ret=f(*args,**kwargs) db.commit() except: db.rollback() raise returnret @with_transaction defhide(self): '''订单不在app端显示''' ifself.statusnotinOrderStatus.allow_deletion_statuses(): raiseOrderStatusChangeNotAllowed(self.status,OrderStatus.deleted) ... @with_transaction defchange_receipt_info(self,address,name,phone): region=Region.get_by_address(address) ...
当我们去执行下面语句的时候,事务会被强制提交. 当然这里前提是autocommit=True。
ALTERFUNCTION ALTERPROCEDURE ALTERTABLE BEGIN CREATEDATABASE CREATEFUNCTION CREATEINDEX CREATEPROCEDURE CREATETABLE DROPDATABASE DROPFUNCTION DROPINDEX DROPPROCEDURE DROPTABLE UNLOCKTABLES LOADMASTERDATA LOCKTABLES RENAMETABLE TRUNCATETABLE SETAUTOCOMMIT=1 STARTTRANSACTION