MYSQL 调优和使用必读

MYSQL 应该是最流行了 WEB 后端数据库。WEB 开发语言最近发展很快,PHP, Ruby, Python, Java 各有特色,虽然 NOSQL 最近越來越多的被提到,可是相信大部分架构师仍是会选择 MYSQL 来作数据存储。mysql

MYSQL 如此方便和稳定,以致于咱们在开发 WEB 程序的时候不多想到它。即便想到优化也是程序级别的,好比,不要写过于消耗资源的 SQL 语句。可是除此以外,在整个系统上仍然有不少能够优化的地方。ios

1. 选择合适的存储引擎: InnoDB

除非你的数据表使用来作只读或者全文检索 (相信如今提到全文检索,没人会用 MYSQL 了),你应该默认选择 InnoDB 。sql

你本身在测试的时候可能会发现 MyISAM 比 InnoDB 速度快,这是由于: MyISAM 只缓存索引,而 InnoDB 缓存数据和索引,MyISAM 不支持事务。可是 若是你使用 innodb_flush_log_at_trx_commit = 2 能够得到接近的读取性能 (相差百倍) 。数据库

1.1 如何将现有的 MyISAM 数据库转换为 InnoDB:后端

 
  1. mysql -u [USER_NAME] -p -e "SHOW TABLES IN [DATABASE_NAME];" | tail -n +2 | xargs -I '{}' echo "ALTER TABLE {} ENGINE=InnoDB;" > alter_table.sql
  2. perl -p -i -e 's/(search_[a-z_]+ ENGINE=)InnoDB/\1MyISAM/g' alter_table.sql
  3. mysql -u [USER_NAME] -p [DATABASE_NAME] < alter_table.sql

1.2 为每一个表分别建立 InnoDB FILE:缓存

 
  1. innodb_file_per_table=1

这样能够保证 ibdata1 文件不会过大,失去控制。尤为是在执行 mysqlcheck -o –all-databases 的时候。安全

2. 保证从内存中读取数据,讲数据保存在内存中

2.1 足够大的 innodb_buffer_pool_size服务器

推荐将数据彻底保存在 innodb_buffer_pool_size ,即按存储量规划 innodb_buffer_pool_size 的容量。这样你能够彻底从内存中读取数据,最大限度减小磁盘操做。架构

2.1.1 如何肯定 innodb_buffer_pool_size 足够大,数据是从内存读取而不是硬盘?

方法 1框架

 
  1. mysql> SHOW GLOBAL STATUS LIKE 'innodb_buffer_pool_pages_%';
  2. +----------------------------------+--------+
  3. | Variable_name | Value |
  4. +----------------------------------+--------+
  5. | Innodb_buffer_pool_pages_data | 129037 |
  6. | Innodb_buffer_pool_pages_dirty | 362 |
  7. | Innodb_buffer_pool_pages_flushed | 9998 |
  8. | Innodb_buffer_pool_pages_free | 0 | !!!!!!!!
  9. | Innodb_buffer_pool_pages_misc | 2035 |
  10. | Innodb_buffer_pool_pages_total | 131072 |
  11. +----------------------------------+--------+
  12. 6 rows in set (0.00 sec)

发现 Innodb_buffer_pool_pages_free 为 0,则说明 buffer pool 已经被用光,须要增大 innodb_buffer_pool_size

InnoDB 的其余几个参数:

 
  1. innodb_additional_mem_pool_size = 1/200 of buffer_pool
  2. innodb_max_dirty_pages_pct 80%

方法 2

或者用iostat -d -x -k 1 命令,查看硬盘的操做。

2.1.2 服务器上是否有足够内存用来规划

执行 echo 1 > /proc/sys/vm/drop_caches 清除操做系统的文件缓存,能够看到真正的内存使用量。

2.2 数据预热

默认状况,只有某条数据被读取一次,才会缓存在 innodb_buffer_pool。因此,数据库刚刚启动,须要进行数据预热,将磁盘上的全部数据缓存到内存中。数据预热能够提升读取速度。

