MySQL中的事务
为什么需要事务
现在的软件基本上都是多用户、多程序、多线程的,对同一个表可能同时有很多人在用,为保持数据的一致性,所以提出了事务的概念。
一个事务一般包含多个操作,这些操作必须当成一个整体来执行,要么都成功,要么都失败,不允许部分成功和部分失败。假如要从A的账户给B的账户转账1000元,那么A的账户减少1000元,B的账户就要增加1000元,这两个update语句必须作为一个整体来执行,不然可能会导致A的账户扣钱了,但是B的账户没有加钱这种情况,这样就会导致数据不一致。
什么存储引擎支持事务
查看数据库中的哪些存储引擎支持事务?只有InnoDB支持事务。
1mysql> show engines; 2+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ 3| Engine | Support | Comment | Transactions | XA | Savepoints | 4+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ 5| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO | 6| CSV | YES | CSV storage engine | NO | NO | NO | 7| MyISAM | YES | MyISAM storage engine | NO | NO | NO | 8| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO | 9| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES | 10| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO | 11| ARCHIVE | YES | Archive storage engine | NO | NO | NO | 12| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO | 13| FEDERATED | YES | Federated MySQL storage engine | NO | NO | NO | 14+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
查看mysql当前默认的存储引擎?
1mysql> show variables like '%storage_engine%'; 2+----------------------------------+--------+ 3| Variable_name | Value | 4+----------------------------------+--------+ 5| default_storage_engine | InnoDB | 6| default_tmp_storage_engine | InnoDB | 7| disabled_storage_engines | | 8| internal_tmp_disk_storage_engine | InnoDB | 9+----------------------------------+--------+
查看某张表的存储引擎?
1mysql> show create table emp; 2+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 3| Table | Create Table | 4+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 5| emp | CREATE TABLE `emp` ( 6 `empno` int(4) NOT NULL, 7 `ename` varchar(10) DEFAULT NULL, 8 `job` varchar(9) DEFAULT NULL, 9 `mgr` int(4) DEFAULT NULL, 10 `hiredate` date DEFAULT NULL, 11 `sal` float(7,2) DEFAULT NULL, 12 `comm` float(7,2) DEFAULT NULL, 13 `deptno` int(2) DEFAULT NULL, 14 PRIMARY KEY (`empno`), 15 KEY `fk_deptno` (`deptno`), 16 CONSTRAINT `fk_deptno` FOREIGN KEY (`deptno`) REFERENCES `dept` (`deptno`) 17) ENGINE=InnoDB DEFAULT CHARSET=utf8 | 18+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 19
修改表的存储引擎?
mysql> alter table testmyisam engine=innodb;
事务的特性
事务应该具有4个特性:
-
原子性
-
一致性
-
隔离性
-
持久性
这四个特性通常称为ACID特性。
原子性
原子性(Atomicity):一个事务必须被视为一个不可分割的最小单元,整个事务中的所有操作要么全部提交成功,要么全部失败,对于一个事务来说,不可能只执行其中的一部分操作。
例如上面的转账例子中,A的账户减少1000元和B的账户增加1000元这两个操作要么全部成功,要么全部失败,不能出现A的账户扣钱了,但是B的账户没有加钱这种情况。
一致性
一致性(Consistency):指事务将数据库从一种一致性转换到另外一种一致性状态,在事务开始之前和事务结束之后数据库中数据的完整性没有被破坏。
例如上面的转账例子中,A的账户减少了1000元,那么B的账户就要增加1000元,不能出现B的账户增加1500的情况,一致性与原子性是密切相关的。
持久性
持久性(Durability)也称永久性(permanence),指一个事务一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,已经提交的修改数据也不会丢失。
隔离性
隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
事务的隔离级别:
事务隔离级别
脏读
不可重复读
幻读
读未提交
是
是
是
不可重复读
否
是
是
可重复读
否
否
是
串行化
否
否
否
未提交读
未提交读(READ UNCOMMITED)也称为读未提交:事务中的修改,即使没有提交,对其它事务也是可见的。
脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
mysql的默认隔离级别为REPEATABLE-READ,可以使用以下命令查看:
1mysql> show variables like '%tx_isolation%'; 2+---------------+-----------------+ 3| Variable_name | Value | 4+---------------+-----------------+ 5| tx_isolation | REPEATABLE-READ | 6+---------------+-----------------+
脏读的演示:
session1
session2
set session transaction isolation level read uncommitted;
set session transaction isolation level read uncommitted;
begin;
update account set balance=balance-50 where id=1;
select balance from account where id=1; – 950
rollback;
select balance from account where id=1; – 1000
session2中读取到了session1中已经修改但是还没提交的数据,这就是脏读。
已提交读
已提交读 (READ COMMITED):一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据做了更新并提交,导致事务A多次读取同一数据时结果不一致。
不可重复读的演示:
session1
session2
set session transaction isolation level read committed;
set session transaction isolation level read committed;
begin;
begin;
update account set balance=balance-50 where id=1;
select balance from account where id=1; – 1000
commit;
select balance from account where id=1; – 950
可重复读
可重复读(REPEATABLE READ):保证在同一个事务中多次读取同样数据的结果是一样的。
幻读:事务A将数据库中所有数据都删除的时候,但是事务B就在这个时候新插入了一条记录,当事务A删除结束后发现还有一条数据,就好像发生了幻觉一样。这就叫幻读。
在mysql中无法演示幻读,因为mysql的隔离级别REPEATABLE READ使用间隙锁已经解决了幻读。
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
串行化
串行化(SERIALIZABLE):强制让事务串行执行,就是把表锁住。
串行化的演示:
session1
session2
set session transaction isolation level serializable;
set session transaction isolation level serializable;
begin;
begin;
select count(*) from account; – 1
select count(*) from account; --1
insert into account values(0, 500); – 阻塞
commit;
插入成功
总结:
-
事务隔离级别为可重复读时,如果有索引(包括主键索引)的时候,以索引列为条件更新数据,会存在间隙锁间、行锁、页锁的问题,从而锁住一些行;如果没有索引,更新数据时会锁住整张表。
-
事务隔离级别为串行化时,读写数据都会锁住整张表。
-
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大,对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。
事务的语法
开启事务
- begin
- start transaction
- begin work
事务回滚
- rollback
事务提交
- commit
还原点
-
savepoint
mysql> show variables like '%autocommit%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ mysql> create table test_demo(a int, b int, c int); mysql> set autocommit=0; mysql> insert into test_demo values(5,5,5); mysql> savepoint s1; mysql> insert into test_demo values(6,6,6); mysql> savepoint s2; mysql> insert into test_demo values(7,7,7); mysql> savepoint s3; mysql> select * from test_demo; +------+------+------+ | a | b | c | +------+------+------+ | 5 | 5 | 5 | | 6 | 6 | 6 | | 7 | 7 | 7 | +------+------+------+ mysql> rollback to savepoint s2; mysql> select * from test_demo; +------+------+------+ | a | b | c | +------+------+------+ | 5 | 5 | 5 | | 6 | 6 | 6 | +------+------+------+ mysql> rollback;
更多精彩内容关注本人公众号:架构师升级之路
