MYSQL事务及存储引擎对比
Innodb支持事务,而myisam不支持事务。
事务的定义:
当多个用户访问同一份数据时,一个用户在更改数据的过程中可能有其他用户同时发起更改请求,为保证数据的更新从一个一致性状态变更为另一个一致性状态,这时有必要引入事务的概念。
Mysql提供了多种引擎支持Innodb和BDB。Innodb存储引擎事务主要通过UNDO日志和REDO日志实现,Myisam和memory引擎则不支持事务。下图分别给出三种mysql引擎的区别和特性:
Myisam存储引擎:由于该引擎不支持事务、也不支持外键,所以访问速度很快。因此对事务完整性没有要求并以访问为主的应用适合使用该存储引擎。
Innodb存储引擎:
由于该存储引擎在事务上具有优势,即支持具有提交、回滚和崩溃恢复能力的事务安装,所以比myisam存储引擎占用更多的磁盘空间。因此需要进行频繁的更新、删除操作,同时对事务的完整性要求比较高,需要实现并发控制,此时适合使用该存储引擎。
Memory存储引擎:
该存储引擎使用内存来存储数据,因此该存储引擎的数据访问速度非常快,但是安全上没有保障,(redis也是一个内存存储引擎)。如果应用中涉及数据比较小,需要进行快速访问,则适合使用该存储引擎。
本文主要从以下四个方面来介绍mysql的事务:
- 事务概述
- 事务控制语句
- 事务隔离级别
- innodb锁机制
事务概述:
事务具有ACID四个性质来保证数据库的更新从一个一致性状态到另一个一致性状态:
原子性(atomicity):事务中所有的操作视为一个原子单元,即对事务所进行的数据修改等操作只能是完全提交或者完全回滚。
一致性(consistency):事务在完成时,必须使所有的数据从一种一致性状态变更为另外一种一致性状态,所有的变更都必须应用于事务的修改,以确保数据的完整性。
隔离性(insolation):一个事物中的操作语句所做的修改必须与其他事物所做的修改相隔离。在进行事务查看数据时数据所处的状态,要么是被另一并发事务修改之前的状态,要么是被另一并发事务修改之后的状态,即当前事务不会查看由另外一个并发事务正在修改的数据。这种特性主要由锁机制实现。
持久性(durability):事务完成之后,所做的修改对数据的影响是永久的,即使系统重启或者系统出现故障,数据仍可恢复。
REDO日志和UNDO日志:
REDO日志:
事务执行时需要将执行的事务日志写入到日志文件里,对应的文件为redo日志。当每条sql语句进行数据库更新操作时,首先将redo日志写入到日志缓冲区。当客户端执行commit命令提交时,日志缓冲区的内容将被刷新到磁盘,日志缓冲区的刷新方式或者时间间隔可以通过参数innodb_flush_log_at_trx_commit控制。
REDO日志对应磁盘上的ib_logfileN(ib_logfile0,ib_logfile1)文件,该文件默认为5MB,建议设置为512MB以便容纳较大的事务。在mysql崩溃恢复时会重新执行redo日志中的记录。
UNDO日志:
与redo日志相反,undo日志主要用于事务异常时的数据回滚,具体内容就是复制事务前的数据库内容到undo缓冲区,然后在合适的时间将内容刷新到磁盘。
与redo日志不同的是,磁盘上不存在单独的undo日志文件,所有的undo日志文件均存放在表空间对应的.idb数据文件中,undo日志又被成为回滚段。
Mysql事务控制语句:
START TRANSACTION | BEGIN [WORK] //开启一个事务 COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE] //提交一个事务 ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE] //回滚一个事务 SET AUTOCOMMIT = {0 | 1} //自动提交事务开启或者关闭
查看事务的隔离级别:
show variables like 'tx_isolation';
例子:commit之后才会对表进行插入
回滚之后,表不会有变动
SET AUTOCOMMIT =1;
当自动提交被打开以后,我们rollback也没用,会自动提交。
Mysql事务隔离级别
sql标准定义了四种隔离级别,指出了哪些改变其他事务可见,哪些数据改变其他数据不可见。低级别的隔离级别可以支持更高的并发处理,同时占用的系统资源更少。
查看事务的隔离级别:
show variables like 'tx_isolation';
#未提交读,以下是设置隔离级别语句,更改默认隔离级别,需要重新登录 SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; #提交读 SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; #可重复读 SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; #可串行化 SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
READ-UNCOMMITTED(读取未提交内容):
在该隔离级别,所有事物都可以看到其他未提交事务的执行结果。因为其性能也不必其他级别高很多,因此该隔离级别很少使用。读取未提交的数据被称为脏读。
两个事物在更新前,数据一致,但是左边的事务还未提交更改,右边的事务也读到其更改数据,这种情况称为脏读。
READ_COMMITED(读取提交内容):
这是大多数数据库默认的隔离级别,但并不是mysql默认的隔离级别。其满足了隔离的简单定义:一个事务从开始到提交前所做的任何改变都是不可见的,事务职能看见已经提交的事务所做的改变。
该隔离级别可能导致不可重复读问题,因为同一事务的其他实例在该实例的处理期间可能会有新的数据提交导致数据改变,所以同一个查询可能在不同时刻返回不同结果。
以下例子:A事务虽然没有提交,但是却看到不同数据。同一事务中,两次看到的数据是不一致的。
REPEATABLE-READ(可重读):
这是mysql的默认隔离级别。
该隔离级别能保证同一事务的多个实例在并发读取数据时,会看到同样的数据行,理论上会导致另一问题:幻读。
幻读:例如事务A对一个表中的数据修改涉及到全部的数据行。此时,事务B对该表插入一个数据行。那么出现的问题就是,该表中出现了事务A还没有修改的数据行。
Innodb和falcon存储引擎通过mvcc(多版本控制)机制解决了该问题。
innodb存储引擎的MVCC机制:innodb通过为每个数据行增加两个隐含值的方式来实现。这两个隐含值记录了行的创建时间,及过期时间。每一行存储事件发生时的系统版本号,每一次开始一个新的事物,版本号会自动加1,每个事物都会保存开始时的版本号,每个查询根据事物的版本来查询结果。
Serializable(可串行化):
这是最高的隔离级别,通过强制事物排序,使之不可能相互冲突,从而解决幻读问题。简言之,是在每个读的数据行上加上共享锁实现。在这个级别,可能会导致大量的超时现象和锁竞争,一般不推荐使用。
该隔离级别采用不同的锁类型来实现。
事务的隐式提交:例如alter table会造成隐式事务提交,尽管我们没有提交事务,但是在其他事务中却看到了我们的数据变化。
Innodb的锁机制:
锁的类型:
共享锁:
共享锁又称为S锁,是share的缩写,共享锁的锁粒度是行或者元组(多个行)。一个事务获取了共享锁之后,可以对锁定范围的数据执行读操作。
排它锁:
排它锁的代号是X,是eXclusive的缩写,排它锁的锁粒度也是行或者元组,一个事务获取了排它锁以后,可以对锁定范围内的数据执行写操作。
意向锁:
意向锁是一种表锁,锁的粒度是整张表,分为意向共享锁(IS)和意向排它锁(IX)两类。意向锁表示一个事务有意对数据上共享锁或者排它锁。”有意“表示事务想执行操作,但还没有真正执行。
锁和锁之间要么是相容的,要么是互斥的:
锁粒度:
锁的粒度主要分为行锁和表锁。锁的粒度越小,其耗费的系统资源越多,但是并发性却更好。
表锁的开销最小,同时允许的并发量也是最小的锁机制。Myisam使用该锁机制。
行锁支持最大的并发,innodb存储引擎使用该锁机制,如果支持并发读写,建议采用innodb锁机制。
select语句会加上一个共享锁,如果查找的数据已经被加上排它锁的话,共享锁会等待期结束再加,若等待时间过长就会显示需要等待的锁超时。
insert、update、delete会被加上排它锁。