对于 InnoDB 数据库,能够用如下方法,进行数据预热:

  1. 将如下脚本保存为 MakeSelectQueriesToLoad.sql
     
    1. SELECT DISTINCT
    2. CONCAT('SELECT ',ndxcollist,' FROM ',db,'.',tb,
    3. ' ORDER BY ',ndxcollist,';') SelectQueryToLoadCache
    4. FROM
    5. (
    6. SELECT
    7. engine,table_schema db,table_name tb,
    8. index_name,GROUP_CONCAT(column_name ORDER BY seq_in_index) ndxcollist
    9. FROM
    10. (
    11. SELECT
    12. B.engine,A.table_schema,A.table_name,
    13. A.index_name,A.column_name,A.seq_in_index
    14. FROM
    15. information_schema.statistics A INNER JOIN
    16. (
    17. SELECT engine,table_schema,table_name
    18. FROM information_schema.tables WHERE
    19. engine='InnoDB'
    20. ) B USING (table_schema,table_name)
    21. WHERE B.table_schema NOT IN ('information_schema','mysql')
    22. ORDER BY table_schema,table_name,index_name,seq_in_index
    23. ) A
    24. GROUP BY table_schema,table_name,index_name
    25. ) AA
    26. ORDER BY db,tb
    27. ;
  2. 执行
     
    1. mysql -uroot -AN < /root/MakeSelectQueriesToLoad.sql > /root/SelectQueriesToLoad.sql
  3. 每次重启数据库,或者整库备份前须要预热的时候执行:
     
    1. mysql -uroot < /root/SelectQueriesToLoad.sql > /dev/null 2>&1

2.3 不要让数据存到 SWAP 中

若是是专用 MYSQL 服务器,能够禁用 SWAP,若是是共享服务器,肯定 innodb_buffer_pool_size 足够大。或者使用固定的内存空间作缓存,使用 memlock 指令。

3. 按期优化重建数据库

mysqlcheck -o –all-databases 会让 ibdata1 不断增大,真正的优化只有重建数据表结构:

 
  1. CREATE TABLE mydb.mytablenew LIKE mydb.mytable;
  2. INSERT INTO mydb.mytablenew SELECT * FROM mydb.mytable;
  3. ALTER TABLE mydb.mytable RENAME mydb.mytablezap;
  4. ALTER TABLE mydb.mytablenew RENAME mydb.mytable;
  5. DROP TABLE mydb.mytablezap;

4. 减小磁盘写入操做

4.1 使用足够大的写入缓存 innodb_log_file_size

可是须要注意若是用 1G 的 innodb_log_file_size ,假如服务器当机,须要 10 分钟来恢复。

推荐 innodb_log_file_size 设置为 0.25 * innodb_buffer_pool_size

4.2 innodb_flush_log_at_trx_commit

0:日志缓冲每秒一次地被写到日志文件,而且对日志文件作到磁盘操做的刷新, 可是在一个事务提交不作任何操做。

1:在每一个事务提交时,日志缓冲被写到日志文件,对日志文件作到磁盘操做的 刷新。

2:在每一个提交,日志缓冲被写到文件,但不对日志文件作到磁盘操做的刷新。 对日志文件每秒刷新一次。

默认值是 1,也是最安全的设置,即每一个事务提交的时候都会从 log buffer 写 到日志文件,并且会实际刷新磁盘,可是这样性能有必定的损失。若是能够容忍在数 据库崩溃的时候损失一部分数据,那么设置成 0 或者 2 都会有所改善。设置成 0,则 在数据库崩溃的时候会丢失那些没有被写入日志文件的事务,最多丢失 1 秒钟的事 务,这种方式是最不安全的,也是效率最高的。设置成 2 的时候,由于只是没有刷新 到磁盘,可是已经写入日志文件,因此只要操做系统没有崩溃,那么并无丢失数据 , 比设置成 0 更安全一些。

