高性能MySql学习笔记-附录
A. MySQL分支与变种
Percona Server
- Percona Server 是个与 MySQL 向后兼容的替代品,任何运行在 MySQL 上的都可以运行在 Percona Server 上。
- Percona Server 包括 Percona XtraDB 存储引擎,即改进版本的 InnoDB,这同样是个向后兼容的替代品。
MariaDB
Drizzle
- Drizzle 是真正的 MySQL 的分支,而非只是个变种或增强版本。它并不与 MySQL 兼容。
B. MySQL服务器状态
系统变量
- MySQL 通过
SHOW VARIABLES SQL
命令展示系统变量。
SHOW STATUS
-
SHOW STATUS
命令会显示每个服务器变量的名字和值。这些变量是只读的。如果使用 SQL 命令,可以使用 LIKE 或 WHERE 来限制结果。可以从 INFORMATION_SCHEMA.GLOBAL_STATUS 和 INFORMATION_SCHEMA.SESSION_STATUS 表中查询。 -
现在
SHOW STATUS
默认展示会话变量,如果查看全局变量,则需要运行SHOW GLOBAL STATUS
。 -
线程和连接统计。这些变量用来跟踪尝试的连接、退出的连接、网络流量和线程统计等。
-
二进制日志状态。Binlog_cache_use和 Binlog_cache_disk_use状态变量显示了在二进制日志缓存中有多少事务被存储过,以及多少事务因超过二进制日志缓存而必须存储到一个临时文件中。
-
命令计数器。Com_*变量统计了每种类型的 SQL 或 C API 命令发起过的次数。
-
临时文件和表。
-
句柄操作。句柄 API 是 MySQL 和存储引擎之间的接口。Handle_* 变量用于统计句柄操作。
-
MyISAM 键缓冲。 Key_*变量包含度量值和关于 MyISAM 键缓冲的计数。
-
文件描述符。对于 MyISAM 存储引擎,Open_*变量揭示 MySQL 每隔多久打开每个表的.frm、.MYI 和.MYD 文件。InnoDB 保持所有的数据在表空间文件中,所以对于 InnoDB这些变量并不准确。
-
查询缓存。查询 Qcache_*状态变量可以检查查询缓存。
-
SELECT 类型。Select_*变量是特定类型的 SELECT 查询的计数器。它们展示各种查询计划的 SELECT 查询比率。
-
排序。当 MySQL 不能使用索引获取预先排序的行,则必须使用文件排序,这会增加 Sort_*状态变量。
-
表锁。Table_locks_immediate 和 Table_locks_waited 变量展示有多少锁被立即授权,有多少锁需要等待。
SHOW ENGINE INNODB STATUS
- InnoDB 存储引擎在
SHOW ENGINE INNODB STATUS
输出中,它的输出是单独的一个字符串,分为很多小段。 - SEMAPHORES。包含了两种数据:事件计数器、以及可选的当前等待线程的列表。如果有高并发的工作负载,则需要关注该端。
- LATEST FOREIGN KEY ERROR。展示服务器最新的外键错误。
- LATEST DETECTED DEADLOCK。展示服务器内最新的死锁信息。
- TRANSACTIONS。包含关于 InnoDB 事务的总结信息,紧随其后是当前活跃事务列表。
- FILE I/O。FILE IO 部分显示的是 IO 辅助线程的状态,还有性能计数器的状态。
- INSERT BUFFER AND ADAPTIVE HASH INDEX。显示关于插入缓存和自适应哈希索引的状态。
- LOG。显示关于 InnoDB 事务日志(重做日志)子系统的统计。
- BUFFER POOL AND MEMORY。显示关于 InnoDB 缓冲池机器如何使用内存的统计。
- ROW OPERATIONS。
SHOW PROCESSLIST
- 进程列表是当前连接到 MySQL 的连接或线程的清单。SHOW PROCESSLIST 列出了这些线程,以及每个线程的状态信息。Command 和 State 列真正表明了线程的状态。
SHOW ENGINE INNODB MUTEX
- 改命令返回 InnoDB 互斥体的详细信息,每个互斥体都保护着代码中一个临界区。
复制状态
- MySQL 有几个命令用以监测复制。在主库上执行
SHOW MASTER STATUS
可显示主库的复制状态和配置。输出包含了主库当前的二进制日志位置。通过SHOW BINARY LOGS
可以获取到二进制日志的列表。通过SHOW BINLOG EVENTS
可以查看二进制日志中的事件。在备库上执行SHOW SLAVE STATUS
查看复制的状态和配置。
INFORMATION_SCHEMA
- INFORMATION_SCHEMA 库是一个 SQL 标准中定义的系统视图的集合。能够以标准的 SQL 来进行查询。
Performance Schema
- Performance Schema是 MySQL 增强仪表的新的汇总处。默认情况下,Performance Schema是禁用的,需要打开并且使其在一个想要手机的特定的仅表点启用。
C. 大文件传输
一个简单的示例
- 在server1的源服务器,将文件进行压缩,并使用scp方法复制到server2
$ gzip -c /backup/mydb/mytable.MYD > mytable.MYD.gz
$ scp mytable.MYD.gz root@server2:/var/lib/mysql/mydb/
- 在server2执行如下命令,进行解压
$ gunzip /var/lib/mysql/mydb/mytable.MYD.gz
- 这个方法效率不高,因为在server1上gzip需要读写,scp在server1上读在server2上写,gunzip在server2上既要读又要写。
一步到位的方法
- 下面这个方法将压缩、复制文件和在传输的另一端解压缩文件全部放在一个步骤里完成。这个方法只需要在server1读,在server2上写。
$ gzip -c /backup/mydb/mytable.MYD | ssh root@server2 "gunzip -c - > /var/lib/mysql/mydb/mytable.MYD"
避免加密的系统开销
- SSH 部署跨网传输数据的最快方法,因为它增加了加解密的系统开销。如果不需要加密,可以使用netcat把“裸数据”进行跨网传输。
其他选项
- 另一个选项是rsync。rsync易于在源和目标直接做镜像,并且还可以断点续传。但是,如果它的二进制差异算法无法被很好的发挥时,它不太会得到很好应用。
D. EXPLAIN
调用 EXPLAIN
- 在 SELECT 关键字前增加 EXPLAIN 这个词,MySQL 在执行查询时,这个标记会使其返回关于在执行计划中的每一步的信息,而不是执行它。
- EXPLAIN 语句并不总是不执行查询,如果在 FROM 子句中包含子查询,那 MySQL 实际会执行子查询。
- EXPLAIN 只是一个近似结果,有时其结果与真相相差甚远。
EXPLAIN 中的列
-
id 列。id 列包含一个编号,标识 SELECT 所属的行。如果语句中没有子查询或联合,那么只会有唯一的 SELECT,于是每一行在这个列中都将显示一个1。否则,内层的 SELECT 语句一般会顺序编号。
-
select_type 列。这一列展示了对应行是简单查询还是复杂查询,如果是复杂查询,那么是三种复杂类型中的哪一种。SIMPLE值意味着查询不包括子查询和 UNION。如果查询有任何复杂的子部分,则最外层部分标记为 PRIMARY。其他部分标记如下:
- SUBQUERY。包含在 SELECT 列表中子查询的 SELECT。(不在 FROM 子句中)。
- DERIVED。包含在 FROM 子句的子查询中的 SELECT。
- UNION。在 UNION 中的第二个和随后的 SELECT 被标记为 UNION。
- UNION RESULT。用来从 UNION 的匿名临时表中检索结果的 SELECT 被标记为 UNION RESULT。
-
table 列。展示对应行正在访问哪个表。
-
type 列。代表访问类型,也就是 MySQL 决定如何查找表中的行。如下为重要的访问方法,依次从差到优。
- ALL。俗称全表扫描,代表 MySQL 必须扫描整张表来查询数据。
- index。这个同样是全部扫描,只不过扫描表时按索引顺序进行,而不是行顺序。它的主要优点是避免了排序,缺点是要承担按索引排序读取整个表的开销,因为这往往是随机读取。
- range。范围扫描就是一个有限制的索引扫描。这比全索引扫描好一些,因为不用遍历全部索引。常见的范围扫描是带有 BETWEEN 或在 WHERE 子句中带有比较的查询。
- ref。这是一种索引访问,返回所有匹配某个单个值的行,当然也可能有多个符合条件的行。
- eq_ref。这也是一种索引访问,相比 ref,MySQL 确定最多只返回一条符合条件的记录。
- const,system。当 MySQL 能对查询的某部分优化转化为常量时,则使用此种访问类型。
- NULL。 这种访问方式意为着 MySQL 能够在优化阶段分解查询语句,在执行阶段甚至用不着再访问表或者索引。
-
possible_keys 列。这一列显示了查询可以使用哪些索引,这是基于查询访问的列和使用的比较操作符来判断的,这里出现的索引不代表 MySQL 最终会选择他们。
-
key 列。这一列展示 MySQL 最终选择了哪个索引来优化对该表的访问。如果该索引没有出现在 possible_keys 列中,那么 MySQL 选用它是由于另外的原因——例如,它可能选择了一个覆盖索引,哪怕没有 WHERE 子句。
-
key_len列。该列显示了 MySQL 在索引里使用的字节数。如果 MySQL 正在使用的只是索引里的某些列,可以根据这个值进行推算,比如一个联合索引,但是只使用了其最左侧列的情况。
-
ref列。这一列展示了之前的表在 key 列记录的索引中查找值所用的列或常量。
-
rows 列。这一列是 MySQL 估计为了找到所需的行而要读取的行数。它不是 MySQL 认为最终要从表里读取出来的行数,而是 MySQL 为了查询而必须扫描的行数。
-
filtered列。它显示的是针对表里符合某个条件(WHERE 子句或联接条件)的记录数的百分比所做的一个悲观估算,优化器只有在使用 ALL、index、range、和 index_merge 访问方法时才会使用这一估算。
-
Extra 列。这一列显示不适合在其他列显示的额外信息。常见的重要的值如下:
- Using index。表示 MySQL 将使用覆盖索引,以避免访问表。不要把覆盖索引和 index 访问类型弄混了。
- Using where。这意味着 MySQL 服务器将在存储引擎检索行后再进行过滤。又是 Using where 的出现就是一个暗示:查询可受益于不同的索引。
- Using temporary。代表 MySQL 在对查询结果排序时会使用一个临时表。
- Using filesort。这意味着 MySQL 会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。
MySQL5.6中的改进
- MySQL 5.6中包括一个对 EXPLAIN 的重要改进:能对类似 UPDATE、INSERT 等的查询进行解释。
E. 锁的调试
服务器级别的锁等待
- 锁等待可能发生在服务器级别或存储引擎级别。服务器级别的锁有表锁、全局锁、命名锁、字符锁等类型。
- 表锁
- 表锁可以是显式的,也可以是隐式的。显示的锁用 LOCK TABLES 创建。可以执行如下命令,在 sakila.film 上获取一个显式的锁:
LOCK TABLES sakila.film READ;
- 如果在另一个会话中也获取这个表的锁,查询将被挂起并且不会执行:
LOCK TABLES sakila.film WRITE;
- 使用命令
SHOW PROCESSLIST;
可以看到等待线程的状态是 Locked。 - 服务器在查询过程中也会隐式地锁住表。一个长时间的查询即可将表锁住:
SELECT SLEEP(30) FROM sakila.film LIMIT 1;
当这个查询运行时,尝试再次锁住 film 表将会因为隐式锁而挂起。 - 隐式锁和显式锁会阻塞,隐式锁也会相互阻塞。
- 表锁可以是显式的,也可以是隐式的。显示的锁用 LOCK TABLES 创建。可以执行如下命令,在 sakila.film 上获取一个显式的锁:
- 全局读锁
- MySQL 服务器实现了一个全局读锁,可以如下获取该锁:
FLUSH TABLES WITH READ LOCK
; - 如果此时在另一个会话中尝试获取表锁,将会被挂起。而等待线程在 SHOW PROCESSLIST 中的状态将会是 Waiting for release of readlock,代表查询在等待一个全局读锁而不是表级锁。
- MySQL 服务器实现了一个全局读锁,可以如下获取该锁:
- 命名锁
- 命名锁是一种表锁:服务器在重命名或删除一个表时创建。命名锁与普通的表锁相冲突,无论是显式还是隐式的。
- 用户锁
- 在服务器中实现的最后一种锁是用户锁,它是一个命名互斥量,需要指定锁的名称字符串,以及等待超时秒数。
SELECT GET_LOCK('my lock', 100);
- 如果另一个线程尝试锁相同字符串,它将被挂起直到超时。
- 在服务器中实现的最后一种锁是用户锁,它是一个命名互斥量,需要指定锁的名称字符串,以及等待超时秒数。
InnoDB 的锁等待
- InnoDB 在
SHOW INNODB STATUS
的输出中显示了一些锁信息。也可以通过查询 INFORMATION_SCHEMA 表的方式来显露 InnoDB 的事务和锁。