高性能MySQLmysql
MySQL最重要、不同凡响的特性时它的存储引擎架构,这种架构的设计将查询处理及其余系统任务和数据存储、提取相分离。 这种处理和存储的设计能够在使用时根据性能、特性以及其余需求来选择数据的存储方式
mysql服务器逻辑架构图
算法
存储引擎层sql
所谓锁策略,就是在锁的开销和数据的安全性之间寻求平衡。数据库
是锁最基本的锁策略,而且开销最小的策略。
服务器会为alter table之类的语句使用表锁,而忽略存储引擎的锁机制设计模式
最大程度的支持并发处理,同时也带来了最大程度的锁开销。api
一组原子性的sql查询,或者说一个独立的工做单元。缓存
可重复读隔离级别仍是没法解决另一个幻读的问题:当某一个事务在读取某个范围内的记录时,另外一个事务由在该范围内插入了新的记录,当以前的事务再次读取该范围的记录时,会产生幻行安全
使用事务日志,存储引擎在修改表的数据时只须要修改其内存拷贝,再把该修改行为记录持久化在硬盘上事务日志中,而不用每次都将修改的数据自己持久到磁盘。事务日志采用的是追加的方式,所以写日志的操做是磁盘上一小块区域的顺序I/O,
Wtite ahead Logging性能优化
Mysql默认采用autocommit模式服务器
SET TRANSACTION ISOLATION LEVEL 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED 设置数据库的隔离级别
Innodb采用的是两阶段锁定协议,在事务执行过程当中,随时均可以执行锁定,锁只有在执行commit或者rollback的时候才会释放。而且全部锁在同一时间释放
显式锁
SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE
## 1.4 多版本并发控制
MVCC的实现,是经过保存数据在某个时间点的快照来实现的。也就是说,无论须要执行多长时间,每一个事务看到的数据都是一致的。根据事务开始的时间不一样,每一个事务对同一张表,同一时刻看到的数据可能不同。
REPEATABLE READ隔离级别下MVCC实现 SELECT a.InnoDB查找版本早于当前事务版本的数据行,这样能够确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或修改的。 b.行的删除版本要么为定义,要么大于当前事务版本号。这能够确保事务读取到的行,在事务开始以前未被删除。 INSERT Innodb 为新插入的每一行保存当前系统版本号做为行版本号 DELETE InnoDB 为删除的每一行保存当前系统版本号做为删除标识 UPDATE InnoDB为插入一行新记录,保存当前系统版本号做为行版本号,同时保存当前系统版本号到原来的行做为行删除标识
null对于mysql来讲更难优化,由于可为null的列使得索引、索引统计信息和值都更复杂
DateTime 和Timestamp均可以存储相同类型的数据,时间和日期,精确到秒。而Timestamp只使用DateTime通常的存储空间,而且会根据时区变化,具备自动更新的能力。
存储空间(bit) TINYINT 8 SMALLINT 16 MEDIUMINT 24 INT 32 BIGINT 64
尽可能值在对小数进行精确计算时才使用decimal
varchar 和 char
VARCHAR须要使用1或2个额外字节记录字符串长度。
可是因为时变长,在update时可能使得行变得比原来长,这就致使须要额外的工做。 若是一个行占用的空间增加,而且页内没有更多空间存储时,不一样存储引擎处理方式不一样。
MyISAM会将行拆成不一样的片断存储。 InnoDB须要分裂页来使得行可存储。
mysql在内会将每一个值在列表中的位置保存为证书,并在.frm文件中保存数字-字符串的映射关系
枚举字段时按照内部存储的证书而不是定义的字符串进行排序的。
“实体-属性-值”设计模式是一个常见的糟糕的设计模式,尤为是在MySQL下不能靠谱的工做。 mysql限制了每一个关联操做最多只能有61张表
在范式化的数据库中,每一个事实数据都会出现而且只出现一次。相反在反范式化的数据库中,信息时冗余的,可能会存储在多个地方。
mysql执行大部分修改表结构操做的方法时用新的结构建立一个空表,从旧表中查出全部数据插入新表,而后删除旧表
列的默认值实际上存放在.frm文件中,因此能够直接修改这个文件而不须要改动表自己。
alter colum 操做改变列的默认值,而不涉及改表的数据。
在mysql中,索引实在存储引擎而不是在服务器层实现的
B-TREE一般意味着全部的值都是按顺序存储,而且每个叶子页到根的距离象通B-TREE索引
B-TREE对索引列的顺序组织存储的,很适合查找范围数据
可使用b-tree索引的查询类型:全键值、键值范围或键前缀查找
关于B-tree索引的限制
mysql中只有memory引擎显式支持哈希索引
哈希索引的限制
Innodb引擎有一个特殊功能叫作自适应哈希:当Innodb注意到某些索引值被使用的很是频繁时,它会在内存中基于B-Tree索引值上再建立一个哈希索引。这样就让B-tree索引也具备哈希索引的一些优势
relational database index design and the optimizers ---by Tapio lahdenmak and Mike leach
索引并不老是最好的工具 - 对于很是小的表,大部分状况下简单的全表扫描更有效 - 对于中到大型的表,索引就很是有效 - 对于特大型的表,创建和使用索引的代价将随之增加,可使用分区技术
若是不是独立的列, mysql就不会使用索引,独立的列时指索引列不能是表达式的一部分
索引选择性(cardinality) 是指不重复的索引值和数据表总记录的比值
select count(*) as cnt LEFT(city,7) as pref from sakil.city_demo group by pref order by cnt desc limit 10; 经过统计发现前缀长度到达7的时候,再增长前缀长度选择性提高的幅度已经很小了。 建立前缀索引 alter table sakila.city_demo add key(city(7));
索引从最左列进行匹配:
选择性最高的列放到索引最前列
可是性能不仅是依赖于全部索引列的选择性,也和查询条件和具体值有关,也就是和值的分布有关。
聚簇索引并非一种单独的索引类型,而时一种数据存储方法。具体的细节依赖于其实现方式,但InnoDB的聚簇索引实际上再同一个结构中保存了B-TREE索引和数据行
当由聚簇索引时,它的数据行实际上存放在索引的叶子页中。 聚簇表示数据行和相邻的键值紧凑的存储在一块儿。由于没法同时把数据行存放在两个不一样的地方,因此一个表只能有一个聚簇索引。
Innodb过主键汇集数据。 若是没有定义主键,InnoDB会选择一个惟一的非空索引代替,若是没有这样的索引,InnoDB会隐式定义一个主键来做为聚簇索引。
InnoDB只汇集再同一个页面中的记录,包含相邻键值的页面可能会相距甚远。
聚簇索引的一些优势
聚簇索引的缺点
myIsam中主键和其余索引在结构上并无什么不一样myisam索引结构图
聚簇索引就是表,因此不想myISAM那样须要独立的行存储
聚簇索引的每个叶子节点都包含了主键值、事务Id,用于事务和MVCC的回滚指针以及全部剩余列
innodb的二级索引的叶子节点中存储的不是行指针,而时主键值,并以此做为指向行的指针。这样的策略减小了出现行移动时或者数据页分裂时二级索引的维护工做
Innodb表的主键分布
聚簇索引和非聚簇表对比
最好避免随机的聚簇索引
随机写入的缺点
随机值载入聚簇索引以后,也须要作一次OPTIMIZE_TABLE 来重建表并优化页的填充
若是一个索引包含全部查询字段的值,咱们就称之为覆盖索引
覆盖索引带来的好处
当发起一个被索引覆盖的查询时,在EXPLAIN的extra列能够看到using index 的信息。
索引没法发起覆盖查询的缘由
MYSQL 有郎中方式能够生成有序结果
若是explain 出来的type列值为index 则说明mysql使用了索引扫描来排序
扫描索引自己时很快的,由于只须要从一条索引记录移动到紧接着的下一条记录。但若是索引不能覆盖查询所需的所有列,那就不得不没扫描一条索引记录就都回表查询一次对应的行。这基本上都是随机I/O,所以按索引顺序读取数据的速度一般要比顺序的全表扫描慢,尤为时I/O密集性的工做负载
只有当索引列的顺序和order by子句顺序彻底一致时,而且全部列排序方向同样时, mysql才能使用索引对结果作排序。
如通查询须要关联多张表,则只有当order by 子句引用的字段所有是第一个表示,才能使用索引作排序。
order by 子句和查找型查询的限制是同样的,须要知足索引最左前缀的要求,不然,mysql都须要执行排序操做,而不发使用索引排序
有一种状况order by 子句能够不知足索引的最左前缀的要求,就是前导列为常量的时候,
InnoDB 只有在访问行的时候才会对其加锁,而索引可以减小InnoDB访问的次数,从而减小锁的数量。 但这只有当InnoDB在存储引擎层可以过滤掉全部不须要的行时才有效。
选择索引和编写利用这些索引查询时
查询优化、库表优化、索引优化
查询的生命周期大体能够按照顺序来看:从客户端、服务器、而后在服务器上进行解析、生成执行计划、执行、返回结果给客户端。
其中执行能够认为是整个生命周期中最重要的阶段,这其中包括爱了大量为检索数据到存储引擎的调用以及调用后的数据处理、包括排序、分组。
在完成这些任务的时候查询须要在不一样地方花费时间,包括网络、CPU计算、生成统计信息和执行计划、锁等待等操做。尤为是像底层存储引擎检索数据的调用操做。这些调用须要在内存操做、cpu操做和内存不足时致使的I/O操做上消耗时间,根据存储引擎不一样,可能还会产生大量的上下文切换以及系统调用。
一、确认应用程序是否在检索大量超过须要的数据
二、确认mySQL服务器是否在分析大量查过须要的数据行
访问类型: 全表扫描 索引所秒 范围扫描 惟一索引扫描 常数引用
通常mysql可以使用以下三种方式应用where条件
delete from messages where created < DATE.SUB(now(),INTERVASL 3 MONTH); rows_affected = 0 do { row_affected = do_query( "delete from messages where created < DATE_SUB(NOW(),INTERVAL 3 MONTH ) limit 1000") } while rows_affected > 0
优点
查询执行路径
半双工通讯协议,意味着在任什么时候刻,要么是服务器想客户端发送数据,要么是由客户端向服务器发送数据,这两个动做不能同时发生。
也意味着,没法进行流量控制。一旦一段开始发送消息,另外一端要接受完整消息才能响应它。
SHOW FULL PROCESSLIST
查看当mysql的状态
这个阶段包括:解析SQL,预处理,优化SQL执行计划
mysql可以处理的优化类型
等价转化成or语句复杂度为O(N)
嵌套循环关联
UNION为例, mySQL首先将一系列的单个查询结果放到一个临时表中,而后从新读出临时表的数据完成union操做
当前mysql关联执行的策略很简单,mysql对任何关联执行嵌套循环关联操做,即mysql先在一个表中循环读出单条数据,而后再嵌套循环到下一个表中寻找匹配的行。依次下去。
全外链接就没法经过嵌套循环和回溯的方式完成,这是当发线关联表中没有找到任何匹配行的时候,则多是由于关联时刚好从一个没有任何匹配的表开始
mysql执行计划老是一个左侧深度优先的树
排序是一个成本很高的左槽,从性能角度考虑,应该尽量避免排序或者尽量避免对大量数据进行排序。
当不能经过索引进行排序时,mysql须要本身进行排序,若是数据量小则再内存中进行,若是数据量大则须要使用磁盘,不过mysql将这个过程统一称为文件排序(file sort)
mysql将结果返回给客户端是一个增量、逐步返回的过程
IN的查询
例子
select * from sakila.file where film_id in ( select file_id from sakila.film_actor where actor_id = 1);
实际上mysql会将象关的外城表压到子查询,它认为这样能够更高效率地查找到数据行
select * from sakila.file where exists(select * from sakila.film_actor where actor_id = 1 and film_actor.film_id = film.film_id);
能够改写这个查询为
select film.* from sakila.file INNER JOIN sakila.film_actor USING(film_id) where actor_id = 1;
使用IN()加子查询性能可能回不好,因此一般建议使用exists()等效改写查询得到更好的效率
mysql没法利用多核特性来并行执行查询。尽管其余不少关系型数据库可以提供这个特性
mysql不逊于对同一张表同时进行查询和更新
一般count()须要扫描大量的数据
“快速、精确和实现简单”三者永远只能知足其二