1、LOCK TABLE 和 UNLOCK TABLEmysql
lock tables 锁定当前线性表。若是表被其余线程锁定,则当前线程会等待,直到能够获取全部锁定为止。sql
unlock tables 释放当前线程得到的任何锁定。当前线程执行另外一个 LOCK TABLES 时, 或当与服务器的链接被关闭时,全部由当前线程锁定的表被隐含地解锁 数据库
一个得到表锁和释放表锁的简单例子服务器
session_1 | session_2 |
得到表film_text的Read锁定session lock table film_text read分布式 |
|
当前session能够查询该表纪录spa select id,title from film_text where id = 1;线程 +-------+-----------orm id. title事务 +--------+---------- 1 标签 |
其余session也能够查询该表的纪录 select id,title from film_text where id = 1; +-------+----------- id. title +--------+---------- 1 标签
|
其余session更新锁定表会等待得到锁 update film_text set title='lalamei' where id=1; ......等待 |
|
释放锁:unlock tables | ......等待 |
session 得到锁,更新操做完成 完成 |
2、事务控制
默认状况下,MySQL 是自动提交(Autocommit)的,若是须要经过明确的 Commit 和 Rollback 来提交和回滚事务,那么须要经过明确的事务控制命令来开始事务 。
* start transaction 或 begin语句能够开始一项新的事务
* commit 和 rollback 用来提交或者回滚事务
* chain 和 release 字句分别用来定义在事务提交或者回滚以后的操做,chain会当即启动一个新事物,而且和刚才的事物具备相同的隔离级别,release则会断开和客户端链接
* set autocommit 能够修改当前链接的提交方式,若是设置了set autocommit=0,则设置以后的全部事务都须要经过明确的命令进行提交或者回滚
演示使用start transaction 开始的事务在提交后自动回到自动提交的方式;若是在提交的时候使用commit and chain,那么会在提交后当即开启一个新的事务。
start transaction和commit and chain 的使用例子
session_1 | session_2 |
从表actor中查询actor_id=201的记录,结果为空: mysql> select * from actor where actor_id = 201; Empty set (0.00 sec) |
从表actor中查询actor_id=201的记录,结果为空: mysql> select * from actor where actor_id = 201; Empty set (0.00 sec) |
用start transaction 命令启动一个事务,往表actor中插入一条纪录,没有commit: mysql> start transaction; Query OK, 0 rows affected (0.00 sec)
mysql> insert into actor(actor_id,first_name,last_name) values(201,'lisa','tom'); Query OK, 1 row affected (0.00 sec) |
查询表actor,结果仍然为空: mysql> select * from actor where actor_id=201; Empty set (0.00 sec)
|
执行提交: mysql> commit; Query OK, 0 rows affected (0.00 sec) |
|
再次查询表actor,能够查询到结果: mysql> select * from actor where actor_id=201; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 201 | lisa | tom | +----------+------------+-----------+ 1 row in set (0.00 sec) |
|
这个事务是按照自动提交执行的: mysql> insert into actor(actor_id,first_name,last_name) values(202,'lisa','lan'); Query OK, 1 row affected (0.01 sec) |
|
能够从actor表中查询到session1刚刚插入的数据。 mysql> select * from actor where actor_id in (201,202); +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 201 | lisa | tom | | 202 | lisa | lan | +----------+------------+-----------+ 2 rows in set (0.00 sec) |
|
从新用start transaction 启动一个事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec)
往表actor中插入一条纪录: mysql> insert into actor(actor_id,first_name,last_name) values(203,'lisa','tt'); Query OK, 1 row affected (0.00 sec)
用commit and chain命令提交: mysql> commit and chain; Query OK, 0 rows affected (0.00 sec)
此时自动开始一个新的事务: mysql> insert into actor (actor_id,first_name,last_name) values(204,'Lisa','Mou'); Query OK, 1 row affected (0.00 sec) |
|
session1刚插入的纪录没法查看: mysql> select * from actor where first_name = 'lisa'; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 201 | lisa | tom | | 202 | lisa | lan | | 203 | lisa | tt | +----------+------------+-----------+ 3 rows in set (0.00 sec) |
|
用commit命令提交: mysql> commit; Query OK, 0 rows affected (0.01 sec) |
|
session1插入的新记录能够看到: mysql> select * from actor where first_name = 'lisa'; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 201 | lisa | tom | | 202 | lisa | lan | | 203 | lisa | tt | | 204 | lisa | mou | +----------+------------+-----------+ 4 rows in set (0.00 sec) |
若是在锁表期间,用 start sransaction 命令开始一个新事务,会形成一个隐含的unlock tables 被执行
start transaction 致使的 unlock tables
session_1 | session_2 |
从表actor中查询actor_id=301的记录,结果为空: mysql> select * from actor where actor_id = 301; Empty set (0.00 sec) |
从表actor中查询actor_id=301的记录,结果为空: mysql> select * from actor where actor_id = 301; Empty set (0.00 sec) |
对表actor加写锁: mysql> lock table actor write; Query OK, 0 rows affected (0.00 sec) |
|
对表 actor 的读写操做被阻塞: mysql> select * from actor where actor_id=301; ....等待 |
|
插入一条记录 mysql> insert into actor(actor_id,first_name,last_name) values(301,'Lisa','Tom'); Query OK, 1 row affected (0.00 sec) |
....等待 |
回滚刚才的纪录: mysql> rollback; Query OK, 0 rows affected (0.00 sec) |
....等待 |
用start transaction 命令从新开始一个事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) |
....等待 |
session1 开始一个事务时,表锁被释放,能够查询: mysql> select * from actor where actor_id=301; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 301 | Lisa | Tom | +----------+------------+-----------+ 1 row in set (14 min 58.07 sec) 对lock方式加的表锁,不能经过rollback进行回滚。 |
/*
* 一、commit、rollback只能对事务类型的表进行提交和回滚
* 二、一般对提交的事务纪录到二进制文件中
* 三、全部的DDL语句是不能回滚的,部分DDL语句会形成隐式提交
*/
在事务中能够经过定义 SAVEPOINT,指定回滚事务的一个部分,可是不能指定提交事务 的一个部分。对于复杂的应用,能够定义多个不一样的 SAVEPOINT,知足不一样的条件时,回滚 不一样的 SAVEPOINT。须要注意的是,若是定义了相同名字的 SAVEPOINT,则后面定义的 SAVEPOINT 会覆盖以前的定义。对于再也不须要使用的 SAVEPOINT,能够经过 RELEASE SAVEPOINT 命令删除 SAVEPOINT,删除后的 SAVEPOINT,不能再执行 ROLLBACK TO SAVEPOINT 命令。
模拟回滚事务
session_1 | session_2 |
从表actor中查询 first_name='Simon' 的纪录, 结果为空: mysql> select * from actor where first_name = 'Simon'; Empty set (0.00 sec) |
从表actor中查询 first_name='Simon' 的纪录, 结果为空: mysql> select * from actor where first_name = 'Simon'; Empty set (0.00 sec) |
启动一个事务,往表 actor 中插入一条纪录; mysql> start transaction; Query OK, 0 rows affected (0.00 sec)
mysql> insert into actor(actor_id,first_name,last_name) values(501,'Simon','Tom'); Query OK, 1 row affected (0.00 sec) |
|
能够查询到刚刚插入的纪录: mysql> select * from actor where first_name = 'Simon'; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 501 | Simon | Tom | +----------+------------+-----------+ 1 row in set (0.00 sec)
|
没法从actor表中查询到session1刚插入的纪录: mysql> select * from actor where first_name = 'Simon'; Empty set (0.00 sec)
|
定义 savepoint,名称为test; mysql> savepoint test; Query OK, 0 rows affected (0.00 sec)
继续插入一条纪录: mysql> insert into actor(actor_id,first_name,last_name) values(502,'Simon','Cof'); Query OK, 1 row affected (0.00 sec) |
|
能够查询到两条纪录 mysql> select * from actor where first_name = 'Simon'; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 501 | Simon | Tom | | 502 | Simon | Cof | +----------+------------+-----------+ 2 rows in set (0.00 sec) |
仍然没法查询到结果: mysql> select * from actor where first_name = 'Simon'; Empty set (0.00 sec)
|
回滚到刚才定义的savepoint: mysql> rollback to savepoint test; Query OK, 0 rows affected (0.00 sec) |
|
只能从表 actor 中查询到第一条记录,由于第二条记录已经回滚: mysql> select * from actor where first_name = 'Simon'; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 501 | Simon | Tom | +----------+------------+-----------+ 1 row in set (0.00 sec) |
仍然没法查询到结果: mysql> select * from actor where first_name = 'Simon'; Empty set (0.00 sec) |
用commit命令提交: mysql> commit; Query OK, 0 rows affected (0.00 sec) |
|
只能从表 actor 中查询到第一条记录: mysql> select * from actor where first_name = 'Simon'; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 501 | Simon | Tom | +----------+------------+-----------+ 1 row in set (0.00 sec) |
只能从表 actor 中查询到 session1 插入的第一条记录: mysql> select * from actor where first_name = 'Simon'; +----------+------------+-----------+ | actor_id | first_name | last_name | +----------+------------+-----------+ | 501 | Simon | Tom | +----------+------------+-----------+ 1 row in set (0.00 sec) |
3、分布式事务的使用
简介
一、分布式事务只支持innoDB存储引擎
二、一个分布式事务会涉及多个行动,这些行动自己是事务性的。全部行动都必须一块儿成功完成,或者一块儿被回滚
分布式事务的原理
分布式事务语法
XA {START|BEGIN} xid [JOIN|RESUMF]
XA START xid 用于启动一个带给xid值的XA事务。每一个XA事务必须有一个惟一的xid值,所以该值当前不能被其余的XA事务使用。
xid是一个XA事务标识符,用来惟一标识一个分布式事务。xid值由客服端提供,或由mysql服务器生成。
xid值包含1~3个部分
xid : gtrid [, bqual [, formatId ] ]
* gtrid 是一个分布式事务标识符,相同的分布式事务应该使用相同的gtrid,这样能够明确知道xa事务属于那个分布式事务。
* bqual 是一个分支限定符,默认值是空串。对于一个分布式事务中的每一个分支事务,bqual值必须是惟一的。
* formatID 是一个数字,用于标识由gtrid 和 bqual 值使用的格式,默认值是1。
下面其余 XA 语法中用到的 xid 值,都必须和 START 操做使用的 xid 值相同,也就是表示 对这个启动的 XA 事务进行操做。
XA PREPARE xid
使事务进入 PREPARE 状态,也就是两阶段提交的第一个提交阶段
这两个命令用来提交或者回滚具体的分支事务。也就是两阶段提交的第二个提交阶段, 分支事务被实际的提交或者回滚。
XA RECOVER
XA RECOVER返回当前数据库中处于PREPARE 状态的分支事务的详细信息。
分布式的关键在于?
如何确保分布式事务的完整性,
在某个分支出现问题时的故障解决。
演示了一个简单的分布式事务的执行,事务的内容是在 DB1 中插入一条记录, 同时在 DB2 中更新一条记录,两个操做做为同一事务提交或者回滚。
分布式事务例子
session_1 in DB1 | session_2 in DB2 |
在数据库 DB1 中启动一个分布式事务的一个分支事务,xid 的 gtrid 为test,bqual 为 db1: mysql> xa start 'test','db1'; Query OK, 0 rows affected (0.00 sec)
分支事务1在表 actor 中插入一条纪录: mysql> insert into actor(actor_id,first_name,last_name) values(601,'Simon','Tom'); Query OK, 1 row affected (0.00 sec)
对分支事务1进行第一阶段提交,进入 prepare 状态: mysql> xa end 'test','db1'; Query OK, 0 rows affected (0.00 sec)
mysql> xa prepare 'test','db1'; Query OK, 0 rows affected (0.00 sec) |
在数据库DB2中启动分布式事务test的另外一个分支事务,xid 的 gtrid 为 test, bqual 为 db2: mysql> xa start 'test','db2'; Query OK, 0 rows affected (0.00 sec)
分支事务 2 在表 film_actor 中更新了21条记录: mysql> update film_actor set last_update=now() where actor_id=178; Query OK, 21 rows affected (0.00 sec) Rows matched: 21 Changed: 21 Warnings: 0
对分支事务 2 进行第一阶段提交,进入 prepare 状态: mysql> xa end 'test','db2'; Query OK, 0 rows affected (0.00 sec)
mysql> xa prepare 'test','db2'; Query OK, 0 rows affected (0.01 sec) |
用 xa recover 命令查看当前分支事务状态: mysql> xa recover \G; *************************** *************************** formatID: 1 gtrid_length: 4 bqual_length: 3 data: testdb1 1 rows in set (0.00 sec) |
用 xa recover 命令查看当前分支事务状态: mysql> xa recover \G; *************************** *************************** formatID: 1 gtrid_length: 4 bqual_length: 3 data: testdb2 1 rows in set (0.00 sec) |
两个事务都进入准备提交阶段,若是以前遇到任何错误,都应该回滚全部的分支,以确保分布事务的正确。 | |
提交分支事务 1: mysql> xa commit 'test','db1'; Query OK, 0 rows affected (0.00 sec) |
提交分支事务2: mysql> xa commit 'test','db2'; Query OK, 0 rows affected (0.00 sec)
|
两个事务都到达准备提交阶段后,一旦开始进行提交操做,就须要确保所有的分支都提交成功。 |
存在的问题1
条件:
分支事务在达到prepare状态时,数据库异常从新启动,重启之后能够继续执行事务进行提交回滚的操做。
问题:
提交事务没有写binlog,存在隐患,可能致使使用binlog恢复丢失部分数据。若是存在复制的数据库,则有可能致使主从数据库的数据不一致。
存在问题二
若是分支事务的客户端链接异常停止,那么数据库会自动回滚未完成的分支事务,若是 此时分支事务已经执行到 prepare 状态,那么这个分布式事务的其余分支可能已经成功提交, 若是这个分支回滚,可能致使分布式事务的不完整,丢失部分分支事务的内容。