事务就是指逻辑上的一组sql语句操作。组成这组操作的各个sql语句,执行时要么全成功要么全失败。
可以以银行转账来类比事务。比如A转账给B 200块RMB。在数据库中的表现就是:
update user_accout set accout = accout -200 where name = A;
update user_accout set accout = accout+200 where name = B;
以上sql在事务中的操作要么全成功,要么全失败。这就是事务的原子性。
mysql5.5.5 支持事务的引擎: innodb/ndb
事务的四大特性:ACID;
A--Atomicity 原子性。
C--Consistency 一致性。事务发生前和发生后,数据的完整性必须保持一致。
I--Isolation 隔离性。隔离性的四个等级。当并发访问数据库时,一个正在执行的事务在执行完毕前,对于其他的会话是不可见的,多个并发事务之间的数据是相互隔离的。假如: mysqldump --single-transaction
D--Durability 持久性。一个事务一旦被提交,它对数据库中的数据改变就是永久性的。如果出了错误,事务也不允许撤销,只能通过“补偿性事务。”
mysql的事务是自动提交的。也就是发一条sql它就执行一条。如果想多条sql放在一个事务中执行。则需要使用事务进行处理。当我们开启一个事务,并且没有提交,mysql会自动回滚事务。或者我们使用rollback手动回滚事务。
start transaction
rollback
commit
set autocommit = 1 开启自动提交
具体的示例如下:
mysql> use test;
mysql> create table `student`
-> (`id` int(11) NOT NULL AUTO_INCREMENT,
-> `name` varchar(8) DEFAULT NULL,
-> PRIMARY KEY(`ID`)
-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.20 sec)
mysql> insert into student(`name`) values(1);
mysql> select * from student;
+----+------+
| id | name |
+----+------+
| 1 | 3 |
+----+------+
1 row in set (0.00 sec)
mysql> start transaction;
mysql> insert into student(`name`) values(1);
mysql> insert into student(`name`) values(2);
mysql> select * from student;
+----+------+
| id | name |
+----+------+
| 1 | 3 |
| 9 | 1 |
| 11 | 2 |
+----+------+
3 rows in set (0.00 sec)
mysql> quit
Bye
mysql>use test;
#由于上一次事务没有提交手动退出,数据没有写入表。
mysql> select * from student;
+----+------+
| id | name |
+----+------+
| 1 | 3 |
+----+------+
1 row in set (0.00 sec)
补充:
SQL标准规定了四个隔离水平:
- READ_UNCOMMITTED
- READ_COMMITTED
- REPETABLE_READ
- SERIALIZABLE
隔离级别 | 脏读 | 非重复读 | Phantom read |
---|---|---|---|
READ_UNCOMMITTED | allowed | allowed | allowed |
READ_COMMITTED | prevented | allowed | allowed |
REPETABLE_READ | prevented | prevented | allowed |
SERIALIZABLE | prevented | prevented | prevented |
脏读
脏读发生在:当一个事务允许读取一个被其他事务改变但是未提交的状态时,这是因为并没有锁阻止读取,如上图,你看到第二个事务读取了一个并不一致的值,不一致的意思是,这个值是无效的,因为修改这个值的第一个事务已经回滚,也就是说,第一个事务修改了这个值,但是未提交确认,却被第二个事务读取,第一个事务又放弃修改,悔棋了,而第二个事务就得到一个脏数据。
非重复读
反复读同一个数据却得到不同的结果,这是因为在反复几次读取的过程中,数据被修改了,这就导致我们使用了stale数据,这可以通过一个共享读锁来避免。这是隔离级别READ_COMMITTED会导致可重复读的原因。设置共享读锁也就是隔离级别提高到REPETABLE_READ。
Phantom 读
当第二个事务插入一行记录,而正好之前第一个事务查询了应该包含这个新纪录的数据,那么这个查询事务的结果里肯定没有包含这个刚刚新插入的数据,这时幻影读发生了,通过变化锁和predicate locking避免。
下图是主流数据库的默认隔离级别:
Database | Default isolation Level |
---|---|
Oracle | READ_COMMITTED |
MySQL | REPETABLE_READ(通过查看tx_isolation变量对应的值即可) |
Microsoft SQL Server | READ_COMMITTED |
PostgreSQL | READ_COMMITTED |
DB2 | CURSOR STABILITY (a.k.a READ_COMMITTED) |
补充:
1、事务隔离级别为读提交时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时,读写数据都会锁住整张表
4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
5、MYSQL MVCC实现机制参考链接:https://blog.csdn.net/whoamiyang/article/details/51901888
6、关于mysql锁机制的解读:https://blog.csdn.net/mysteryhaohao/article/details/51669741
参考:
1.https://www.jdon.com/concurrent/acid-database.html
2.mysql5.1手册
3.老男孩视频。
4.https://www.cnblogs.com/huanongying/p/7021555.html