每一个客户端链接会在服务器进程中拥有一个线程,该链接的查询只会在单独的线程中执行。MySQL会解析查询,并建立内部数据结构,而后对其进行各类优化。对于SELECT语句,解析查询以前,服务器会先检查缓存,若是可以在其中找到对应的查询,服务器就再也不执行查询解析,而是直接返回缓存中的结果。mysql
MySQL在两个层面实现并发控制:服务器层与存储引擎层。算法
在处理并发读或写时,能够经过实现一个由两种锁组成的系统来解决问题。这两种锁一般被称为共享锁和排他锁,或者称为读锁和写锁。读锁是共享的,或者说是相互不阻塞的,多个客户能够在同时读取同一数据。而写锁是排他的,同一时刻只能有一个用户可以写入,并防止其余用户读取正在写入的数据。sql
锁粒度是指加锁的对象的大小。显然,锁的粒度越小,并发控制效率越高。锁的各类操做,包括得到锁、检查锁和释放锁等,都会增长系统开销。所以,若是系统花费大量时间来管理锁,而不是用来获取数据,就会影响系统性能。数据库
有两种常见的缩策略,表锁和行级锁。表锁开销较小,可是并发控制很差。行级锁能够很好地实现并发控制,可是开销比较大。缓存
事务将几个操做做为一个总体,要么所有执行,要么所有放弃。事务的四大特性ACID:安全
事务处理也会使系统作更多额外工做,用户能够根据是否须要进行事务处理,选择合适的存储引擎。服务器
下面是四种事务的隔离级别:session
死锁指的是多个事务在同一资源上相互占用,并请求对方占用的资源,致使恶性循环的现象。数据库系统中实现了各类死锁检测和死锁超时机制。数据结构
在MySQL中默认是自动提交事务的,每一个查询操做被看成一个事务。可使用set autocommit
来设置是否自动提交。能够经过set session transaction isolation level
来设置隔离级别。在同一事务中使用多种存储引擎是不可靠的。并发
MySQL中大多事务型存储引擎实现的都不是简单的行级锁,通常同时实现了多版本并发控制。MVCC的实现是经过保存数据在某个时间点的快照来实现的。即,不论须要执行多长时间,每一个事务看到的数据都是一致的。典型的实现有乐观并发控制和悲观并发控制。
InnoDB的MVCC经过在每行记录的后面保存两个隐藏的列来实现。这两个列,一个用来保存过时(被删除)的版本号,一个用来保存建立的版本号。每一个事务开始时,会使版本号递增。事务开始时的版本号被用做事务的版本号:
.frm
文件用来保存表的定义。
MySQL默认存储引擎,采用MVCC来支持高并发,而且实现了四个标准的隔离级别。基于聚簇索引创建。
MyISAM不支持事务和行级锁,并且没法在崩溃以后安全恢复;它将表存储在两个文件中:数据文件和索引文件,分别以.MYD
和.MYI
为拓展名。它对整张表加锁,而不是某行。若是建立并导入数据以后,不会再进行修改,可使用压缩表来减小空间占用和IO,从而提高查询性能。
还有一些其余的存储引擎。
除非某些InnoDB不具有的特性,而且没有其余方法能够替代,不然都应该优先优先选择InnoDB引擎。除非万不得已,不然不要混合使用多种存储引擎,不然可能带来一些复杂的问题及潜在的BUG.
修改存储引擎:
Alter table tbl_name engine = Innodb
:须要执行很长时间,MySQL会按行将数据从原表复制到一张新的表中,在复制期间可能会小号系统的IO能力,同时在原表上加锁。导入导出
:用mysqldump将数据导出到文件,燃火修改文件中create table语句的存储引擎选项,注意要同时修改表名。CREATE & SELECT
:数据量不大的时候,能够先建立一张使用新的存储引擎的表,而后利用INSERT SELECT
将原表中的数据插入到新表中。当数据量比较大的时候,可使用between语句来分批次操做完成。MySQL整数能够指定宽度,如int(11),对大多数应用这是没有意义的:它不会限制值的合法范围,只是规定了MySQL的一些交互工具用来显示字符的个数。对于存储和计算来讲,int(20)和int(1)是相同的。
由于须要额外空间和计算开销,因此尽可能只在对小数进行计算的时候才使用decimal。在数据量比较大的时候,能够考虑使用bigint代替decimal,将存储的单位根据小数的位数乘以相应的倍数便可。
CHAR适合存储短的短的字符串,或者全部的值都接近同一长度。对于常常变动的数据,CHAR也比VARCHAR好,由于定长的CHAR不容易产生碎片。对于很是短的字符串,CHAR也比VARCHAR更好,由于VARCHAR还须要1或2个额外的字节存储字符串长度。
可使用枚举替代经常使用的字符串类型,枚举能够把一些不重复的字符串存储成预约义的集合。枚举字段是按照内部存储的整数而不是字符串进行排序的。枚举很差的地方是当向枚举中增长字段的时候,须要使用ALTER TABLE语句来进行修改。因此,对未来可能会变的字符串,使用枚举不是个好的主意。
若是须要将时间保存到毫秒级别,可使用BIGINT.
可使用BIT列在一列中存储一个或多个true/false值。BIT(1)定义一个包含单个位的字段,bit(2)存储两个位。bit列最多存储64个位。
MySQL将BIT看成字符串类型,而不是数字类型。当检索bit(1)时,结果是一个包含二进制0或1值的字符串,而不是ASCII码的"0"或"1"。而后,在数字上下文场景中检索时,结果将是为字符串转换成的数字.
所谓的范式就是,好比,若是咱们须要学生和学校的记录,若是咱们将学生和学校放在不一样的表中就符合范式,若是放在同一表中就是反范式的。
范式化具备一些好处,好比:操做更快;每次只要修改少许的数据;表更小,适合放在内存中,操做更快。缺点是一般须要表关联。
不过实际咱们并不彻底遵照范式和反范式的规则。
缓存表表示存储那些能够简单地从schema其余表获取数据的表。汇总表保存的是使用GROUP BY语句聚合数据的表,使用汇总表的缘由是,实时计算和统计值是很昂贵的操做,由于要么须要扫描表中的大部分数据,要么只能在某些索引上才能有效运行。
计数器表是用来统计某个操做的次数的表,咱们能够在一个表中定义一个名为cnt的字段来表示操做的次数,而后每次执行了操做以后将其加1。可是,加1须要更新操做来完成,每次更新的时候要获取记录的锁,所以并发效率不高。解决这个问题,咱们能够再增长一个字段slot做为随机的槽,每次执行操做的时候,咱们使用随机数选择某个slot,并对其进行+1更新(只用锁住部分数据,所以效率比较高)。最后统计的时候将所有记录加起来便可。
数据库的索引相似于书的索引,实际的查找某个值的时候,先按照值进行查找,而后返回包含该值的数据行。索引能够包含一个或多个列的值,若是索引包含多个列,那么列的顺序也很重要——索引对多个列排序的依据是CREATE TABLE时定义索引的顺序,因此MySQL只能高效地使用索引的最左前缀列。
在MySQL中,索引是在存储引擎层而不是服务器层实现的。因此,没有统一的标准:不一样存储引擎工做方式不一样。MySQL支持的索引类型以下:
一般人们所说的索引。实际上不少存储引擎使用的是B+Tree. B-Tree索引适用于全键值、键值范围或键前缀查找。类型,以多列索引key(last_name, first_name, dob)为例:
B-Tree的一些限制:
基于哈希表实现,只有精确匹配索引全部列的查询才有效。由于它对每行中的全部索引列计算出一个哈希码,做为哈希表的键(原理是基于拉链法的解决碰撞的策略)。在MySQL中只有Memory引擎显式地支持哈希索引,Memory引擎同时也支持B-Tree索引。
哈希索引只须要存储对应的哈希值,因此索引的结构十分紧凑,这让哈希索引的查找速度很是快。然而,它也有自身的限制:
MyISAM表支持空间索引,能够用做地理数据存储。
它查找的是文本中的关键词,而不是直接比较索引中的值。全文索引相似于搜索引擎作的事情,而不是简单的where条件匹配。在相同的列上建立全文索引和基于B-Tree的索引不会冲突。
其余索引,还有分型树索引。
索引的优势有:
对于小型的表,使用全表扫描更高效;对中到大型的表,使用索引很是有效。对于特大型的表,创建和使用索引的代价会随之增加。这种状况下可使用分区来查出一组数据,而不是一条一条地匹配。
若是查找中的列不是独立的,则MySQL不会使用索引。独立的列是指索引列不能是表达式的一部分,也不能是函数的参数。好比
select * from actor where actor_id + 1 = 5;
select * from actor where to_days(current_date) - to_days(col_day) <= 10;
复制代码
索引很长的字符串会让索引变得大且慢。一般能够只索引开始部分的字符,这样能够节约索引空间,从而提升索引的效率。缺点是会下降索引的选择性。索引的选择性是指不重复的索引值和记录总数的比,显然越大越好。因此,咱们须要选择足够长的前缀来保证选择性,同时又不能太长以下降索引空间。
咱们可使用语句
select count(distinct left(col_name, 3)) / count(*) from tbl_name;
复制代码
来统计使用3个字符的前缀选择性,同理能够计算出4个,5个等的状况。最后,选择一个合理的前缀长度便可。选择了长度以后能够像下面这样设置指定长度的索引:
alter table add key(col_nane(4));
复制代码
常见的错误是,为每一个列建立独立的索引,或者按照错误的顺序建立多列索引。
若是一张表在col1和col2列上面存在索引,若是咱们使用col1 and col2做为where的条件,那么索引会作相角操做,若是使用col1 or col2,索引会作联合操做. 相交操做一般意味着须要一个包含全部相关列的多列索引,而不是独立的单列索引。联合操做则会消耗CPU和内存在算法的缓存、排序和合并上。若在explain中看到有合并索引,应先检查查询和表结构,看看是否是最优的。也能够经过optimizer_switch来关闭索引合并功能,或使用igonre index提示优化器忽略掉某些索引。
若是要对多个列创建一个索引,除了上面的问题以外,还应该考虑所建的索引中列的顺序。好比,对col1, col2两列数据创建索引,那么咱们的顺序应被指定为(col1, col2)仍是(col2, col1)呢。咱们能够依然可使用上面的选择性来解决这个问题,咱们能够将选择性比较高的列做为索引的第一列,另外一列做为第二列。
聚簇索引不是一种单独的索引类型,而是一种数据存储方式。“聚簇”表示数据行和相邻的键值紧凑地存储在一块儿。由于没法同时把数据行存放在两个不一样的地方,因此一个表只能有一个聚簇索引。
InnoDB经过主键汇集数据,若是没有主键就选择一个惟一的非空索引,若是没有这样的索引,就隐式定义一个主键做为聚簇索引。
聚餐的优势:
缺点:
关于聚簇索引和非聚簇索引的存储方式的区别:
假设有数据以下:
若是是聚簇的方式来存储,那么它的一级索引是下面的样子:
也就是它们使用主键的值做为汇集数据,而后每一个叶子包含了每行的所有记录。聚簇索引的二级索引是下面的样子:
注意将二级索引中存储的值和最上面的表中的数据进行对比。从中能够看出,实际上它的二级索引是先用二级索引的值找到一级索引,而后使用一级索引来查找整个记录。
非聚簇的存储方式是下面的样子:
以上是非聚簇的一级索引的例子,非聚簇的二级索引的状况与之相同。即它们都是先用指定的值找到行号,而后使用行号来查找完整记录。
最好避免随机的聚簇索引,特别是对于IO密集型的应用。由于随机插入的时候,须要为新的行寻找合适的位置——一般是已有数据的中间位置——而且分配空间。这回增长不少额外的工做,并致使分布不够优化。最好使用自增的主键。
若是一个索引包含了全部须要查询的字段的值,就称之为覆盖索引。覆盖索引就是从索引中直接获取查询结果,要使用覆盖索引须要注意select查询列中包含在索引列中;where条件包含索引列或者复合索引的前导列;查询结果的字段长度尽量少。
使用延迟关联解决索引没法覆盖问题:下面的解决方法对效率的提高不是绝对的!
SELECT * FROM products WHERE actor = 'SEAB CARREY' AND title like '%APPOLO%'
复制代码
上面的SQL中要查询所有的列,而咱们没有覆盖所有列的索引,所以没有覆盖索引。另外,like操做没法使用索引,由于like操做只有在匹配左前缀时才能使用索引。
咱们能够像下面这样解决问题:
SELECT * FROM products
JOIN (SELECT prod_id FROM products
WHERE actor = 'SEAB CARREY' AND title like '%APPOLO%')
AS t1 ON (t1.prod_id = products.prod_id)
复制代码
这里,须要先创建(actor, title, prod_id)索引。咱们先在子查询中找到匹配的prod_id,而后跟外层中数据进行匹配来获取全部列值。当符合where条件的数据数量远小于actor过滤出的数据数量的时候,它的效率尤为高。由于,根据子查询的where过滤出数据以后才与外层查询关联,然后者使用actor读取出数据以后,再用title进行关联。前者须要读取的数据量更少。
生成有序结果的两种方式:排序,按索引顺序扫描。当explain出的type为index时,说明使用索引扫描来进行排序。MySQL可使用一个索引既知足排序,又知足查找。只有当索引的列顺序和ORDER BY子句顺序一致,且列的排序方向都同样时,才能用索引对结果作排序。
下面是一些例子,假设索引是(col1, col2, col3),那么:
...where col1 = 1 order by col2, col3;(√)
...where col1 = 1 order by col2;(√)
...where col1 > 1 order by col1, col2;(√)
...where col1 > 1 order by col2, col3;(X)
...where col1 = 1 order by col2 desc, col3 asc;(X)
...where col1 = 1 order by col2, col4;(X)
...where col1 = 1 order by col3;(X)
...where col1 = 1 and col2 in(1,3) order by col3;(X)
复制代码
重复索引是指在相同的列上按照相同的顺序建立的相同类型的索引。常见的错误有:
Inn哦DB只有在访问行的时候才会对其加锁,而索引可以减小访问行的次数,因此索引能减小锁的数量。
能够经过下面两个步骤来分析慢查询:
典型的请求查过须要的数据的场景:
查询了不须要的记录,若是只须要指定行的记录,可使用limit语句来只返回部分记录;
多表关联的时候返回了所有的列,好比下面的语句会返回tbl1和tbl2的所有记录:
SELECT * FROM tbl1 INNER JOIN tbl2 ...;
复制代码
能够改为下面的样子(若是只须要tbl1的记录的话)
SELECT tb1.* FROM tbl1 INNER JOIN tbl2 ...;
复制代码
总数取出所有的列。缺点是没有办法使用覆盖索引完成优化,并且会为服务器带来额外的IO、内存和CPU消耗。
重复查询相同的记录。最好将这些数据缓存起来。
能够经过explain输出的列type中的值来获得访问类型。