事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如start transaction和end transaction语句(或函数调用)来界定。
例如:在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序。
事务和程序是两个概念。一般的讲,一个程序中包含多个事务。
事务可以由用户显式控制。事务由事务开始(start transaction)和事务结束(end transaction)之间执行的全体操作组成。
SQL中,定义事物的语句一般有三条:
Start TRANSACTION; // 事务开始标识
COMMIT; // 提交事务的所有操作(将事务所有对数据库的更新写回到磁盘上的物理数据库中去)
ROLLBACK; // 回滚,撤销所有已完成的操作,回到事务开始的状态。
二. 事物存在的意义之一:事务是用来保证数据操作的安全性。
三. 事物的ACID特性:
事务应该具有4个属性:原子性、一致性、隔离性、持续性。这四个属性通常称为ACID特性。
原子性(atomicity[ˌætəm'ɪsɪti])。事物是数据库的逻辑工作单位,一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
一致性(consistency)。事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。(当前事务必须成功执行)当数据库只包含成功事物提交的结果时,就说数据库处于一致性状态。 一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务的内部操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。持久化。
事物的ACID特性遭到破坏的因素有:
(1)多个事务并行执行时,不同的事务操作交叉执行;
(2)事务在执行过程中被强行停止;
alter table testalt Engine=InnoDB; # 5.5 版本以后默认引擎 InnoDB
alter table testalt type=InnoDB; # 5.5 之前的版本
begin / start transaction 开始一个事物
commit 提交事务的改变到数据库
rollback 取消操作,回滚
savepoint name 保存,部分取消,部分提交(打标签)
mysql> select * from Student;
+--------+------------------+------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+--------+------------------+------+------+-------+
| 312001 | Ken | fe | 19 | CS |
| 312019 | Linus Travlds | ma | 55 | Linux |
| 312028 | Taylor | fe | 21 | ELE |
| 312088 | Coohx | male | 21 | Linux |
| 31223 | Richard | ma | 23 | IS |
| 319889 | Richard Stallman | male | 88 | Linux |
| 319999 | Dernis Ritchie | male | 88 | Linux |
+--------+------------------+------+------+-------+
7 rows in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> delete from Student where Sname='Ken';
Query OK, 1 row affected (0.12 sec)
mysql> select * from Student;
+--------+------------------+------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+--------+------------------+------+------+-------+
| 312019 | Linus Travlds | ma | 55 | Linux |
| 312028 | Taylor | fe | 21 | ELE |
| 312088 | Coohx | male | 21 | Linux |
| 31223 | Richard | ma | 23 | IS |
| 319889 | Richard Stallman | male | 88 | Linux |
| 319999 | Dernis Ritchie | male | 88 | Linux |
+--------+------------------+------+------+-------+
6 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from Student;
+--------+------------------+------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+--------+------------------+------+------+-------+
| 312001 | Ken | fe | 19 | CS |
| 312019 | Linus Travlds | ma | 55 | Linux |
| 312028 | Taylor | fe | 21 | ELE |
| 312088 | Coohx | male | 21 | Linux |
| 31223 | Richard | ma | 23 | IS |
| 319889 | Richard Stallman | male | 88 | Linux |
| 319999 | Dernis Ritchie | male | 88 | Linux |
+--------+------------------+------+------+-------+
7 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from Student;
+--------+------------------+------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+--------+------------------+------+------+-------+
| 312001 | Ken | fe | 19 | CS |
| 312019 | Linus Travlds | ma | 55 | Linux |
| 312028 | Taylor | fe | 21 | ELE |
| 312088 | Coohx | male | 21 | Linux |
| 31223 | Richard | ma | 23 | IS |
| 319889 | Richard Stallman | male | 88 | Linux |
| 319999 | Dernis Ritchie | male | 88 | Linux |
+--------+------------------+------+------+-------+
7 rows in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into Student values('311010','IEEE','organization','88','Linux');
Query OK, 1 row affected, 1 warning (0.04 sec)
mysql> select * from Student;
+--------+------------------+------------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+--------+------------------+------------+------+-------+
| 311010 | IEEE | organizati | 88 | Linux |
| 312001 | Ken | fe | 19 | CS |
| 312019 | Linus Travlds | ma | 55 | Linux |
| 312028 | Taylor | fe | 21 | ELE |
| 312088 | Coohx | male | 21 | Linux |
| 31223 | Richard | ma | 23 | IS |
| 319889 | Richard Stallman | male | 88 | Linux |
| 319999 | Dernis Ritchie | male | 88 | Linux |
+--------+------------------+------------+------+-------+
8 rows in set (0.01 sec)
mysql> savepoint p1;
Query OK, 0 rows affected (0.00 sec)
mysql> delete from Student where Sname='Ken';
Query OK, 1 row affected (0.00 sec)
mysql> select * from Student;
+--------+------------------+------------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+--------+------------------+------------+------+-------+
| 311010 | IEEE | organizati | 88 | Linux |
| 312019 | Linus Travlds | ma | 55 | Linux |
| 312028 | Taylor | fe | 21 | ELE |
| 312088 | Coohx | male | 21 | Linux |
| 31223 | Richard | ma | 23 | IS |
| 319889 | Richard Stallman | male | 88 | Linux |
| 319999 | Dernis Ritchie | male | 88 | Linux |
+--------+------------------+------------+------+-------+
7 rows in set (0.00 sec)
mysql> rollback to savepoint p1;
Query OK, 0 rows affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from Student;
+--------+------------------+------------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+--------+------------------+------------+------+-------+
| 311010 | IEEE | organizati | 88 | Linux |
| 312001 | Ken | fe | 19 | CS |
| 312019 | Linus Travlds | ma | 55 | Linux |
| 312028 | Taylor | fe | 21 | ELE |
| 312088 | Coohx | male | 21 | Linux |
| 31223 | Richard | ma | 23 | IS |
| 319889 | Richard Stallman | male | 88 | Linux |
| 319999 | Dernis Ritchie | male | 88 | Linux |
+--------+------------------+------------+------+-------+
8 rows in set (0.00 sec)
六. 在mysql中怎样开始或回滚一个事务呢?
1、显示开始,START TRANSACTION WITH CONSISTENT SNAPSHOT; BEGIN;
2、隐士开始,BEGIN / START TRANSACTION / SET AUTOCOMMIT = 1 / 其他非事务语句(DDL/DCL)会导致之前没提交的事务提交。
3、显示回滚,ROLLBACK
4、隐士回滚,连接断开 / 超时
在修改事务隔离级别时需要注意set global xxx;这时只针对新连接生效,当前连接不生效(如果有连接池的话就麻烦了,连接池都是长连接)
mysql> set global transaction isolation level read committed; Query OK, 0 rows affected (0.00 sec) mysql> show session variables like "tx_isolation"; +---------------+-----------------+| Variable_name | Value |+---------------+-----------------+| tx_isolation | REPEATABLE-READ |+---------------+-----------------+1 row in set (0.00 sec)
在修改事物隔离级别时不加global和session,下一个事物才开始生效。set session xxx;直接生效。(mysql中默认是RR隔离级别)
七. 并发控制带来的数据不一致性
如果没有事物控制的话,那么并发读写数据库会有什么隐患?
脏读:一个事物按相同的查询条件读取以前检索过的数据,却发现其他事物更新后达到了满足其查询条件的旧数据(此时它还未提交),这种现象称为“脏读”
不可重复读:一个事物按相同的查询条件读取以前检索过的数据,却发现其他事物更新后达到了满足其查询条件的旧数据(此时它已被提交),这种现象称为“不可重复读”
幻读:一个事物按相同的查询条件读取以前检索过的数据,却发现其他事物插入了满足其查询条件的新数据(此时它已被提交),这种现象称为“幻读”
脏读,在一个事物里面读取到其他事物未提交的数据:
R()代表读数据; W()代表写数据;
不可重复读,在一个事物里面读取到其他事物已经提交的修改的数据:
幻读,在一个事物里面读取到其他事物已经提交的新增的数据:
八. 封锁协议(隔离级别)
mysql的4种隔离级别:
1. read uncommitted 隔离级别最低,允许脏读,允许事物查看其它事物所进行的未提交更改(读未提交时,读事务直接读取主记录,无论更新事务是否完成)
2.read committed 允许幻读,允许事物查看其它事物所进行的已提交更改(读提交时,读事务每次都读取undo log中最近的版本,因此两次对同一字段的读可能读到不同的数据(幻读),但能保证每次都读到最新的数据。)
3.repeatable read 消除了脏读、不可重复读、幻读保证了事物一致性(每次都读取指定的版本,这样保证不会产生幻读,但可能读不到最新的数据)
4.serializable 串行化读,每次读都需要获得表级别共享锁,读写间互相都会阻塞(锁表,读写相互阻塞,使用较少。)
例如有t1表:
mysql> show create table t1\G *************************** 1. row ***************************Table: t1 Create Table: CREATE TABLE `t1` ( `c1` int(11) NOT NULL, `c2` int(11) DEFAULT NULL, `c3` int(11) DEFAULT NULL, PRIMARY KEY (`c1`), KEY `c2` (`c2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) mysql> select * from t1; +----+------+------+| c1 | c2 | c3 |+----+------+------+| 0 | 0 | 0 || 1 | 1 | 1 || 2 | 2 | 2 || 3 | 3 | 3 |+----+------+------+4 rows in set (0.00 sec)
1.读未提交,read uncommitted,RU
2. 读已提交,read committed,RC
3. RC下的幻读:(此处加for update 表示即便是 RC隔离级别,但是可以读取当前数据的最新版本)
4. 可重复读,repeatable read,RR
5. 在RR隔离级别下,没有幻读。
innodb在RR级别下引入了gap lock(间隙锁)来解决幻读,例如:select * from t1 where c2 =2 for update;这样会将索引记录上面1-3(不包括1和3)这个范围的gap全锁定来避免幻读:
6. 串行,serializable
可通过innodb_lock_waits查看发生锁等待信息:
mysql> select * from information_schema.innodb_lock_waits; +-------------------+------------------------+-----------------+------------------+| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |+-------------------+------------------------+-----------------+------------------+| 421424334461664 | 421424334461664:31:3:4 | 4910 | 4910:31:3:4 |+-------------------+------------------------+-----------------+------------------+1 row in set, 1 warning (0.00 sec)
最后我们再说下 InnoDB semi-consitent read,innodb的 半一致性读,在innodb特殊实现的机制,主要目的是提升并发效率。update语句如果读到一行已经加锁的记录,此时InnoDB返回该记录最新版本,再次判断此版本是否满足update的where条件,若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)
这种情况只发生在RC或 RR+innodb_locks_susafe_for_binlog=1环境下,所以说 RR+innodb_locks_susafe_for_binlog=1时存在幻读可能。
可以这样理解:假设这个表有10万行数据,对一个表进行更新,没有索引,正常情况下会把所有数据全部锁定,但是另一个会话想要对这个表update 更新,这个update 可能只影响两行数据,这时候这个update可以被执行,不受全表锁限制,仅仅只针对update生效,其他SQL不会生效。