MySQL的引擎
MySQL其实是分为server层和引擎层。
server层包括:连接器、分析器、优化器、执行器、以及查询缓存。在这里执行的一些MySQL自己的一些逻辑,比如函数、存储过程、视图、触发器,但是还没有真正的去数据文件中读取数据。
引擎层:InnoDB、MyISAM、Memory 负责数据的查询和提取。
现在几乎都用的InnoDB,因为只有它支持事物,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。
设置引擎,在建表语句结尾设置 ENGINE=InnoDB
事物隔离级别
查询当前的事物隔离级别:show variables like 'transaction_isolation';
读未提交:效率最高,最不安全的级别,会读到别的事物未提交的数据。 会出现:脏读、不可重复读、幻读。
读提交:读取别的事物提交的数据,MySQL默认是这种隔离级别。 会出现:不可重复读、幻读。
可重复读:事物开启,那么我无论读一张表几次,中间哪怕被别的事物插入新的数据或者删除数据,我当前事物中也是看不到的。 会出现:幻读。
串行化:对 一行/多行 数据加上"读"、"写"锁,多个事物之间读锁之间不互斥,"写"-"写","写"-"读"都是互斥的。在执行增删改操作时会先去获取"写"锁,获取到才能继续执行事物。
读未提交
将数据库的事物隔离级别设置为读未提交:SET session transaction isolation level read uncommitted ;
会读到别的事物未提交的数据。
脏读:读到别的事物未提交的数据。
比如A事物执行 INSERT INTO `t` (id,name) VALUES(123,"aaa");
这时B事物执行 SELECT * FROM `t`; 事物B这时看到的是有一条(123,"aaa")的数据。
A事物执行 RollBack;
这时表`t`中是没有这条(123,"aaa")的,所以事物B读到了一条脏数据,如果拿着这条脏数据去做一些业务处理,那可能会导致一整条业务线上都出现这条脏数据。
读提交
将数据库的事物隔离级别设置为读提交:SET session transaction isolation level read committed ;
可解决脏读,只读取别的事物 commit之后的数据。
不可重复读:这种隔离级别会出现"不可重复读",什么是不可重复读?我同一个事物,读一张表两次,两次数据不一样。
设置隔离级别为"读提交",开启一个事物,执行两次SELECT语句,中间被另一个事物更新了这条数据,那么两次SELECT出的结果不一致。
可重复读
将数据库的事物隔离级别设置为可重复读提交:SET session transaction isolation level repeatable read ;
在事物开启后,**第一次执行sql的时候**会根据sql的查询条件创建一个视图,在这个事物当中,一直会用当前视图,数据中间被修改了,也不会影响到视图中的数据。
所以,中间读若干次,也不会导致结果不一致。注意,不是在事物开启时创建视图,而是在执行sql的时候创建视图。
这种隔离级别适用的场景:某一天结束的时候需要对当天的帐进行清算,需要多次查询这张表。
但是如果这时有新的交易记录进来,那么会导致流水不正常,这时可以用到可重复读。
在当天结束的时候开启事物并且执行sql,这时视图生成了,再插入数据也是无影响的。
如果想开启事物时就创建视图,在开启事务时 START TRANSACTION with consistent snapshot ;
可解决脏读、不可重复读。
幻读:虽然看起来可重复读不会读到中途插入/更新/删除的数据,但是这种会有一种幻觉,就是在事物开启时是5条数据,事物结束后是6条数据。
串行化
将数据库的事物隔离级别设置为可重复读提交:SET session transaction isolation level serializable ;
可解决脏读、不可重复读,幻读。
设置隔离级别为串行化,开启事物并执行sql,这时会将这条sql所走的索引的行进行上锁。
如果sql中使用WHERE id = 1;这种条件,另一个事物的insert操作不会被阻塞,因为锁住的是id索引=1的行。
但是如果使用的是 WHERE aaa like "%asdasd";这种不会走索引的条件,默认扫描全表,那么会锁住全表,
这时的另一个事物的insert操作会被阻塞,直到当前事物执行完成。这种效率最低。但是不会出现幻读。
两个事物之间操作的行有交集,且其中一方有写操作,那么会上锁,互斥,直到某个事物执行完,第二个事物继续执行。