mysql事务 mysql事务回滚 MySQL事务死锁 如何解除死锁 资金出入账
问题
最近使用golang做资金账户,目前涉及到这两个问题
- 资金入账时,可能存在提现【出账】
- 资金提现时,可能存在资金入账
因而,为了保证资金的正确性,这里需要事务操作。
什么是事务
MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!
为什么需要事务
以上面资金出账和入账为例子,写出如下没有事务的代码:
创建账户表
mysql> create table account (
-> id int(11) primary key not null auto_increment comment '账户自增主键id',
-> userId int(11) not null comment '用户id',
-> balance int(11) not null DEFAULT 0 comment '账户余额,默认为0'
-> ) ENGINE = 'INNODB';
Query OK, 0 rows affected (0.06 sec)
由Query OK, 0 rows affected (0.06 sec)
可知,account
表创建成功。
插入数据
mysql> insert into account(userId,balance) values(1223,444),(1224,666);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
由Query OK, 2 rows affected (0.01 sec)
可知,数据插入成功。
无事务资金出入账
- 假设
用户1223
给用户1224
转账100元。 - 若转账成功,
用户1223
的账户余额为344元,用户1224
的账户余额为766元。 - 如果转账失败,
用户1223
给用户1224
的账户余额不变。
按照这个想法,设计如下SQL语句,此时是没有事务的:
-- 执行用户1223的出账,用户1224的入账
mysql> update account set balance = balance -100 where userId = 1223;
update account set balance = balance + 100 where useId = 1224 ;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
ERROR 1054 (42S22): Unknown column 'useId' in 'where clause'
在两条SQL语句中,用户1223
出账SQL语句正确,即其账户余额为344元;用户1224
入账SQL语句错误,因为account表
中没有useId
这个字段,因而其账户余额不变,仍旧是666元,如下SQL所示:
mysql> select * from account;
+----+--------+---------+
| id | userId | balance |
+----+--------+---------+
| 1 | 1223 | 344 |
| 2 | 1224 | 666 |
+----+--------+---------+
2 rows in set (0.00 sec)
这就和我们的预期不同,此时,我们使用如下SQL恢复用户1223
的账户余额:
mysql> update account set balance = balance + 100 where userId = 1223 ;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
然后使用事务,再次执行上述SQL。
有事务资金出入账
依旧是``用户1223给
用户1224```转账100元,如下所示:
-- 开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
-- 执行用户1223的出账,用户1224的入账
mysql> update account set balance = balance -100 where userId = 1223;
update account set balance = balance + 100 where useId = 1224 ;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
ERROR 1054 (42S22): Unknown column 'useId' in 'where clause'
-- 出现错误语句,回滚金额
mysql> rollback;
Query OK, 0 rows affected (0.01 sec)
-- 查询回滚后的账户余额
mysql> select * from account;
+----+--------+---------+
| id | userId | balance |
+----+--------+---------+
| 1 | 1223 | 444 |
| 2 | 1224 | 666 |
+----+--------+---------+
2 rows in set (0.00 sec)
- 首先,开启事务
- 执行
用户1223
的出账,用户1224
的入账 用户1223
的出账SQL正确,用户1224
的入账SQL错误- 出现错误的SQL语句,事务回滚
- 查询账户余额,发现账户余额不变,符合我们的预期。
因而,为了保证资金的正确性,我们必须使用事务。
事务死锁
死锁出现的原因
在事务的情况下,给某个账户加上行级锁。
我使用的是for update
的悲观行级锁,但前置条件是,当前账户要存在,否则,行级锁就会变成表解锁,锁粒度就会增加。
比如,在提现时,需要判断当前账户是否绑卡,如果没有绑卡,就抛出您尚未绑卡的toast
。一般情况下都绑了卡,但就怕特殊情况,真的是想啥来啥。恰巧遇到某用户没有绑卡,抛出您尚未绑卡的toast之后,再次访问就报出如下问题:
于是去排查问题,发现在抛出尚未绑卡的异常时,没有将事务回滚,于是,此处添加事务回滚tx.rollBack
。
如果不进行事务回滚,那么当前行就不释放锁,相同的请求SQL过来,就会不停地尝试连接,,如果连接不成功,则会抛出连接超时的问题。
当前行不释放锁,新的SQL请求加锁,便出现了死锁的情况。
解决事务死锁
查看表级锁
如果行级锁不存在,使用SQLshow OPEN TABLES where In_use > 0
查看是否存在表级锁,如下SQL所示:
mysql> show OPEN TABLES where In_use > 0;
Empty set (0.00 sec)
查询表锁进程
其次,查询进程(如果您有SUPER权限,您可以看到所有线程。否则,您只能看到您自己的线程)show processlist
,最后杀死【kill】
进程id
查询行级锁
使用sql语句SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
查看当前存在锁的事务,如下图存在一个事务死锁:
杀死行锁进程
杀死锁进程,我们可以使用命令: kill trx_mysql_thread_id
比如杀死如上的行级死锁,找到trx_mysql_thread_id
的数值,执行命令:kill 4314823
相关文章
- 第14章_MySQL事务日志
- 数据库-数据库-MySQL(12)- 事务
- MySQL 是如何实现RC事务隔离级别的
- A机器连接同一局域网下的B机器的虚拟机中的mysql
- 利用Dockerfile构建一个基于centos 7,包括java 8, tomcat 7,php ,mysql+mycat的镜像
- mysql事务
- Sqlite向MySql导入数据
- MySQL 两种存储引擎: MyISAM和InnoDB 简单总结
- MySQL学习之Mysql锁&事务隔离级别
- 同步mysql数据到ElasticSearch的最佳实践
- node.js操作mysql
- MySQL必知必会:简介undo log、truncate、以及undo log如何帮你回滚事务(一)
- mysqlbinlog和binlog2sql的使用及mysql通过binlog日志恢复数据
- MySQL之增删改查,详细干货,建议收藏
- MySQL系列-高级-深入理解Mysql事务隔离级别与锁机制01
- mysql 根据日志 恢复数据详细
- MySQL explain,type分析(转)
- MySQL 的"双1设置"-数据安全的关键参数(案例分享)
- 【mysql我能讲两小时036】为什么需要保证mysql数据库二进制文件写入顺序和innodb层的事务提交顺序一致?