深入了解mysql

存储引擎

Attribute MyISAM Heap BDB InnoDB
TransactionsNoNoYesYes
Lock granularityTableTablePage (8 KB)Row
StorageSplit filesIn-memorySingle file per tableTablespace(s)
Isolation levelsNoneNoneRead committedAll
Portable formatYesN/ANoYes
Referential integrityNoNoNoYes
Primary key with dataNoNoYesYes
MySQL caches data recordsNoYesYesYes
AvailabilityAll versionsAll versionsMySQL-MaxAll Versions

To make it easier to follow the unique characteristics of each storage engine, I created this magic quadrant diagram:

《深入了解mysql》

Below are some examples of using the best storage engine for different tasks:

Search Engine – NDBCluster

Web stats logging – Flat file for the logging with an offline processing demon processing and writing all stats into InnoDB tables.

Financial Transactions – InnoDB

Session data – MyISAM or NDBCluster

Localized calculations – HEAP

Dictionary – MyISAM


MyISAM

设计为处理读频率远大于写频率的情况,查询性能非常好;不支持事务,没有REDO、UNDO日志;支持表锁;

每个表使用独立的文件,索引和数据都存放在不同文件中,文件的存储以file block(页)的形式存储。只缓存索引,并不缓存实际数据,每次读取数据时要使用磁盘IO,索引在内存中以cache block形式组织,与file block对应。索引的缓存使用LRU算法管理,为了提高缓存的利用率,支持将缓存分成多个区域,例如分成Hot Area、Warm Area

key_buffer_size:设置缓存总大小

key_buffer_block_size:设置cache block大小

key_cache_division_limit:以百分比的形式将整个缓存区划分为多个区域。系统默认为100,即只有Warm Area

key_cache_age_threshold:控制各区域中的何时被降级,值越小,越容易降级到下一级area中

表的扫描分为Sequential Scan和Radom Scan 2种方式,read_buffer_size设置sequential scan时使用的缓存,read_rnd_buffer_size设置radom scan时使用的缓存

InnoDB

设计用于高并发读写情况,支持行锁(必须有索引支持);支持事务安全性,具有REDO、UNDO日志,具备故障恢复能力,其事务实现了SQL92的4个级别;支持外键,实现了数据库的引用完整性特性;

Adaptive Hash Index:InnoDB自动检测索引状况,如果发现可以通过hash index提高效率,会在内部创建一个基于B-Tree的hash index,并根据B-Tree索引的变化自动调整。hash index并不基于整个B-Tree创建,只针对其中的某部分;并不会存储到磁盘,仅创建在缓存区中

InnoDB的数据文件支持共享表空间和独享表空间2种模式,数据和索引存储在一起,支持数据和索引的缓存。存储结构从大到小依次为tablespace->segment->extent->Page,page默认为16KB,每个extent包含64个page,每个segment存放同一种数据,一般每个表存放于一个单独的segment中

锁机制

有表锁(MyISAM)、页锁(BDB)、行锁(InnoDB)三种

表锁

有4个队列记录锁的使用情况:Current read-lock(当前读锁队列), Pending read-lock(等待读锁的队列), Current write-lock(当前写锁队列), Pending write-lock(等待写锁的队列)

读锁、写锁

a). Current write-lock中当前资源的写锁会阻塞读锁和写锁请求.

b). Pending write-lock中WRITE类型的写锁会阻塞除了READ_HIGH_PRIORITY类型外的所有读锁请 求;READ_HIGH_PRIORITY类型的读锁比WRITE类型的写锁优先级高,因此它会阻塞Pending write-lock中所有的写锁请求;除了WRITE类型的写锁,Pending write-lock中其他类型的写锁优先级比读锁低(提高查询的响应时间)

c). Current write-lock中对资源的写锁类型为WRITE_ALLOW_WRITE时,允许除了WRITE_ONLY之外的所有读锁和写锁请求

MyISAM是MySQL官方开发的存储引擎,完全使用MySQL自己的表锁机制,其他几种支持事务的存储引擎都是让MySQL将锁处理交由存储引擎自行 实现,他们在MySQL中仅持有WRITE_ALLOW_WRITE类型的锁,至于锁的定义、并发冲突控制等都由各存储引擎处理

MyISAM表锁优化提示:MyISAM表锁读写互相阻塞,写锁优先级高于读锁

a). 参数选项low_priority_updates设置写锁优先级比读锁低,用于保证查询响应速度

b). 参数选项concurrent_insert配置是否使用并发插入特性,可以实现并发的读取和插入操作,配置值: 0:不允许并发插入; 1:数据文件中不存在空闲空间的时候可以在文件尾部进行并发插入; 2:不管数据文件是否存在空闲空间,均允许在文件尾部进行并发插入(插入操作将一直在文件尾部进行,中间的空闲空间无法利用,适用于删除操作很少的表)

InnoDB的行锁

不是MySQL实现的锁机制,行锁都由其他存储引擎实现,这里以InnoDB为例(不同存储引擎实现机制也不一样)

