MYSQL应该是最流行了WEB后端数据库。WEB开发语言最近发展很快,PHP,Ruby, Python, Java各有特点,虽然NOSQL最近越來越多的被提到,但是相信大部分架构师还是会选择MYSQL来做数据存储。
MYSQL如此方便和稳定,以至于我们在开发WEB程序的时候很少想到它。即使想到优化也是程序级别的,比如,不要写过于消耗资源的SQL语句。但是除此之外,在整个系统上仍然有很多可以优化的地方。
1.选择合适的存储引擎: InnoDB
除非你的数据表使用来做只读或者全文检索(相信现在提到全文检索,没人会用MYSQL了),你应该默认选择InnoDB。
你自己在测试的时候可能会发现MyISAM比InnoDB速度快,这是因为:MyISAM只缓存索引,而InnoDB缓存数据和索引,MyISAM不支持事务。但是 如果你使用innodb_flush_log_at_trx_commit = 2可以获得接近的读取性能(相差百倍)。
1.1如何将现有的MyISAM数据库转换为InnoDB:
mysql -u [USER_NAME] -p -e "SHOW TABLES IN [DATABASE_NAME];" | tail -n +2 | xargs -I '{}' echo "ALTER TABLE {} ENGINE=InnoDB;" > alter_table.sql
perl -p -i -e 's/(search_[a-z_]+ ENGINE=)InnoDB/\1MyISAM/g' alter_table.sql
mysql -u [USER_NAME] -p [DATABASE_NAME] < alter_table.sql
1.2为每个表分别创建InnoDB FILE:
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
mysql> SHOW GLOBAL STATUS LIKE 'innodb_buffer_pool_pages_%';
+----------------------------------+--------+
| Variable_name | Value |
+----------------------------------+--------+
| Innodb_buffer_pool_pages_data | 129037 |
| Innodb_buffer_pool_pages_dirty | 362 |
| Innodb_buffer_pool_pages_flushed | 9998 |
| Innodb_buffer_pool_pages_free | 0 | !!!!!!!!
| Innodb_buffer_pool_pages_misc | 2035 |
| Innodb_buffer_pool_pages_total | 131072 |
+----------------------------------+--------+
6 rows in set (0.00 sec)
发现Innodb_buffer_pool_pages_free为0,则说明buffer pool已经被用光,需要增大innodb_buffer_pool_size
InnoDB的其他几个参数:
innodb_additional_mem_pool_size = 1/200 of buffer_pool
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
SELECT DISTINCT
CONCAT('SELECT ',ndxcollist,' FROM ',db,'.',tb,
' ORDER BY ',ndxcollist,';') SelectQueryToLoadCache
FROM
(
SELECT
engine,table_schema db,table_name tb,
index_name,GROUP_CONCAT(column_name ORDER BY seq_in_index) ndxcollist
FROM
(
SELECT
B.engine,A.table_schema,A.table_name,
A.index_name,A.column_name,A.seq_in_index
FROM
information_schema.statistics A INNER JOIN
(
SELECT engine,table_schema,table_name
FROM information_schema.tables WHERE
engine='InnoDB'
) B USING (table_schema,table_name)
WHERE B.table_schema NOT IN ('information_schema','mysql')
ORDER BY table_schema,table_name,index_name,seq_in_index
) A
GROUP BY table_schema,table_name,index_name
) AA
ORDER BY db,tb
;
2.执行
mysql -uroot -AN < /root/MakeSelectQueriesToLoad.sql > /root/SelectQueriesToLoad.sql
3.每次重启数据库,或者整库备份前需要预热的时候执行:
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不断增大,真正的优化只有重建数据表结构:
CREATE TABLE mydb.mytablenew LIKE mydb.mytable;
INSERT INTO mydb.mytablenew SELECT * FROM mydb.mytable;
ALTER TABLE mydb.mytable RENAME mydb.mytablezap;
ALTER TABLE mydb.mytablenew RENAME mydb.mytable;
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
这个选项和写磁盘操作密切相关:
innodb_flush_log_at_trx_commit = 1则每次修改写入磁盘
innodb_flush_log_at_trx_commit = 0/2每秒写入磁盘
如果你的应用不涉及很高的安全性(金融系统),或者基础架构足够安全,或者 事务都很小,都可以用0或者2来降低磁盘操作。
4.3避免双写入缓冲
innodb_flush_method=O_DIRECT
5.提高磁盘读写速度
RAID0尤其是在使用EC2这种虚拟磁盘(EBS)的时候,使用软RAID0非常重要。
6.充分使用索引
6.1查看现有表结构和索引
SHOW CREATE TABLE db1.tb1\G
6.2添加必要的索引
索引是提高查询速度的唯一方法,比如搜索引擎用的倒排索引是一样的原理。
索引的添加需要根据查询来确定,比如通过慢查询日志或者查询日志,或者通过EXPLAIN命令分析查询。
ADD UNIQUE INDEX
ADD INDEX
6.2.1比如,优化用户验证表:
添加索引
ALTER TABLE users ADD UNIQUE INDEX username_ndx (username);
ALTER TABLE users ADD UNIQUE INDEX username_password_ndx (username,password);
每次重启服务器进行数据预热
echo “select username,password from users;” > /var/lib/mysql/upcache.sql
添加启动脚本到my.cnf
[mysqld]
init-file=/var/lib/mysql/upcache.sql
6.2.2使用自动加索引的框架或者自动拆分表结构的框架
比如,Rails这样的框架,会自动添加索引,Drupal这样的框架会自动拆分表结构。会在你开发的初期指明正确的方向。所以,经验不太丰富的人一开始就追求从0开始构建,实际是不好的做法。
7.分析查询日志和慢查询日志
记录所有查询,这在用ORM系统或者生成查询语句的系统很有用。
log=/var/log/mysql.log
注意不要在生产环境用,否则会占满你的磁盘空间。
记录执行时间超过1秒的查询:
long_query_time=1
log-slow-queries=/var/log/mysql/log-slow-queries.log
8.激进的方法,使用内存磁盘
现在基础设施的可靠性已经非常高了,比如EC2几乎不用担心服务器硬件当机。而且内存实在是便宜,很容易买到几十G内存的服务器,可以用内存磁盘,定期备份到磁盘。
将MYSQL目录迁移到4G的内存磁盘
mkdir -p /mnt/ramdisk
sudo mount -t tmpfs -o size=4000M tmpfs /mnt/ramdisk/
mv /var/lib/mysql /mnt/ramdisk/mysql
ln -s /tmp/ramdisk/mysql /var/lib/mysql
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低很多。
学PHP、Linux、HTML5、UI、Android等视频教程(课件+笔记+视频)!联系Q2430675018
Linux交流群:478068715欢迎各位加入!