能够进行优化的层面mysql
进行优化前的数据准备linux
打开如下连接下载数据sql
http://downloads.mysql.com/docs/sakila-db.zip
打开终端,执行如下命令数据库
# 登陆 MySQL Cli 模式 mysql -uroot -p # 建立数据库 SOURCE /Users/LuisEdware/Downloads/sakila-db/sakila-schema.sql.sql # 填充数据到数据库 SOURCE /Users/LuisEdware/Downloads/sakila-db/sakila-data.sql # 使用 sakila 数据库 USE sakila;
SQL 语句和索引服务器
如何发现有问题的 SQL?答案是使用 MySQL 慢查询日志对有效率问题的 SQL 进行监控,执行命令以下:网络
# 查看是否开启慢查询日志 show variables like "slow_query_log"; # 查看是否设置了把没有索引的记录到慢查询日志 show variables like "log_queries_not_using_indexes"; # 查看是否设置慢查询的 SQL 执行时间 show variables like "long_query_time"; # 查看慢查询日志记录位置 show variables like "slow_query_log_file"; # 开启慢查询日志 set global slow_query_log=on # 设置没有索引的记录到慢查询日志 set global log_queries_not_using_indexes=on # 设置到慢查询日志的 SQL 执行时间 set global long_query_time=0 # 查看慢查询日志(在 Linux 终端下执行) tail -50 /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log;
慢查询日志所包含的内容tcp
慢查询日志分析工具函数
mysqldumpslow /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log;
brew install brew install percona-toolkit
pt-query-digest /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log | more;
如何经过慢查询日志发现有问题的 SQL?工具
使用 EXPLAIN 分析 SQL 的执行计划的例子以下:性能
EXPLAIN SELECT * FROM staff;
使用 EXPLAIN 分析 SQL 的各列参数含义以下:
分析 SQL 语句:使用 MAX() 方法查询最后一笔交易的时间
EXPLAIN SELECT MAX(payment_date) FROM payment_date
执行结果以下:
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: payment partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 16086 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
若是数据表的数据很是大,查询频率又很是高,那么服务器的 IO 消耗也会很是高,因此这条 SQL 语句须要优化。能够经过创建索引进行优化。执行代码以下:
CREATE INDEX idx_paydate ON payment(payment_date);
而后再分析 SQL 语句,执行结果以下:
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: NULL partitions: NULL type: NULL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Select tables optimized away 1 row in set, 1 warning (0.01 sec)
通过优化以后,因为索引是按顺序排列的,MySQL 不须要查询表中的数据,而是经过查询索引最后的一个数据,就能够得知执行结果了。并且这个时候,无论表的数据量多大,查询 MAX() 所须要的时间都是基本固定的,这样就尽量地减小了 IO 操做。
分析 SQL 语句:使用 COUNT() 函数在一条 SQL 中同时查出 2006 年和 2007 年电影的数量
SELECT count(release_year = '2006' OR NULL) AS '2006 年电影数量' , count(release_year = '2007' OR NULL) AS '2007 年电影数量' FROM film;
count(*) 包含空值,count(id) 不包含空值。上述语句就是优化 Count() 函数取值
分析 SQL 语句:查询 sandra 出演的全部影片
SELECT title , release_year , LENGTH FROM film WHERE film_id IN( SELECT film_id FROM film_actor WHERE actor_id IN( SELECT actor_id FROM actor WHERE first_name = 'sandra' ) )
一般状况下,须要把子查询优化为 join 查询,但在优化时要注意关联键是否有一对多的关系,要注意重复数据。
group by 可能会出现临时表、文件排序等,影响效率。能够经过关联的子查询,来避免产生临时表和文件排序,能够节省 IO。
group by 查询优化前:
EXPLAIN SELECT actor.first_name , actor.last_name , Count(*) FROM sakila.film_actor INNER JOIN sakila.actor USING(actor_id) GROUP BY film_actor.actor_id;
执行结果以下:
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 200 filtered: 100.00 Extra: Using temporary; Using filesort *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor partitions: NULL type: ref possible_keys: PRIMARY,idx_fk_film_id key: PRIMARY key_len: 2 ref: sakila.actor.actor_id rows: 27 filtered: 100.00 Extra: Using index 2 rows in set, 1 warning (0.01 sec)
group by 查询优化后:
EXPLAIN SELECT actor.first_name , actor.last_name , c.cnt FROM sakila.actor INNER JOIN( SELECT actor_id , count(*) AS cnt FROM sakila.film_actor GROUP BY actor_id ) AS c USING(actor_id);
执行结果以下:
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: actor partitions: NULL type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 200 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived2> partitions: NULL type: ref possible_keys: <auto_key0> key: <auto_key0> key_len: 2 ref: sakila.actor.actor_id rows: 27 filtered: 100.00 Extra: NULL *************************** 3. row *************************** id: 2 select_type: DERIVED table: film_actor partitions: NULL type: index possible_keys: PRIMARY,idx_fk_film_id key: PRIMARY key_len: 4 ref: NULL rows: 5462 filtered: 100.00 Extra: Using index 3 rows in set, 1 warning (0.00 sec)
LIMIT 经常使用于分页处理,时常会伴随 ORDER BY 从句使用,所以大多时候会使用 Filesorts ,这样会形成大量的 IO 问题
优化步骤1:使用有索引的列或主键进行 Order By 操做
优化步骤2:记录上次返回的主键,在下次查询时使用主键过滤(保证主键是自增且有索引)
例如:
SELECT * FROM payment WHERE staff_id = 2 AND customer_id = 584;
上述 SQL 语句,是 index(staff_id,customer_id)
合理,仍是 index(customer_id,staff_id)
合理。执行语句以下:
SELECT count(DISTINCT customer_id) , count(DISTINCT staff_id) FROM payment; -- 结果是 599 2
因为 customer_id 的离散度更大,因此应该使用 index(customer_id,staff_id)
之因此要找到重复和冗余的索引,是由于过多的索引不但影响写入,并且影响查询,索引越多,分析越慢。那么为什么重复索引、冗余索引?概念以下:
重复索引是指相同的列以相同的顺序创建的同类型的索引,以下表中 primary key 和 ID 列上的索引就是重复索引,例子以下:
CREATE TABLE test( id INT NOT NULL PRIMARY KEY , NAME VARCHAR(10) NOT NULL , title VARCHAR(50) NOT NULL , UNIQUE(id) ) ENGINE = INNODB;
UNIQUE(ID)
和 PRIMARY KEY
重复了。
冗余索引是指多个索引的前缀列相同,或是在联合索引中包含了主键的索引,例子以下:
CREATE TABLE test( id INT NOT NULL PRIMARY KEY , NAME VARCHAR(10) NOT NULL , title VARCHAR(50) NOT NULL , KEY(NAME , id) ) ENGINE = INNODB;
查找重复及冗余索引的 SQL 语句以下:
USE information_schema; SELECT a.TABLE_SCHEMA AS '数据名' , a.table_name AS '表名' , a.index_name AS '索引1' , b.INDEX_NAME AS '索引2' , a.COLUMN_NAME AS '重复列名' FROM STATISTICS a JOIN STATISTICS b ON a.TABLE_SCHEMA = b.TABLE_SCHEMA AND a.TABLE_NAME = b.table_name AND a.SEQ_IN_INDEX = b.SEQ_IN_INDEX AND a.COLUMN_NAME = b.COLUMN_NAME WHERE a.SEQ_IN_INDEX = 1 AND a.INDEX_NAME <> b.INDEX_NAME
也可使用工具 pt-duplicate-key-checker
检查重复索引和冗余索引,使用例如:
pt-duplicate-key-checker -uroot -p '123456' -h 127.0.0.1 -d sakila
执行结果以下:
# ######################################################################## # Summary of indexes # ######################################################################## # Size Duplicate Indexes 118425374 # Total Duplicate Indexes 24 # Total Indexes 1439
目前 MySQL 中尚未记录索引的使用状况,可是在 PerconMySQL 和 MariaDB 中能够经过 INDEX_STATISTICS 表来查看哪些索引未使用,但在 MySQL 中目前只能经过慢查询日志配合共组 pt-index-usage
来进行索引使用状况的分析。
pt-index-usage -uroot -p '123456' /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log;
数据库结构优化
略
垂直拆分,就是把原来一个有不少列的表拆分红多个表,这解决了表的宽度问题。一般垂直拆分就能够按如下原则进行:
当单表的数据量过大,致使增删查改等操做过慢,这时候须要对表进行水平拆分。水平拆分的表,每一张表的结构都是彻底一致的。
经常使用的水平拆分方法为:
挑战:
系统配置优化
数据库是基于操做系统的,目前大多数 MySQL 都是安装在Linux 系统之上,因此对于操做系统的一些参数配置也会影响到 MySQL 的性能,下面列举一些经常使用到的系统配置。
网络方面的配置,要修改文件 /etc/sysctl.conf
# 增长 tcp 支持的队列数 net.ipv4.tcp_max_syn_backlog = 65535 # 减小断开链接时,资源回收 net.ipv4.tcp_max_tw_buckets = 8000 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 10
打开文件数的限制,可使用 ulimit -a 查看目录的各项限制,能够修改文件 /etc/security/limits.conf
,增长如下内容以修改打开文件数量的限制
soft nofile 65535 hard nofile 65535
除此以外最好在 MySQL 服务器上关闭 iptables,selinux 等防火墙软件。
MySQL 能够经过启动时指定配置参数和使用配置文件两种方法进行配置,在通常状况下,配置文件位于 /etc/my.cnf
或是 /etc/mysql/my.cnf
,MySQL 查询配置文件的顺序是能够经过如下方法过的
经常使用参数说明
SELECT ENGINE , round( sum(data_length + index_length) / 1024 / 1024 , 1 ) AS 'Total MB' FROM information_schema. TABLES WHERE table_schema NOT IN( "information_schema" , "performance_schema" ) GROUP BY ENGINE;
percona:https://tools.percona.com/
服务器硬件优化