热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

分析MySQ锁机制和事务控制

innodb_lock_wait_timeout系统变量的值来解决这些情况。如果要依靠锁等待超时来解决死锁问题,对于更新事务密集的应用,将有可能导致大量事务的锁等待,导致系统异常,所以不推荐在一个事务中混合更新不同存储类型的表,也不推荐相同类型的表采用不同的锁定方式加锁

一、如何加锁

锁定表的语法:
    LOCK TABLES
    tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}
    [, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}] ...
解锁语法:
    UNLOCK TABLES
innodb的存储引擎提供行级锁,支持共享锁和排他锁两种锁定模式,以及四种不同的隔离级别。

二、死锁

InnoDB自动检测事务的死锁,并回滚一个或几个事务来防止死锁。InnoDB不能在MySQL LOCK TABLES设定表锁定的地方或者涉及InnoDB之外的存储引擎设置锁定的地方检测死锁。你必须通过设定innodb_lock_wait_timeout系统变量的值来解决这些情况。如果要依靠锁等待超时来解决死锁问题,对于更新事务密集的应用,将有可能导致大量事务的锁等待,导致系统异常,所以不推荐在一个事务中混合更新不同存储类型的表,也不推荐相同类型的表采用不同的锁定方式加锁。

三、事务控制

MySQL通过SET AUTOCOMMIT, START TRANSACTION, COMMIT和ROLLBACK等语句支持本地事务。语法:
   START TRANSACTION | BEGIN [WORK]
   COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
   ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
   SET AUTOCOMMIT = {0 | 1}
默认情况下,mysql是autocommit的,如果需要通过明确的commit和rollback来提交和回滚事务,那么需要通过明确的事务控制命令来开始事务,这是和oracle的事务管理明显不同的地方,如果应用是从oracle数据库迁移到mysql数据库,则需要确保应用中是否对事务进行了明确的管理。

①START TRANSACTION或BEGIN语句可以开始一项新的事务。
②COMMIT和ROLLBACK用来提交或者回滚事务。
③CHAIN和RELEASE子句分别用来定义在事务提交或者回滚之后的操作,chain会立即启动一个新事物,并且和刚才的事务具有相同的隔离级别,release则会断开和客户端的连接。
④SET AUTOCOMMIT可以修改当前连接的提交方式,如果设置了SET AUTOCOMMIT=0,则设置之后的所有事务都需要通过明确的命令进行提交或者回滚。

如果我们只是对某些语句需要进行事务控制,则使用START TRANSACTION开始一个事务比较方便,这样事务结束之后可以自动回到自动提交的方式,如果我们希望我们所有的事务都不是自动提交的,那么通过修改AUTOCOMMIT来控制事务比较方便,这样不用在每个事务开始的时候再执行START TRANSACTION。

time session_1 session_2
---------------------------------------------------------> mysql> select * from tt3;
Empty set (0.00 sec)
mysql> select * from tt3;
Empty set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into tt3 values('1',1);
Query OK, 1 row affected (0.03 sec)
 
  mysql> select * from tt3;
Empty set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.05 sec)
 
  mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 1    | 1.00 |
+------+------+
1 row in set (0.00 sec)
mysql> insert into tt3 values('2',2);
Query OK, 1 row affected (0.04 sec)
这个事务是按照自动提交执行的
 
  mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 1    | 1.00 |
| 2    | 2.00 |
+------+------+
2 rows in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
 
mysql> insert into tt3 values('3',3);
Query OK, 1 row affected (0.00 sec)
 
mysql> commit and chain;
Query OK, 0 rows affected (0.05 sec)
自动开始一个新的事务
mysql> insert into tt3 values('4',4);
Query OK, 1 row affected (0.00 sec)
 
  mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 1    | 1.00 |
| 2    | 2.00 |
| 3    | 3.00 |
+------+------+
3 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.06 sec)
 
  mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 1    | 1.00 |
| 2    | 2.00 |
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
4 rows in set (0.00 sec)
 
开始一个事务,会造成一个隐含的unlock tables被执行:
time session_1 session_2
---------------------------------------------------------> mysql> select * from tt3;
Empty set (16.65 sec)
mysql> select * from tt3;
Empty set (16.65 sec)
mysql> lock table tt3 write;
Query OK, 0 rows affected (0.00 sec)
 
  mysql> select * from tt3;
等待
mysql> insert into tt3 values('1',1);
Query OK, 1 row affected (0.07 sec)
等待
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
等待
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
等待
  mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 1    | 1.00 |
+------+------+
1 row in set (37.71 sec)
开始一个事务时,表锁被释放。
对lock方式加的表锁,不能通过rollback进行回滚。
 
因此,在同一个事务中,最好不使用不同存储引擎的表,否则rollback时需要对非事务类型的表进行特别的处理,因为commit、rollback只能对事务类型的表进行提交和回滚。
通常情况下,只对提交的事务纪录到二进制的日志中,但是如果一个事务中包含非事务类型的表,那么回滚操作也会被记录到二进制日志中,以确保非事务类型表的更新可以被复制到从的数据库中。

和oracle的事务管理相同,所有的DDL语句是不能回滚的,并且部分的DDL语句会造成隐式的提交。

