每个虚线框为一层,总共三层。
第一层,客户端层:为请求做连接处理,授权认证,安全等。
第二层,服务层:查询解析,分析,优化,缓存,提供内建函数;存储过程,触发器,视图。
第三层,存储引擎层,不光做存储和提取数据,而且针对特殊数据引擎还要做事务处理。
读锁(read lock):也称共享锁,读锁是共享的,或者说是相互不阻塞的。多个线程在同时可以读取同一个资源而不相互干扰。
写锁(write lock):也称排他锁,写锁是排他的,也就是说,一个写锁会阻塞其他的写锁和读锁。
锁粒度也称锁的级别,分为表锁和行锁。
锁策略:就是在锁的开销和数据的安全性之间寻求平衡。
表锁(table lock):是MySQL中最基本的锁策略,也是成本最小的锁策略。一个用户对表进行写操作时,会锁定整张表。这时会阻塞其他用户对表的所有读写操作。读锁之间是不相互阻塞的。写锁比读锁有更高的优先级,因此一个写锁请求可能会被插入到读锁队列的前面。(尽管存储引擎可以自己管理锁,但是MySql服务器会为诸如ALTER TABLE之类的语句使用表锁而忽略存储引擎的锁机制)
行级锁(row lock):行级锁可以最大程度的地支持并发处理,同时也带来了最大的锁开销。行级锁只会在存储引擎层实现。
死锁是指两个或者多个事务在同一个资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,可能会造成死锁。多个事务同时锁定同一个资源,也会产生死锁。
如果凑巧,两个事务都执行了第一条更新语句,并且都锁定了这一行数据,接着两个事务试着去执行第二行语句时,却发现该行数据已经被对方锁定,同时自身又持有对方的锁,出现死锁现象。
解决办法:
锁的行为和顺序与存储引擎相关。死锁产生的原因有两种,一种是真正的数据冲突,一种是由于存储引擎的实现方式导致的。
事务(transaction):是一组原子性的SQL查询,或者说是一个独立的工作单元。事务内的语句,要么就全部执行,要么就全都不执行。
事务特性(ACID):
数据库事务的隔离级别有4种,由低到高分别为未提交读(Read uncommitted )、提交读(Read committed )、重复读(Repeatable read )、序列化(Serializable) 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读(两次执行一样查询,得到不同的结果)。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
什么时候会出现幻读?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
幻读(phantom Read):指的是一个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(phantom row)
InnoDB存储引擎通过Next-Key Locking解决了幻读的问题
隔离级别 | 读数据一致性 | 脏读 | 不可重复 读 | 幻读 |
---|---|---|---|---|
未提交读(Read uncommitted) | 最低级别 | 是 | 是 | 是 |
已提交读(Read committed) | 语句级 | 否 | 是 | 是 |
可重复读(Repeatable read) | 事务级 | 否 | 否 | 是 |
可序列化(Serializable) | 最高级别,事务级 | 否 | 否 | 否 |
事务常见问题
更新丢失(Lost Update)
原因:当多个事务选择同一行操作,并且都是基于最初选定的值,由于每个事务都不知道其他事务的存在,就会发生更新覆盖的问题。类比github提交冲突。
脏读(Dirty Reads)
原因:事务A读取了事务B已经修改但尚未提交的数据。若事务B回滚数据,事务A的数据存在不一致性的问题。
不可重复读(Non-Repeatable Reads)
原因:事务A第一次读取最初数据,第二次读取事务B已经提交的修改或删除数据。导致两次读取数据不一致。不符合事务的隔离性。
幻读(Phantom Reads)
原因:事务A根据相同条件第二次查询到事务B提交的新增数据,两次数据结果集不一致。不符合事务的隔离性。指的是一个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(phantom row)
幻读和脏读有点类似
脏读是事务B里面修改了数据,
幻读是事务B里面新增了数据。
MVCC是行级锁的一个变种,它在很多情况下都避免了加锁操作,因此开销更低。虽然实现机制不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。MVCC是通过保存数据在某个时间点的快照来实现的。
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的创建时间,一个保存的是行的删除时间(或过期时间)。这里存储的并不是实际的时间值,而是系统版本号(可以理解为事务的ID),每开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的行记录的版本号做比较。因此InnoDB使用MVCC解决了幻读的问题。
MVCC只在repeatable read和read committed两个隔离级别下工作,其他两个隔离级别都与MVCC不兼容。read uncommitted 总是读取最新的行,serializable则会对多有读取到的行都加锁。
Mysql区别于其他数据库的一个重要特点是其插件式的表存储引擎,注意,存储引擎是基于表的,而不是基于数据库。
如果表在创建并且导入数据后,不再进行修改操作,那么可以采取MyISAM压缩表。压缩表可以极大减少磁盘空间占用,因此可以减少磁盘I/O,从而提升查询性能。压缩表也支持索引,但索引是只读的。
一、MyISAM引擎的特点
1、不支持事物(事物是指逻辑上的一组操作,组成这组操作的各个单元,要么全成功要么全失败)
2、表级锁定,数据更新时锁定整个表,其锁定级别是表级锁定,这虽然可以染锁定的实现成本很小但是也同时大大降低了其并发性能
3、读写互相阻塞:不仅会在写入的时候阻塞读取,MyISAM还会在读取的时候阻塞写入,但读本身并不会阻塞另外的读
4、只会缓存索引:MyISAM可以通过key_buffer_size缓存索引,以大大提高读取的时候阻塞写入,但读本身并不会阻塞另外的读。(key_buffer_size = 16M)
5、读取速度轻快,占用资源相对较少
6、不支持外键约束、但支持全文索引
存储结构
MyISAM:每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。.frm文件存储表定义。数据文件的扩展名为.MYD (MYData)。索引文件的扩展名是.MYI (MYIndex)。
InnoDB:所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB。
存储空间
MyISAM:可被压缩,存储空间较小。支持三种不同的存储格式:静态表(默认,但是注意数据末尾不能有空格,会被去掉)、动态表、压缩表。
InnoDB:需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。
可移植性、备份及恢复
MyISAM:数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作。
InnoDB:免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了。
事务支持
MyISAM:强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持。
InnoDB:提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
AUTO_INCREMENT
MyISAM:可以和其他字段一起建立联合索引。引擎的自动增长列必须是索引,如果是组合索引,自动增长可以不是第一列,他可以根据前面几列进行排序后递增。
InnoDB:InnoDB中必须包含只有该字段的索引。引擎的自动增长列必须是索引,如果是组合索引也必须是组合索引的第一列。
表锁差异
MyISAM:只支持表级锁,用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。
InnoDB:支持事务和行级锁,是innodb的最大特色。行锁大幅度提高了多用户并发操作的新能。但是InnoDB的行锁,只是在WHERE的主键是有效的,非主键的WHERE都会锁全表的。
全文索引
MyISAM:支持 FULLTEXT类型的全文索引
InnoDB:不支持FULLTEXT类型的全文索引,但是innodb可以使用sphinx插件支持全文索引,并且效果更好(在InnoDB 1.2 也就是MySQL 5.6版本后InnoDB也支持全文索引)。
表主键
MyISAM:允许没有任何索引和主键的表存在,索引都是保存行的地址。
InnoDB:如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。
表的具体行数
MyISAM:保存有表的总行数,如果select count() from table;会直接取出出该值。
InnoDB:没有保存表的总行数,如果使用select count() from table;就会遍历整个表,消耗相当大,但是在加了wehre条件后,myisam和innodb处理的方式都一样。
CURD操作
MyISAM:如果执行大量的SELECT,MyISAM是更好的选择。
InnoDB:如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表。DELETE 从性能上InnoDB更优,但DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除,在innodb上如果要清空保存有大量数据的表,最好使用truncate table这个命令。
外键
MyISAM:不支持
InnoDB:支持
通过上述的分析,基本上可以考虑使用InnoDB来替代MyISAM引擎了,原因是InnoDB自身很多良好的特点,比如事务支持、存储 过程、视图、行级锁定等等,在并发很多的情况下,相信InnoDB的表现肯定要比MyISAM强很多。另外,任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。如果不是很复杂的Web应用,非关键应用,还是可以继续考虑MyISAM的,这个具体情况可以自己斟酌。
为什么在一定程度上MyISAM比innodb读取数据要快?
1.innodb不仅要缓存数据还要缓存索引,MyISAM只需要缓存索引;
2.innodb寻址要映射到块,再到行,MYISAM记录的直接是文件的OFFSET,定位比INNODB要快
3.INNODB还需要维护MVCC一致;虽然在查询的场景可能没有,但还是需要去检查和维护,而MyISAM由于没有了多行,不需要判断 选取可见的那行数据
InnoDB:通过为每一行记录添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。但是InnoDB并不存储这些事件发生时的实际时间,相反它只存储这些事件发生时的系统版本号。这是一个随着事务的创建而不断增长的数字。每个事务在事务开始时会记录它自己的系统版本号。每个查询必须去检查每行数据的版本号与事务的版本号是否相同。让我们来看看当隔离级别是REPEATABLEREAD时这种策略是如何应用到特定的操作的:
SELECT InnoDB必须每行数据来保证它符合两个条件:
1、InnoDB必须找到一个行的版本,它至少要和事务的版本一样老(也即它的版本号不大于事务的版本号)。这保证了不管是事务开始之前,或者事务创建时,或者修改了这行数据的时候,这行数据是存在的。
2、这行数据的删除版本必须是未定义的或者比事务版本要大。这可以保证在事务开始之前这行数据没有被删除
采用MyISAM引擎
采用InnoDB引擎
采用Memory引擎