高性能MySQL(Chapter 1 MySQL架构和历史笔记)

1.1 MySQL逻辑架构

高性能MySQL(Chapter 1 MySQL架构和历史笔记)_第1张图片
MySQL服务器逻辑架构图.png

最上层的不是MySQL所独有的,大多数是基于网络的客户端/服务器的工具或者服务都有类似的架构。比如链接处理、授权认证、安全等等。
第二层架构MySQL的核心服务功能,包括查询解析、分析、优化、缓存以及所有的内置函数,所有跨存储引擎的功能都在这实现的:存储过程、触发器、视图等。
第三层包含了存储引擎。存储引擎负责MySQL中数据的存储和提取。服务器通过API与存储引擎进行通信,API屏蔽了不同的存储引擎之间的差异。存储引擎包含几十个底层函数,用于执行诸如"开始一个事务"或者"根据主键提取一行记录"等操作。但存储引擎不会去解析SQL,不同存储引擎之间不会相互通信,而只是简单地响应上层服务的请求。

1.2 并发控制

1.2.2 锁粒度

表锁(table lock) 是MySQL中最基本的锁策略,并且是开销最小的策略,它会锁定整张表。一个用户在对表进行写操作前,需要先获得写锁,这回阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取的用户才能获得读锁。
写锁比读锁有更高的优先级,一个写锁请求可能会被插入到读锁队列的前面(写锁可以插入到锁队列中读锁的前面,反之读锁不能插入到写锁的前面)。
尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同的目的。例如ALTER TABLE之类的语句使用表锁此时会覆盖存储引擎的锁机制。

行级锁(row lock)可以最大程度的支持并发处理(同时也带了最大的锁开销)。众所周知,在InnoDB和XtraDB,以及其他一些存储引擎中实现了行级锁,行级锁只在存储引擎层实现,而MySQL的服务层没有实现。

1.3 事务

事务就是一组原子性的SQL查询,或者说一个独立的工作单元。事务内的语句要么全部执行,只要有一条失败,所有语句都不会执行。可以用START TRANSACTION 开始一个事务,然后用COMMIT 提交事务将修改的数据持久化保存,或者用ROLLBAK撤销所有修改。

1.3.1 隔离级别

隔离性其实比想象中的复杂。在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。

  • READ UNCOMMITTED :

在未提交读级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务读取未提交的数据,称为脏读(Dirty Read)。从性能上说,READ UNCOMMITTED不会比其它级别好太多,但缺乏其他级别很多好处,实际应用中一般很少使用。

  • READ COMMITTED :

提交读满足隔离性的简单定义:一个事务开始,只能“看见”已经提交的事务所做的修改。这个级别有时候也叫做不可重复读(nonrepeatable read),因为两次执行同样的查询,可能会得到不一样的效果。

  • REPEATABLE READ :

可重复读解决了脏读的问题。该级别保证了同一个事物多次读取同样记录的结果是一致的。但是无法解决幻读,幻读指的是当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入新的记录,之前的事务再次读取会产生幻行(Phantom Row)。InnoDB和XtraDB存储引擎通过多版本冰法控制(MVCC,Multiversion Concurrency Control) 解决了幻读的问题。可重复读也是MySQL的默认事务隔离级别。

  • SERIALIZABLE

可串行化是最高的隔离级别。强制事务串行执行,避免了前面说的幻读问题。SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁晶振问题。实际应用中只有在非常需要确保数据一致性而且可以接受没有并发的情况下考虑采用该级别。


高性能MySQL(Chapter 1 MySQL架构和历史笔记)_第2张图片
Isolation.png
1.3.3 事务日志

使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中。事务日志采用的是追加的方式。因此写日志的操作是磁盘上的一小块区域内的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。事务日志持久以后,内存中修改的数据在后滩可以慢慢刷回到磁盘。目前大多数存储引擎都是这样实现的,我们通常称之为预写式日志(Write-Ahead Logging),修改数据需要写两次磁盘。
如果数据的修改已经记录到事务日志并持久化,但数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动回复这部分修改的数据。具体的回复方式则视存储引擎而定。

1.3.4 MySQL中的事务
  • AUTOCOMMIT

MySQL默认采用自动提交(AUTOCOMMIT)模式。也就是说如果不是现实的开始一个事务,则每个查询都被当做一个事务提交操作。
另外,像一些ALTER TABLE 这种DDL、LOCK TABLES等会强制执行COMMIT提交当前的活动事务。MySQL可以通过SET TRANSACTION ISOLATION LEVEL命令来设置隔离级别。新的隔离级别会在下次事务开始生效。

  • 隐式和显示的锁定

InnoDB采用的是两阶段锁定协议(two-phase locking protocol).在事务执行过程中,随时可以执行锁定,只有在COMMIT和ROLLBACK的时候才会释放,并且所有锁在同一时刻释放。前面描述的锁定都是隐式锁定,InnoDB根据隔离级别在需要的时候自动加锁。
另外,InnoDB也支持通过语句显示锁定,这些事MySQL范畴的语句,不属于SQL规范:

SELECT ... LOCK IN SHARE MODE
SELECT ... FOR UPDATE

MySQL支持LOCK TABLES 和 UNLOCK TABLES语句,这是在服务器层实现的,和存储引擎无关。他们有自己的用途,并不能替代事务处理,还是要依赖事务性存储引擎来实现事务。如果一个线程在一个表上得到一个写锁,哪么只有这个线程可以从表中读取和写表,其他线程阻塞;若获得是读锁,那么所有线程只读,不能写。
LOCK TABLES和事务之间相互影响的话,情况会变得非常复杂。因此建议,除了事务中禁用了AUTOCOMMIT,可以使用LOCK TABLES之外,其他任何时候都不要显示的执行LOCK TABLES。

1.4 多版本并发控制

MySQL 大多数事务性存储引擎都不是简单的行级锁,他们一般都实现了多版本并发控制(MVCC)。可以认为MVCC是行级锁的一个变种,但是它很多情况下避免了加锁操作,因此开销更低,不同数据库的实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。通过保存数据在某个时间点的快照来实现,根据事务开始的时间不同,每个事务对同一张表同一时刻看到的数据可能会不一样。
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增,同时作为事务的版本号,用来和查询到的每行记录的版本号进行比较,下面看一下REPEATABLE READ隔离级别下MVCC具体实现:

  • SELECT
    InooDB会根据以下两个条件检查每行记录:
    a. InnoDB只查找版本早于当前事务版本的数据航(即行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行要么是在事务开始前存在的,要么是事务自身插入或修改过的。
    b. 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未删除。
    只有符合以上两个条件的记录,才能返回作为查询的结果。
  • INSERT:
    InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
  • DELETE:
    InooDB为删除的每一行保存当前系统版本号作为行删除标识。
  • UPDATE:
    InnoDB为插入一行新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为航删除标识。
    保存这两个系统版本号,使大多数操作不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,做额外的行检查和维护工作。
    MVCC只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容,READ UNCOMMITTED总是读取最新的数据行,而SERIALIZABLE则会对所有读取的行都加锁。

1.5 MySQL的存储引擎

在文件系统中,MySQL将每个数据库保存为数据目录下的一个子目录。创建表时,MySQl会在数据库子目录下创建一个和表同名的.frm文件保存表的定义。可以用show table status 显示表的相关信息:

mysql> show table status like 'user' \G
*************************** 1. row ***************************
           Name: user #表名
         Engine: MyISAM #存储引擎类型
        Version: 10
     Row_format: Dynamic  #行的格式
           Rows: 7 #表中的行数
 Avg_row_length: 116 #平均每行包括的字节数
    Data_length: 812 #表数据的大小
Max_data_length: 281474976710655 #表数据的最大容量
   Index_length: 2048 #索引的大小
      Data_free: 0 #对于MyISAM表标识已分配但未使用的空间
 Auto_increment: NULL #下一个AUTO_INCREMENT的值
    Create_time: 2018-11-08 17:53:35 #表的创建时间
    Update_time: 2018-11-08 18:26:30 #表的数据最后修改时间
     Check_time: NULL #最后一次检查表的时间
      Collation: utf8_bin #表的默认字符集和字符排列顺序规则
       Checksum: NULL #如果启用保存的是整个表的实时校验和
 Create_options:  //创建表时指定的其他选项
        Comment: Users and global privileges #其他一些额外信息
1 row in set (0.00 sec)
1.5.1 InnoDB存储引擎

InnoDB的数据存储在表空间中,在MySQL4.1以后InnoDB将每个表的数据和索引文件存放在单独的文件中。
InnoDB采用MVCC来支持高并发,并且实现了四个标准的隔离级别。其默认级别是REPEATABLE READ,并且通过间隙锁(next-key locking)策略防止幻读。
InnoDB基于聚簇索引建立的,对主键查询有很高的性能。不过它的耳机索引中必须包含主键列,所以如果主键列很大,其它的索引都会很大。因此,若表上的索引较多的话,主键应当尽可能的小。InnoDB的存储格式是平台独立的,可以将数据和索引从Intel平台复制到PowerPC或者Sun SPARC平台。
作为事务型的存储引擎,InnoDB通过一些机制和工具支持真正的热备份,Oracle提供MySQL Enterprise Backup、Percona 提供的开源的XtraBackup都可以做到这一点。

1.5.2 MyISAM存储引擎

在MySQL5.1之前的版本,MyISAM是默认的存储引擎。MyISAM提供了大量的特性,包括全文索引、压缩、空间函数等,但不支持事务和行级锁。

1.8 总结

MySQL拥有分层的架构。上层是服务器层的服务和查询执行引擎,下层则是存储引擎。虽然有很多不同欧诺个作用的插件API,但存储引擎API还是最重要的。

你可能感兴趣的:(高性能MySQL(Chapter 1 MySQL架构和历史笔记))