大家好,我是彬彬酱,目前在腾讯从事Web后端开发。
由于在工作中经常需要使用到MySQL,仅仅知道基本指令是不够的,所以就开了这个专题整理关于MySQL 的进阶知识,也适合大家进行学习。
本篇开始会对MySQL进行原理介绍,篇1介绍了MySQL的基础知识,包括逻辑架构、并发控制、锁、事务性以及存储引擎,为接下来的MySQL性能优化进行铺垫。
1 - MySQL 逻辑架构
最上面的服务并不是MySQL所独有的,大多数基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等。
第二层架构是MySQL比较有意思的部分。大多数MySQL的核心服务功能都在这一层,包括查询解析、分析、优化、缓存以及所有的内置函数,所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。
第三层包含了存储引擎,存储引擎负责MySQL中数据的存储和提取,最常见的引擎包括InnoDB和MyISAM。每一种存储引擎都有自身的优势和劣势。
2 - 并发控制
当多个查询在同一时间对数据进行修改,就会产生并发控制的问题。MySQL主要有两个层面进行并发控制: 服务器层与存储引擎层 。
2.1 读写锁
在处理并发读或者写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。这两种类型的锁通常被称为 共享锁 和 排它锁,也叫 读锁 和 写锁 。
读锁是共享的,或者说是相互不阻塞的。多个客户在同一时刻可以同时读取同一个资源,而互不干扰。写锁则是排他的,也就是说一个写锁会阻塞其他的写锁和读锁。
2.2 锁粒度
一种提高共享资源并发性的方式是让锁定对象更有选择性。尽量只锁定需要修改的部分数据,而不是全部的资源。更理想的方式是,只会对修改的数据片进行精确的锁定。任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。
问题是加锁也需要消耗资源。锁的各种操作,包括获得锁,检查锁是否已经解除、释放锁等,都会增加系统的开销,如果系统花费大量的时间来管理锁,而不是存取数据,那么系统的性能可能会因此受到影响。下面将介绍两种最重要的锁策略。
表锁(table lock)
表锁是MySQL中最基本的锁策略,并且是开销最小的策略。表锁会锁定整张表,一个用户在对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁是,其他读取的用户才能获得读锁,读锁之间是不相互阻塞的。
尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同的目的。例如,服务器会为诸如ALTER TABLE之类的语句使用表锁,而忽略存储引擎的锁机制。
行级锁(row lock)
行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。行级锁只在存储引擎层实现,而MySQL服务器层没有实现,例如InnoDB和XtraDB。
3 - 数据库的事务性
事务最典型的应用场景是银行转账,当A要向B转账100元时,需要经历三个步骤:
- A的账户余额要大于100元
- 从A的账户中减去100元
- 给B的账户中加上100元
上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有的步骤。而事务的实现需要经过严格的ACID测试,否则空谈事务的概念是不够的。
3.1 ACID
原子性(atomicity)
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。一致性 (consistency)
数据库总是从一个一致性的状态转换到另一个一致性的状态。隔离性(isolation)
一个事务所做的修改在最终提交以前,对其他事物是不可见的。持久性(durability)
一旦事务提交,则其所做的修改就会永久保存到数据库中。
3.2 隔离级别
READ UNCOMMITTED(未提交读)
这个级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,被称为脏读(Dirty Read),这个级别性能不会比其他级别好太多,但缺乏其他级别的很多好处,一般很少使用。READ COMMITTED(提交读)
这个级别是大多数数据库系统的默认隔离级别(但MySQL不是)。一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别也叫作不可重复读(nonrepeatable read),因为两次执行同样的查询,可能会得到不一样的结果。REPEATABLE READ(可重复读)
该级别保证了在同一个事务中多次读取同样记录的结果是一致的,但依然无法解决另外一个幻读(Phantom Read)的问题。幻读,指的是当某个事物在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。InnoDB 和 XtraDB 存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。可重复读是MySQL的默认事务隔离级别。SERIALIZABLE(可串行化)
最高的隔离级别,强制事务串行执行,避免了前面说的幻读的问题。会在读取的每一行数据上都加锁,可能会导致大量的超时和锁征用的问题,实际应用中也很少用到这个级别。
3.3 死锁
死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚(这是相对比较简单的死锁回滚算法)。
4 - 多版本并发控制(MVCC)
4.1 乐观并发控制
顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
版本号机制
每行记录后面保存两个隐藏的列来实现,这两个列,分别保存了行的创建时间和行的删除时间(系统版本号)。每开始一个新的事务,系统版本号都会自动递增。
SELECT
a.查找版本早于当前事务版本的数据行
b.行的删除版本要么未定义,要么大于当前事务版本号
INSERT
新插入的每一行保存当前系统版本号作为行版本号
DELETE
删除的每一行保存当前系统版本号作为行删除标识
UPDATE
插入的新纪录保存为行版本号,原来的行保存行删除标识
4.2 悲观并发控制
就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁
5 - MySQL的存储引擎
通过 show table status 指令来查看表的信息
mysql>show table status like 'esf_seller_history'\G;
名称 | 解释 |
---|---|
Name | 表名称 |
Engine | 表的存储引擎 |
Version | 版本 |
Row_format | 行格式。对于MyISAM引擎,这可能是Dynamic,Fixed或Compressed。动态行的行长度可变,例如Varchar或Blob类型字段。固定行是指行长度不变,例如Char和Integer类型字段 |
Rows | 表中的行数。对于非事务性表,这个值是精确的,对于事务性引擎,这个值通常是估算的 |
Avg_row_length | 平均每行包括的字节数 |
Data_length | 整个表的数据量(单位:字节) |
Max_data_length | 表可以容纳的最大数据量 |
Index_length | 索引占用磁盘的空间大小 |
Data_free | 对于MyISAM引擎,标识已分配,但现在未使用的空间,并且包含了已被删除行的空间。 |
Auto_increment | 下一个Auto_increment的值 |
Create_time | 表的创建时间 |
Update_time | 表的最近更新时间 |
Check_time | 使用 check table 或myisamchk工具检查表的最近时间 |
Collation | 表的默认字符集和字符排序规则 |
Checksum | 如果启用,则对整个表的内容计算时的校验和 |
Create_options | 指表创建时的其他所有选项 |
Comment | 包含了其他额外信息,对于MyISAM引擎,包含了注释徐标新,如果表使用的是innodb引擎 ,将现实表的剩余空间。如果是一个视图,注释里面包含了VIEW字样。 |
5.1 InnoDB存储引擎
InnoDB是MySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎。它被设计用来处理大量的短期事务,短期事务大部分情况是正常提交的,很少会被回滚。InnoDB的性能和自动崩溃恢复特性,使得它在非事务型存储的需求中也很流行。
InnoDB的数据存储在表空间中;
InnoDB采用MVCC来支持高并发,并且实现了四个标准的隔离级别,默认级别是REPEATABLE READ(可重复读),并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。
InnoDB表是基于聚簇索引建立的,InnoDB的索引结构和MySQL的其他存储引擎有很大的不同,聚簇索引对主键查询有很高的性能。不过它的二级索引中必须包含主键列,所以如果主键列很大的话,其他的索引都会很大。因此,若表上的索引较多的话,主键应当尽可能的小。
InnoDB内部做了很多优化,包括从磁盘读取数据时采用的可预测性预读,能够自动在内存中创建hash索引以加速读操作的自适应哈希索引,以及能够加速插入操作的插入缓冲区。
5.2 MyISAM存储引擎
MyISAM提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且有一个毫无疑问的缺陷就是崩溃后无法安全恢复。对于只读的数据,或者表比较小,可以忍受修复操作,则依然可以继续使用MyISAM。
存储
MyISAM会将表存储在两个文件中:数据文件和索引文件,分别以.MYD和.MYI为扩展名。MyISAM表可以包含动态或者静态行。MySQL会根据表的定义来决定采用何种行格式。MyISAM表可以存储的行记录数,一般受限于可用的磁盘空间,或者操作系统中单个文件的最大尺寸。
MyISAM特性
加锁与并发
MyISAM对整张表加锁,而不是针对行。读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但是在表有读取查询的同时,也可以往表中插入新的记录。修复
对于MyISAM表,MySQL可以手工或者自动执行检查和修复操作,但这里说的修复和事务恢复以及崩溃恢复是不同的概念。执行表的修复可能导致一些数据丢失,而且修复操作是非常慢的。索引特性
对于MyISAM表,即使是BLOB和TEXT等长字段,也可以基于其前500个字符创建索引。MyISAM也支持全文索引,这是一种基于分词创建的索引,可以支持复杂的查询。延迟更新索引键
创建MyISAM表的时候,如果指定了DELAY_KEY_WRITE选项,在每次修改执行完成时,不会立刻将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入到磁盘。这种方式可以极大地提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。
大家好,我是彬彬酱,目前在腾讯从事Web后端开发。
菜鸟必知的 MySQL 知识专题整理了关于 MySQL 的基础知识,适合大家进行入门级学习,这个专题现包含下列文章:
菜鸟必知的 MySQL 知识(一)—— 基础知识
菜鸟必知的 MySQL 知识(二)—— 数据类型优化
菜鸟必知的 MySQL 知识(三)—— 索引优化