Oracle的行锁是在物理块的事务槽中记录锁信息,而InnoDB是在索引键值的起始、结束位置上记录锁信息(间隙锁),所以InnoDB的行锁只是利用索引实现的一个范围锁,而利用索引可以定位到数据行

锁类型以及排他性:

 共享锁(S)排他锁(X)意向共享锁(IS)意向排他锁(IX)
共享锁(S)兼容冲突兼容冲突
排他锁(X)冲突冲突冲突冲突
意向共享锁(IS)兼容冲突兼容兼容
意向排他锁(IX)冲突冲突兼容兼容

InnoDB行锁潜在的问题:

a). 如果无法使用索引信息,InnoDB将使用表锁

b). 当索引不是确定到某一行数据时,InnoDB锁定的是索引匹配到的整个范围内的数据。例如使用索引定位到了10条记录,而加上非索引的条件可以准确确定到一条记录,这种情况下InnoDB仍然锁定这10条记录

事务隔离级别:

InnoDB实现了SQL92的4个隔离级别:Read UnCommited, Read Commited, Repeatable Read, Serializable

InnoDB有死锁检测机制,将发生死锁的2个事务中较小(修改数据量比较少)的那个作为死锁牺牲品。当然只限于InnoDB存储引擎范围之内,跨存储引擎的死锁只能通过死锁超时设置进行处理

索引

MySQL主要有4类索引:B-Tree索引、Hash索引、Fulltext索引、R-Tree索引

B-Tree索引: 通用索引类型

InnoDB中的B-Tree索引分为Cluster形式的Primary Key和Secondary Index,与SQL Server类似,Primary Key索引的叶节点是实际数据文件,按索引顺序排列,Secondary Index的叶节点只存储Primary Key值。MyISAM的主键索引和非主键索引没什么区别,与InnoDB的Secondary Index类似,只是其叶节点存放的不是PK值,而是直接定位到数据行的信息

Hash索引:

将数据的索引键值进行hash运算建立索引,查询匹配时将查询条件也做hash运算,比较hash值进行匹配。主要是Memory和NDB Cluster存储引擎使用

Hash索引的查询效率非常高,因为不需要像B-Tree一样从根匹配到页节点(《MySQL性能调优与架构设计》中说hash索引可以一次定位要查找的记录,这种说法可能存在问题,Hash索引的组织、hash值的匹配同样需要数据结构和算法的实现,不可能一次定位,设想hash索引以B-Tree方式组织,比B-Tree索引优秀的地方可能是其B-Tree数据量会小,即使这样也可能意味着hash冲突的存在,其效率比B-Tree索引高的说法是有待验证的,可能需要有不少前提条件)

缺点:只能进行等值匹配;hash索引是无序的,可能需要额外的排序操作;无法进行部分匹配,只能全索引匹配;遇到hash冲突后效率可能会比较低

Fulltext索引: 只有MyISAM的CHAR, VARCHAR, TEXT三种数据类型支持fulltext索引,主要用于优化效率比较低的like ‘%***%’操作

MySQL的fulltext中文支持有待考察,fulltext索引的创建成本比较高

R-Tree索引: 用于空间数据检索

MySQL有空间数据类型GEOMETRY(5.0.16之前只有MyISAM支持,之后BDB、InnoDB、NDB Cluster、Archieve等支持),只有MyISAM存储引擎支持R-Tree索引

查询优化器

Explain:在查询语句前加EXPLAIN查看查询计划,各操作解释参考《MySQL性能调优与架构设计》P131

Profiling:

a). set profiling=1; 开启Query Profiler

b). 执行查询

c). show profiles; 显示已记录的profile信息列表

d). show profile *** for query n; 例如show profile cpu, block io for query 6; 其中n为c)中的Query_ID

MySQL查询优化器在CBO(Cost Based Optimizer)增加了Heuristic Optimize(启发式优化),即增加了RBO(Rule Based Optimizer)功能,主要优化处理步骤与SQL Server有点相似


JOIN算法
:只支持Nested Loop方式,有2种类型

a). simple nested-loop join (NLJ),假如有t1, t2, t3 3个表join,则算法为:

《深入了解mysql》
for
 each row 
in
 t1 matching range {
  

for
 each row 
in
 t2 matching reference key {
    

for
 each row 
in
 t3 {
      

if
 row satisfies join conditions,
      send to client
    }
  }
}

《深入了解mysql》 即直接使用3个嵌套循环进行匹配

b). Block Nested-Loop Join (BNL),具体算法描述为:

《深入了解mysql》
for
 each row 
in
 t1 matching range {
  

for
 each row 
in
 t2 matching reference key {
    store used columns from t1, t2 

in
 join buffer
    

if
 buffer 
is
 full {
      

for
 each row 
in
 t3 {
        

for
 each t1, t2 combination 
in
 join buffer {
          

if
 row satisfies join conditions,
          send to client
        }
      }
      empty buffer
    }
  }
}


if
 buffer 
is
 not empty {
  

for
 each row 
in
 t3 {
    

for
 each t1, t2 combination 
in
 join buffer {
      

if
 row satisfies join conditions,
      send to client
    }
  }
}