这个选项和写磁盘操做密切相关:

 
  1. innodb_flush_log_at_trx_commit = 1 则每次修改写入磁盘
  2. innodb_flush_log_at_trx_commit = 0/2 每秒写入磁盘

若是你的应用不涉及很高的安全性 (金融系统),或者基础架构足够安全,或者 事务都很小,均可以用 0 或者 2 来下降磁盘操做。

4.3 避免双写入缓冲

 
  1. innodb_flush_method=O_DIRECT

5. 提升磁盘读写速度

RAID0 尤为是在使用 EC2 这种虚拟磁盘 (EBS) 的时候,使用软 RAID0 很是重要。

6. 充分使用索引

6.1 查看现有表结构和索引

 
  1. SHOW CREATE TABLE db1.tb1\G

6.2 添加必要的索引

索引是提升查询速度的惟一方法,好比搜索引擎用的倒排索引是同样的原理。

索引的添加须要根据查询来肯定,好比经过慢查询日志或者查询日志,或者经过 EXPLAIN 命令分析查询。

 
  1. ADD UNIQUE INDEX
  2. ADD INDEX

6.2.1 好比,优化用户验证表:

添加索引

 
  1. ALTER TABLE users ADD UNIQUE INDEX username_ndx (username);
  2. ALTER TABLE users ADD UNIQUE INDEX username_password_ndx (username,password);

每次重启服务器进行数据预热

 
  1. echo “select username,password from users;” > /var/lib/mysql/upcache.sql

添加启动脚本到 my.cnf

 
  1. [mysqld]
  2. init-file=/var/lib/mysql/upcache.sql

6.2.2 使用自动加索引的框架或者自动拆分表结构的框架

好比,Rails 这样的框架,会自动添加索引,Drupal 这样的框架会自动拆分表结构。会在你开发的初期指明正确的方向。因此,经验不太丰富的人一开始就追求从 0 开始构建,实际是很差的作法。

7. 分析查询日志和慢查询日志

记录全部查询,这在用 ORM 系统或者生成查询语句的系统颇有用。

 
  1. log=/var/log/mysql.log

注意不要在生产环境用,不然会占满你的磁盘空间。

记录执行时间超过 1 秒的查询:

 
  1. long_query_time=1
  2. log-slow-queries=/var/log/mysql/log-slow-queries.log

8. 激进的方法,使用内存磁盘

如今基础设施的可靠性已经很是高了,好比 EC2 几乎不用担忧服务器硬件当机。并且内存实在是便宜,很容易买到几十G内存的服务器,能够用内存磁盘,按期备份到磁盘。

将 MYSQL 目录迁移到 4G 的内存磁盘

 
  1. mkdir -p /mnt/ramdisk
  2. sudo mount -t tmpfs -o size=4000M tmpfs /mnt/ramdisk/
  3. mv /var/lib/mysql /mnt/ramdisk/mysql
  4. ln -s /tmp/ramdisk/mysql /var/lib/mysql
  5. chown mysql:mysql mysql

9. 用 NOSQL 的方式使用 MYSQL

B-TREE 仍然是最高效的索引之一,全部 MYSQL 仍然不会过期。

用 HandlerSocket 跳过 MYSQL 的 SQL 解析层,MYSQL 就真正变成了 NOSQL。

10. 其余

  • 单条查询最后增长 LIMIT 1,中止全表扫描。
  • 将非”索引”数据分离,好比将大篇文章分离存储,不影响其余自动查询。
  • 不用 MYSQL 内置的函数,由于内置函数不会创建查询缓存。
  • PHP 的创建链接速度很是快,全部能够不用链接池,不然可能会形成超过链接数。固然不用链接池 PHP 程序也可能将
  • 链接数占满好比用了 @ignore_user_abort(TRUE);
  • 使用 IP 而不是域名作数据库路径,避免 DNS 解析问题

11. 结束

你会发现优化后,数据库的性能提升几倍到几百倍。因此 MYSQL 基本仍是能够适用大部分场景的应用的。优化现有系统的成本比系统重构或者迁移到 NOSQL 低不少。

相关文章
相关标签/搜索