在事务中可以通过定义savepoint,指定回滚事务的一个部分,但是不能指定提交事务的一个部分。对于复杂的应用,可以定义多个不同的savepoint,满足不同的条件时,回滚不同的savepoint。需要注意的是,如果定义了相同名字的savepoint,则后面定义的savepoint会覆盖之前的定义。对于不再需要使用的savepoint,可以通过release savepoint命令删除savepoint,删除后的savepoint,不能再执行rollback to savepoint命令。

下面我们例子就是模拟回滚事务的一个部分,通过定义savepoint来指定需要回滚的事务的位置。
time session_1 session_2
---------------------------------------------------------> mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 2    | 2.00 |
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
3 rows in set (0.00 sec)
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 2    | 2.00 |
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
3 rows in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
 
mysql> delete from tt3 where id = '2';
Query OK, 1 row affected (0.00 sec)
 
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
3 rows in set (0.00 sec)
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 2    | 2.00 |
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
3 rows in set (0.00 sec)
mysql> savepoint test;
Query OK, 0 rows affected (0.00 sec)
 
mysql> delete from tt3 where id = '3';
Query OK, 1 row affected (0.00 sec)
 
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 4    | 4.00 |
+------+------+
3 rows in set (0.00 sec)
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 2    | 2.00 |
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
3 rows in set (0.00 sec)
mysql> rollback to savepoint test;
Query OK, 0 rows affected (0.00 sec)
 
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
2 rows in set (0.00 sec)
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 2    | 2.00 |
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
3 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.05 sec)
 
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
2 rows in set (0.00 sec)
mysql> select * from tt3;
+------+------+
| id   | name |
+------+------+
| 3    | 3.00 |
| 4    | 4.00 |
+------+------+
2 rows in set (0.00 sec)


推荐阅读
  • 版本控制工具——Git常用操作(下)
    本文由云+社区发表作者:工程师小熊摘要:上一集我们一起入门学习了git的基本概念和git常用的操作,包括提交和同步代码、使用分支、出现代码冲突的解决办法、紧急保存现场和恢复 ... [详细]
  • 本文介绍如何在MySQL中创建一个自定义函数,用于将包含多个班级编号的字符串拆分为对应的班级名称。通过详细解释代码逻辑和功能,帮助读者理解并应用这一技术。 ... [详细]
  • 优化Flask应用的并发处理:解决Mysql连接过多问题
    本文探讨了在Flask应用中通过优化后端架构来应对高并发请求,特别是针对Mysql 'too many connections' 错误的解决方案。我们将介绍如何利用Redis缓存、Gunicorn多进程和Celery异步任务队列来提升系统的性能和稳定性。 ... [详细]
  • 本文介绍了如何利用 MySQL 的原生函数 FIND_IN_SET 和 GROUP_CONCAT 实现树形结构数据的递归查询。通过创建自定义函数,可以轻松获取多层级的数据关系,并优化查询性能。 ... [详细]
  • MongoDB的核心特性与架构解析
    本文深入探讨了MongoDB的核心特性,包括其强大的查询语言、灵活的文档模型以及高效的索引机制。此外,还详细介绍了MongoDB的体系结构,解释了其文档、集合和数据库的层次关系,并对比了MongoDB与传统关系型数据库(如MySQL)的逻辑结构。 ... [详细]
  • MySQL中的日期格式转换函数:STR_TO_DATE与DATE_FORMAT
    本文详细介绍了MySQL中用于日期格式转换的两个重要函数:STR_TO_DATE和DATE_FORMAT。通过具体示例,帮助读者更好地理解和应用这些函数。 ... [详细]
  • 本文深入探讨了MySQL中常见的面试问题,包括事务隔离级别、存储引擎选择、索引结构及优化等关键知识点。通过详细解析,帮助读者在面对BAT等大厂面试时更加从容。 ... [详细]
  • 在尝试从数据库获取设置的过程中,遇到了一个致命错误:Fatal error: Call to a member function bind_param() on boolean。本文将详细分析该错误的原因,并提供解决方案。 ... [详细]
  • 本文详细介绍了如何解压并安装MySQL集群压缩包,创建用户和组,初始化数据库,配置环境变量,并启动相关服务。此外,还提供了详细的命令行操作步骤和常见问题的解决方案。 ... [详细]
  • docker镜像重启_docker怎么启动镜像dock ... [详细]
  • MySQL DATETIME 类型长度及使用指南
    本文详细介绍了 MySQL 中 DATETIME 类型的长度要求及其格式规范,并补充了其他常见数据类型的说明,帮助开发者更好地理解和使用这些类型。 ... [详细]
  • 软件工程课堂测试2
    要做一个简单的保存网页界面,首先用jsp写出保存界面,本次界面比较简单,首先是三个提示语,后面是三个输入框,然 ... [详细]
  • 尝试执行数据库模式加载时遇到错误'Mysql2::Error: 指定的键太长;最大键长度为767字节'。本文将探讨这一问题的成因及解决方案。 ... [详细]
  • 开发笔记:由数据库某字段存数组引发的json_encode/serialize思考
    开发笔记:由数据库某字段存数组引发的json_encode/serialize思考 ... [详细]
  • MySQL Debug 模式的实现与应用
    本文详细介绍了如何启用和使用 MySQL 的调试模式,包括编译选项、环境变量配置以及调试信息的解析。通过实际案例展示了如何利用调试模式解决客户端无法连接服务器的问题。 ... [详细]
author-avatar
哦呦喂酿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有