資料庫優化方案
阿新 • • 發佈:2020-03-06
[TOC]
MYSQL 應該是最流行了 WEB 後端資料庫。WEB 開發語言近期發展非常快,PHP, Ruby, Python, Java 各有特點,儘管 NOSQL 近期越來越多的被提到,可是相信大部分架構師還是會選擇 MYSQL 來做資料儲存。
MYSQL 如此方便和穩定。以至於我們在開發 WEB 程式的時候非常少想到它。即使想到優化也是程式級別的,比方。不要寫過於消耗資源的 SQL 語句。可是除此之外,在整個系統上仍然有非常多能夠優化的地方。
![](https://img2020.cnblogs.com/blog/1736414/202003/1736414-20200306182206588-523746094.png)
# 一、 選擇合適的儲存引擎: 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.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 server上是否有足夠記憶體用來規劃
執行 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 server。能夠禁用 SWAP,假設是共享server,確定 innodb_buffer_pool_size 足夠大。或者使用固定的記憶體空間做快取,使用 memlock 指令。
# 三、定期優化重建資料庫
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.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 每秒寫入磁碟
假設你的應用不涉及非常高的安全性 (金融系統),或者基礎架構足夠安全,或者 事務都非常小,都能夠用 0 或者 2 來減少磁碟操作。
## 4.3 避免雙寫入緩衝
```
innodb_flush_method=O_DIRECT
```
# 五、提高磁碟讀寫速度
RAID0 尤其是在使用 EC2 這樣的虛擬磁碟 (EBS) 的時候,使用軟 RAID0 很重要。
# 六、充分使用索引
## 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);
```
每次重新啟動server進行資料預熱
```
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 開始構建,實際是不好的做法。
# 七、分析查詢日誌和慢查詢日誌
記錄全部查詢。這在用 ORM 系統或者生成查詢語句的系統非常實用。
```
log=/var/log/mysql.log
```
注意不要在生產環境用。否則會佔滿你的磁碟空間。
記錄執行時間超過 1 秒的查詢:
```
long_query_time=1
log-slow-queries=/var/log/mysql/log-slow-queries.log
```
# 八、激進的方法。使用記憶體磁碟
如今基礎設施的可靠性已經非常高了,比方 EC2 差點兒不用操心server硬體當機。並且記憶體實在是廉價。非常easy買到幾十G記憶體的server,能夠用記憶體磁碟。定期備份到磁碟。
將 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
```
# 九、用 NOSQL 的方式使用 MYSQL
B-TREE 仍然是最高效的索引之中的一個,全部 MYSQL 仍然不會過時。
用 HandlerSocket 跳過 MYSQL 的 SQL 解析層。MYSQL 就真正變成了 NOSQL。
# 十、其它
- 單條查詢最後新增 LIMIT 1,停止全表掃描。
- 將非”索引”資料分離,比方將大篇文章分離儲存,不影響其它自己主動查詢。
- 不用 MYSQL 內建的函式。由於內建函式不會建立查詢快取。
- PHP 的建立連線速度很快,全部能夠不用連線池。否則可能會造成超過連線數。當然不用連線池 PHP 程式也可能將
- 連線數佔滿比方用了 @ignore_user_abort(TRUE);
- 使用 IP 而不是域名做資料庫路徑。避免 DNS 解