《深入了解mysql》 使用BNL的条件: 系统参数join_buffer_size有设置;MySQL的join类型为all(全表扫描)、index(全索引扫描)、range(索引区间扫描);进行join的记录能够放入join buffer中(记录太大无法放入join buffer无法使用BNL);内存满足join buffer分配

因为MySQL只有Nested Loop Join,如果内层表(被驱动表)没有索引或者无法使用索引等,就根本不适合使用Nested Loop,BNL主要用于处理这种情况。上面例子中如果t3没有索引,采用NLJ算法时在最内层每次都要对t3全表扫描,而使用BNL时,假如join_buffer_size可以存储100条中间结果,与NLJ相比每100次全表扫描t3可以减少为1次

ORDER BY算法:有2种,

a). 将排序字段和数据行定位信息读取到排序区(sort_buffer_size设定)中进行排序,完成排序后根据行定位信息从读取查询所需的列。缺点是有2次读取数据的操作

b). 将所需的列都读取到排序区进行排序,排序完后直接将结果返回。缺点是如果所需的列空间占用多,将消耗排序区空间影响排序性能

可以通过max_length_for_sort_data控制排序算法

另外一种对order by的处理是排序字段位于有序的索引中(例如不是hash索引等),索引本身是有序的因此不需要额外的排序操作

GROUP BY算法: 有3种,

a). 使用临时表。创建临时表,放入要group by的数据,按照group by的字段进行排序,生成group by结果集以及计算聚合函数的结果

b). Loose Index Scan

使用条件是针对单表的查询;group by的字段按照顺序前导匹配某个索引;索引必须是有序的;聚合函数只包含min、max,字段必须相同并且在索引中,并且是索引中紧跟着group by字段的下一个字段;索引字段中除了group by的字段外,其他字段如果出现在where条件中,where条件只能是常量值。MySQL 5.4.4加入了几个新的聚合函数:AVG(DISTINCT), SUM(DISTINCT), 和COUNT(DISTINCT),也是有限制性的

这种方式只需要部分扫描索引即可完成group by操作,如果where子句中只有group by字段的条件,则索引扫描的层级只需要到group by包含的字段(如果索引除了group by字段之外还包含其他字段,这种处理方式根本不需要扫描到索引页节点等层级)。正因为索引中包含的信息有限,因此该算法对聚合函数、索引的要求非常多,适用的场景比较窄,但是效率是最高的

例如表t1(c1,c2,c3,c4),有索引idx(c1,c2,c3),下面语句可以使用Loose Index Scan:

SELECT
 c1, c2 
FROM
 t1 
GROUP
 
BY
 c1, c2;

SELECT
 c1, 
MIN
(c2) 
FROM
 t1 
GROUP
 
BY
 c1;

SELECT
 c1, c2 
FROM
 t1 
WHERE
 c1 
<
 const 
GROUP
 
BY
 c1, c2;

SELECT
 
MAX
(c3), 
MIN
(c3), c1, c2 
FROM
 t1 
WHERE
 c2 
>
 const 
GROUP
 
BY
 c1, c2;

SELECT
 c2 
FROM
 t1 
WHERE
 c1 
<
 const 
GROUP
 
BY
 c1, c2;

SELECT
 c1, c2 
FROM
 t1 
WHERE
 c3 
=
 const 
GROUP
 
BY
 c1, c2; c). Tight Index Scan

其实就是利用有序索引的特性,避免创建临时表以及额外的排序操作,因此前提条件是能够利用有序索引;仅通过索引扫描可以完成查询

例如表t1(c1,c2,c3,c4),有索引idx(c1,c2,c3),下面2个语句都可以使用Tight Index Scan进行group by:

SELECT
 c1, c2, c3 
FROM
 t1 
WHERE
 c2 
=
 

a

 
GROUP
 
BY
 c1, c3;

SELECT
 c1, c2, c3 
FROM
 t1 
WHERE
 c1 
=
 

a

 
GROUP
 
BY
 c2, c3;

在EXPLAIN中,Extra中出现Using index for group-by表示使用Loose Index Scan实现;Using Temporary表示使用临时表实现;否则表示使用Tight Index Scan实现

SQL Server 7只有基本的group by算法,即先排序然后求group by结果集;SQL Server 2000引入了使用hash方法计算group by。而MySQL则是在这个基本算法基础上稍微拓展出了2种优化处理方法

DISTINCT算法:

distinct算法与group by基本相同,同样可以使用Loose Index Scan、Tight Index Scan、临时表实现,只是distinct只需要从group by结果中取出一条记录,不需要聚合运算等。算法的不同之处在于distinct算法本身并不要求排序操作(查询中包含group by、max聚合运算等其他操作可能要求排序)

索引选择:与SQL Server类似,使用统计信息进行索引选择,支持query hints调优

参考:

《MySQL性能调优与架构设计》

MySQL存储引擎比较

MySQL Storage Engines

MySQL ref: Nested-Loop Join Algorithms

MySQL ref: Loose Index Scan

MySQL ref: Tight Index Scan

    原文作者:mysql
    原文地址: https://blog.csdn.net/permike/article/details/50056545
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