事情是这样的
一个稍微有些小复杂的事务。首先需要初始化一些记录的值,然后选取一些新的记录,对新的记录进行排序,然后再将排序等信息写入。
SQL是按照 id in (xxx, xxx) 来写的。
# 这就是问题所在, 按这个查找 会出现一些意料不到的问题。
SELECT * FROM infos WHERE id IN(55001170412000002)
数据库id 存储是使用的 varchar(50), id形如 55001170412000002, 已经超过了默认的int11。
由于内部系统,使用的CI没有考虑注入之类的安全问题。 直接使用客户端传递的字段 拼接SQL。前端使用Ajax传递的数字型列表。后台处理方式是简单的增加了 ''。
这个其实是没问题的。问题是 在SQL中,使用了未添加 '' 的列表。
/* $ids_ 为处理过的字符串型列表、$ids 为原始数字型的列表 */
SELECT * FROM '.self::$infos.' WHERE id IN('.$ids.') ORDER BY FIELD(id, '.$ids_.');
在前端 将数字型列表 修改为 字符串型列表。
然后发现后端有处理过的$ids_,然后把SQL修改 全部使用处理过的。
并将前端的修改force push,回滚到未修改的版本。第一次失误:(此处JS存在缓存问题...但当时还没有发现)。
修改之后发现更严重的问题。前端修改排序之后,提交一直等待,然后
Query error: Lock wait timeout exceeded; try restarting transaction
数据库死锁....
第二次失误:使用Postman模拟数据提交,但是却没有再次确认浏览器提交的数据格式。
这也导致了,使用Postman偶尔可以正常提交,多数也是死锁的情况。
第三次失误:粗心的忽略了后台数据库SQL本身报错。也就是 给列表字段加了两次 '''' 单引号。
Query error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xxxx'
第四个失误:而不是第四次。发现 跟着CI官网的 数据库事务。并没有 try...catch...
这时应该真相就出来了。
开启事务-->执行出错-->异常退出 (并没有回滚或者提交导致)
try {
trans()
commit()
} catch(Exception e) {
rollback()
} finally {
close()?
}
mysql> SELECT * FROM information_schema.INNODB_TRX\G
*************************** 1. row ***************************
trx_id: 8483802
trx_state: RUNNING
trx_started: 2017-04-24 20:53:54
trx_requested_lock_id: NULL
trx_wait_started: NULL
trx_weight: 12
trx_mysql_thread_id: 111844
trx_query: NULL
trx_operation_state: NULL
trx_tables_in_use: 0
trx_tables_locked: 0
trx_lock_structs: 5
trx_lock_memory_bytes: 1248
trx_rows_locked: 15
trx_rows_modified: 7
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 10000
trx_is_read_only: 0
trx_autocommit_non_locking: 0
1 row in set (0.04 sec)
# 确认无效后,直接kill掉即可
mysql> kill 111844;
Query OK, 0 rows affected (0.04 sec)