MYSQL 應該是最流行了 WEB 后端數(shù)據(jù)庫。WEB 開發(fā)語言近期發(fā)展非???,PHP, Ruby, Python, Java?各有特點,盡管 NOSQL 近期越來越多的被提到,可是相信大部分架構(gòu)師還是會選擇 MYSQL 來做數(shù)據(jù)存儲。
MYSQL 如此方便和穩(wěn)定。以至于我們在開發(fā) WEB 程序的時候非常少想到它。即使想到優(yōu)化也是程序級別的,比方。不要寫過于消耗資源的 SQL 語句??墒浅酥?,在整個系統(tǒng)上仍然有非常多能夠優(yōu)化的地方。

1. 選擇合適的存儲引擎: InnoDB
除非你的數(shù)據(jù)表使用來做僅僅讀或者全文檢索 (相信如今提到全文檢索,沒人會用 MYSQL 了)。你應該默認選擇?InnoDB 。
你自己在測試的時候可能會發(fā)現(xiàn) MyISAM 比 InnoDB 速度快。這是由于:?MyISAM 僅僅緩存索引,而 InnoDB 緩存數(shù)據(jù)和索引,MyISAM 不支持事務??墒?假設你使用?innodb_flush_log_at_trx_commit = 2?能夠獲得接近的讀取性能 (相差百倍)?。
1.1 怎樣將現(xiàn)有的 MyISAM 數(shù)據(jù)庫轉(zhuǎn)換為 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 為每一個表分別創(chuàng)建 InnoDB FILE:
innodb_file_per_table=1
這樣能夠保證 ibdata1 文件不會過大。失去控制。尤其是在運行?mysqlcheck -o –all-databases?的時候。
2. 保證從內(nèi)存中讀取數(shù)據(jù)。講數(shù)據(jù)保存在內(nèi)存中
2.1?足夠大的 innodb_buffer_pool_size
推薦將數(shù)據(jù)全然保存在 innodb_buffer_pool_size ,即按存儲量規(guī)劃 innodb_buffer_pool_size 的容量。這樣你能夠全然從內(nèi)存中讀取數(shù)據(jù)。最大限度降低磁盤操作。
2.1.1 怎樣確定 innodb_buffer_pool_size 足夠大。數(shù)據(jù)是從內(nèi)存讀取而不是硬盤?
方法 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)
發(fā)現(xiàn) Innodb_buffer_pool_pages_free 為 0,則說明 buffer pool 已經(jīng)被用光,須要增大?innodb_buffer_pool_size
InnoDB 的其它幾個參數(shù):
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 server上是否有足夠內(nèi)存用來規(guī)劃
運行?echo 1 > /proc/sys/vm/drop_caches?清除操作系統(tǒng)的文件緩存。能夠看到真正的內(nèi)存使用量。
2.2 數(shù)據(jù)預熱
默認情況,僅僅有某條數(shù)據(jù)被讀取一次,才會緩存在 innodb_buffer_pool。所以,數(shù)據(jù)庫剛剛啟動,須要進行數(shù)據(jù)預熱,將磁盤上的全部數(shù)據(jù)緩存到內(nèi)存中。
數(shù)據(jù)預熱能夠提高讀取速度。
對于 InnoDB 數(shù)據(jù)庫,能夠用下面方法,進行數(shù)據(jù)預熱:
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. 每次重新啟動數(shù)據(jù)庫,或者整庫備份前須要預熱的時候運行:
mysql -uroot < /root/SelectQueriesToLoad.sql > /dev/null 2>&1
2.3 不要讓數(shù)據(jù)存到 SWAP 中
假設是專用 MYSQL server。能夠禁用 SWAP,假設是共享server,確定 innodb_buffer_pool_size 足夠大?;蛘呤褂霉潭ǖ膬?nèi)存空間做緩存,使用?memlock 指令。
3. 定期優(yōu)化重建數(shù)據(jù)庫
mysqlcheck -o –all-databases 會讓 ibdata1 不斷增大。真正的優(yōu)化僅僅有重建數(shù)據(jù)表結(jié)構(gòu):
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 。假如server當機。須要 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 每秒寫入磁盤
假設你的應用不涉及非常高的安全性 (金融系統(tǒng)),或者基礎架構(gòu)足夠安全,或者 事務都非常小,都能夠用?0 或者 2 來減少磁盤操作。
4.3 避免雙寫入緩沖
innodb_flush_method=O_DIRECT
5. 提高磁盤讀寫速度
RAID0?尤其是在使用 EC2 這樣的虛擬磁盤 (EBS) 的時候,使用軟?RAID0?很重要。
6. 充分使用索引
6.1 查看現(xiàn)有表結(jié)構(gòu)和索引
SHOW CREATE TABLE db1.tb1/G
6.2 加入必要的索引
索引是提高查詢速度的唯一方法。比方搜索引擎用的倒排索引是一樣的原理。
索引的加入須要依據(jù)查詢來確定。比方通過慢查詢?nèi)罩净蛘卟樵內(nèi)罩?或者通過 EXPLAIN 命令分析查詢。
ADD UNIQUE INDEX
ADD INDEX
6.2.1 比方,優(yōu)化用戶驗證表:
加入索引
ALTER TABLE users ADD UNIQUE INDEX username_ndx (username);
ALTER TABLE users ADD UNIQUE INDEX username_password_ndx (username,password);
每次重新啟動server進行數(shù)據(jù)預熱
echo “select username,password from users;” > /var/lib/mysql/upcache.sql
加入啟動腳本到 my.cnf
[mysqld]
init-file=/var/lib/mysql/upcache.sql
6.2.2 使用自己主動加索引的框架或者自己主動拆分表結(jié)構(gòu)的框架
比方。Rails 這種框架。會自己主動加入索引。Drupal 這種框架會自己主動拆分表結(jié)構(gòu)。
會在你開發(fā)的初期指明正確的方向。所以,經(jīng)驗不太豐富的人一開始就追求從 0 開始構(gòu)建,實際是不好的做法。
7. 分析查詢?nèi)罩竞吐樵內(nèi)罩?/p>
記錄全部查詢。這在用 ORM 系統(tǒng)或者生成查詢語句的系統(tǒng)非常實用。
log=/var/log/mysql.log
注意不要在生產(chǎn)環(huán)境用。否則會占滿你的磁盤空間。
記錄運行時間超過 1 秒的查詢:
long_query_time=1
log-slow-queries=/var/log/mysql/log-slow-queries.log
8. 激進的方法。使用內(nèi)存磁盤
如今基礎設施的可靠性已經(jīng)非常高了,比方 EC2 差點兒不用操心server硬件當機。并且內(nèi)存實在是廉價。非常easy買到幾十G內(nèi)存的server,能夠用內(nèi)存磁盤。定期備份到磁盤。
將 MYSQL 文件夾遷移到 4G 的內(nèi)存磁盤
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,停止全表掃描。
將非”索引”數(shù)據(jù)分離,比方將大篇文章分離存儲,不影響其它自己主動查詢。
不用 MYSQL 內(nèi)置的函數(shù)。由于內(nèi)置函數(shù)不會建立查詢緩存。
PHP 的建立連接速度很快,全部能夠不用連接池。否則可能會造成超過連接數(shù)。當然不用連接池 PHP 程序也可能將
連接數(shù)占滿比方用了 @ignore_user_abort(TRUE);
使用 IP 而不是域名做數(shù)據(jù)庫路徑。避免 DNS 解析問題