之前一直想找机会学习研究下MySQL,最近打算开始读《高性能MySQL》,我并不知道这本书好不好,因为我打算一边读书理解,一边来写这系列博客。
(https://img-blog.csdn.net/20170809145103075?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjcyMzYwNw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
最上层是客户端,最要是连接处理,授权认证,安全等。MySQL支持线程池插件,可以使用少量的线程来服务大量的连接。
第二层包含:查询缓存、解析、分析、优化、缓存以及所有的内置函数,还有跨存储引擎的功能:存储过程、触发器、视图。MySQL会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引。用户可以通过特殊的关键字提示(hint)优化器。
第三层就是存储引擎,主要负责数据的存储和提取。
当对MySQL数据进行读写操作时,通常通过读写读写锁来进行数据资源的锁定。
读锁(共享锁):读锁是共享的,或者说是相互不阻塞的,多个客户在同一时刻可以同时读取同一个资源,而且互不干扰。
写锁(排它锁):写锁会阻塞其他的读锁和写锁,一个用户在修改某个数据时,MySQL会同时阻塞其他用户对此数据的操作。
锁粒度
在对对象进行加锁时,尽量只锁定需要修改的部分数据,而不是所有的资源。在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。锁的各种操作都要消耗资源,增加系统开销,所以,我们需要更加细腻的锁粒度。
锁策略
每种MySQL存储引擎都可以实现自己的锁策略和锁粒度。
表锁(table lock):是最基本的锁策略,同时也是开销最小的策略。表锁会锁定整张表,一个用户在对表进行写操作(插入删除更新)前,需要获得写锁,并阻塞其他用户对表的读写。但读锁之间是不相互阻塞的。(写锁比读锁拥有更高的优先级,即在锁队列上,写锁可以插入到读锁的前面,反之不行)
行级锁(row lock):最大程度的并发处理,但同时也带来的最大的锁开销。行级锁即在表的数据行上施加锁,其他的用户可以操作表上其他行的数据。行级锁只在存储引擎中实现。
事务的ACID四个特性,许多博客都有讲解,就不列举了。
事务的处理过程也会增加系统的开销,所以我们可以选择不同的存储引擎架构来处理不同的需求。若不需要处理事务,可以选择一些非事务型的存储引擎,获得更高的性能。
隔离级别
1.read uncommitted(未提交读):也称脏读,即事务中的修改没有提交,对其他事务也都是可见的,事务可以读取未提交的数据。(一般不使用)
2.read committed(提交读):大多数数据库系统的默认隔离级别(但MySQL不是),此级别可以避免脏读,即对数据的修改,其他的事务是不可见的。但可能造成不可重复读:两次执行同样的查询,可能会得到不一样的结果。
3.pepeatable read(可重复读):(MySQL的默认事务隔离级别),解决了脏读和不可重复读,但是无法解决幻读。幻读:指当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,之前的事务再次读取时,就会产生幻行。
4.serializable(可串行化):其强制事务串行执行,避免了幻读。serializable会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用问题。
死锁
指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。
解决方法:死锁检测和死锁超时机制。InnoDB存储引擎能检测死锁的循环依赖,并返回一个错误。死锁超时即当查询时间达到锁等待超时的设定之后放弃锁的请求,这种方式通常说不太好。InnoDB目前处理死锁的方法是:将持有最少行级排他锁的事务进行回滚。
事务日志
事务日志可以提高事务的效率。简单来说,使用事务日志可以记录存储引擎修改数据的内存拷贝,然后可以依据事务日志,将内存中修改的数据在后台慢慢地刷回磁盘,这样就不需要每次都将修改的数据本身持久化到磁盘。所以修改数据需要写两次磁盘,因为写事务日志操作,也是持久化到磁盘上。
MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster。
MySQL默认采用自动提交(autocommit)模式,当然也可以通过命令来设置是否自动提交。
MySQL中可以混合使用了事务型和非事务型的表。
隐式和显示锁定
隐式锁定:采用两阶段锁定协议,在事务执行过程中,随时都可以执行锁定,锁只有在执行提交和回滚的市时候才释放,并且所有的锁在统一时刻被释放。InnoDB会根据隔离级别在需要的时候自动加锁。
显示锁定:通过特定的语句进行显示锁定
select … lock in share mode
select … for update
Multi-Version Concurrency Control 多版本并发控制(MVCC),MVCC是一种增加并发性的机制。它在很多情况下避免了加锁操作,因此开销更低。
MVCC的实现,是通过保存数据在某个时间点的快照来实现的,也就是说,不管需要执行多长时间,每个事物看到的数据都是一致的。根据事物开始的时间不同,每个事物对同一张表,同一时刻看到的数据可能是不一样的。
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,另一个列保存了行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统的版本号,每个新事务开始,系统的版本号都会自动增加。事务开始时刻的版本号会作为事务的版本号,用来和查询的每行记录的版本号作比较,对于不同的操作,更新版本号。
保存这两个额外的系统版本号,使得大多数读操作都可以不要加锁(直接比较事务版本号,即只查找那些行的系统版本号小于或等于事务的系统版本号的数据行),但额外的存储空间,需要更多的行检查工作,以及额外的维护工作。
MySQL将每个数据库(schema)保存为数据目录下的一个子目录,创建表时,MySQL会在数据库子目录下创建一个和表同名的.frm文件保存表的定义。不同的存储引擎保存数据和索引的方式是不同的,但表的定义则是在MySQL服务层统一处理的。
show table status like ‘user’ \G
显示‘user’表的相关信息。
InnoDB存储引擎
InnoDB是MySQL的默认事务引擎,也是最重要,使用最广泛的存储引擎,被设计处理大量的短期事务,短期事务大部分情况是正常提交的,很少会被回滚。
InnoDB的一些新特性:利用排序创建索引,删除或者增加索引时不需要复制全表数据,新的支持压缩的存储格式,新的大型列值如BLOB的存储方式,以及文件格式管理。
InnoDB具体的特点还得参考官方手册。
MyISAM存储引擎
MySQL5.1及之前的版本,MyISAM是默认的存储引擎,但其不支持事务和行级锁。MyISAM将表存储在两个文件中,数据文件和索引文件,分别以.MYD和.MYI为扩展名。
MyISAM特性:
1.加锁与并发:对整张表加锁,而不是行。可以在表读取查询时,也可以往表中插入新的记录(并发插入)。
2.修复
3.索引特性:对于BLBO和TEXT等长字段,也可以基于前500个字符创建索引。
4.延迟更新索引键(Delayed Key Write):在创建表时,如果指定了DELAY_KEY_WRITE,在每次修改执行完成时,不会马上将修改的索引数据写入磁盘,而是写入到内存中的键缓冲区。在清理键缓冲区或者关闭表时才会将对应的索引块写入到磁盘。
MySQL压缩表
如果表在创建并导入数据以后,不会再进行修改操作,这样的表就可以采用MyISAM压缩表。压缩表是不能进行修改的,其可以极大的减少磁盘的空间占用,磁盘I/O,提升查询性能。
1.Archive引擎:适合日志和数据采集类应用,因为Archive是全表查询,这类应用做数据分析时往往需要全表扫描,或者在一些需要更快速的Insert操作的场合下也可以使用,由于其会缓存所有的写并利用zlib对插入的行进行压缩。Archive引擎不是一个事务型引擎,而是一个针对高速插入和压缩做了优化的简单引擎。
2.BlackHole引擎
3.CSV引擎:对CSV格式的数据敏感,可作为一种数据交换的机制。
4.Federated引擎
5.Memory引擎:Memory引擎中使用Memory表,所以的数据都保存在内存中,不需要进行磁盘的I/O。Memory表的结构在重启后还会保留,但数据会丢失,这与MySQL中的临时表不同,临时表用来保存查询过程中的中间结果,其在连接断开后,就会消失。Memory表还支持Hash索引,其查询操作非常快。
6.Merge引擎:是MyISAM引擎的一个变种,Merge表是由多个MyISAM表合并而来的虚拟表。
7.NDB集群引擎
还有其他的第三方引擎,简单浏览下,我觉得研究透某几个存储引擎比较好,所以之后的学习会重点研究InnoDB和MyISAM这两个主流的存储引擎吧。