標籤:style blog http io ar os 使用 sp for
說明:本文的環境為CENTOS 5.5 64 Bit /Mysql 5.1.50
簡介:使用Mysql有一段時間了,期間做了不少關於Mysql最佳化、設計、維護的工作,這兩天有時間做一下簡單的總結,方便自己回憶,同時也希望能對大家有點協助.
I 硬體設定最佳化
- CPU選擇:多核的CPU,主頻高的CPU
- 記憶體:更大的記憶體
- 磁碟選擇:更快的轉速、RAID、陣列卡,
- 網路環境選擇:盡量部署在區域網路、SCI、光纜、千兆網、雙網線提供冗餘、0.0.0.0多連接埠綁定監聽
II 作業系統級最佳化
- 使用64位的作業系統,更好的使用大記憶體。
- 設定noatime,nodiratime
[[email protected]_server1 ~]$ cat /etc/fstab
LABEL=/ / ext3 defaults,noatime,nodiratime 1 1
/dev/sda5 /data xfs defaults,noatime,nodiratime 1 2
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_max_syn_backlog=1024
net.ipv4.tcp_syncookies=1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.neigh.default.gc_thresh3 = 2048
net.ipv4.neigh.default.gc_thresh2 = 1024
net.ipv4.neigh.default.gc_thresh1 = 256
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.forwarding = 1
net.ipv4.conf.default.proxy_arp = 0
net.ipv4.tcp_syncookies = 1
net.core.netdev_max_backlog = 2048
net.core.dev_weight = 64
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_rfc1337 = 1
net.ipv4.tcp_sack = 0
net.ipv4.tcp_fin_timeout = 20
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_max_orphans = 32768
net.core.optmem_max = 20480
net.core.rmem_default = 16777216
net.core.rmem_max = 16777216
net.core.wmem_default = 16777216
net.core.wmem_max = 16777216
net.core.somaxconn = 500
net.ipv4.tcp_orphan_retries = 1
net.ipv4.tcp_max_tw_buckets = 18000
net.ipv4.ip_forward = 0
net.ipv4.conf.default.proxy_arp = 0
net.ipv4.conf.all.rp_filter = 1
kernel.sysrq = 1
net.ipv4.conf.default.send_redirects = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.ip_local_port_range = 5000 65000
kernel.shmmax = 167108864
vm.swappiness=0
Vim /etc/security/limits.conf
加上
* soft nofile 65535
* hard nofile 65535
/dev/sda5 /data xfs defaults,noatime,nodiratime 1 2
III Mysql設計最佳化
III.1儲存引擎的選擇
- Myisam:資料庫並發不大,讀多寫少,而且都能很好的用到索引,sql語句比較簡單的應用,TB資料倉儲
- Innodb:並發訪問大,寫操作比較多,有外鍵、事務等需求的應用,系統記憶體較大。
III.2命名規則
- 多數開發語言命名規則:比如MyAdress
- 多數開源思想命名規則:my_address
- 避免隨便命名
III.3欄位類型選擇
欄位類型的選擇的一般原則:
- 根據需求選擇合適的欄位類型,在滿足需求的情況下欄位類型儘可能小。
- 只分配滿足需求的最小字元數,不要太慷慨。
原因:更小的欄位類型更小的字元數佔用更少的記憶體,佔用更少的磁碟空間,佔用更少的磁碟IO,以及佔用更少的頻寬。
III.3.1 整型:
見如:
類型 |
位元組 |
最小值 |
最大值 |
| |
|
(帶符號的/無符號的) |
(帶符號的/無符號的) |
TINYINT |
1 |
-128 |
127 |
| |
|
0 |
255 |
SMALLINT |
2 |
-32768 |
32767 |
| |
|
0 |
65535 |
MEDIUMINT |
3 |
-8388608 |
8388607 |
| |
|
0 |
16777215 |
INT |
4 |
-2147483648 |
2147483647 |
| |
|
0 |
4294967295 |
BIGINT |
8 |
-9223372036854775808 |
9223372036854775807 |
| |
|
0 |
18446744073709551615 |
根據滿足需求的最小整數為選擇原則,能用INT的就不要用BIGINT。
用無符號INT儲存IP,而非CHAR(15)。
III.3.2 浮點型:
類型 |
位元組 |
精度類型 |
使用情境 |
FLOAT(M,D) |
4 |
單精確度 |
精度要求不高,數值比較小 |
DOUBLE(M,D)(REAL) |
8 |
雙精確度 |
精度要求不高,數值比較大 |
DECIMAL(M,D)(NUMERIC) |
M+2 |
自訂精度 |
精度要求很高的情境 |
III.3.3 時間類型
類型 |
取值範圍 |
儲存空間 |
零值標記法 |
DATE |
1000-01-01~9999-12-31 |
3位元組 |
0000-00-00 |
TIME |
-838:59:59~838:59:59 |
3位元組 |
00:00:00 |
DATETIME |
1000-01-01 00:00:00~9999-12-31 23:59:59 |
8位元組 |
0000-00-00 00:00:00 |
TIMESTAMP |
19700101000000~2037年的某個時刻 |
4位元組 |
00000000000000 |
YEAR |
YEAR(4):1901~2155 YEAR(2):1970~2069 |
1位元組 |
0000 |
III.3.4 字元類型
類型 |
最大長度 |
佔用儲存空間 |
CHAR[(M)] |
M位元組 |
M位元組 |
VARCHAR[(M)] |
M位元組 |
M+1位元組 |
TINYBLOD,TINYTEXT |
2^8-1位元組 |
L+1位元組 |
BLOB,TEXT |
2^16-1位元組 |
L+2 |
MEDIUMBLOB,MEDIUMTEXT |
2^24-1位元組 |
L+3 |
LONGBLOB,LONGTEXT |
2^32-1位元組 |
L+4 |
ENUM(‘value1‘,‘value2‘,...) |
65535個成員 |
1或2位元組 |
SET(‘value1‘,‘value2‘,...) |
64個成員 |
1,2,3,4或8位元組 |
註:L表示可變長度的意思
對於varchar和char的選擇要根據引擎和具體情況的不同來選擇,主要依據如下原則:
- 如果列資料項目的大小一致或者相差不大,則使用char。
- 如果列資料項目的大小差異相當大,則使用varchar。
- 對於MyISAM表,盡量使用Char,對於那些經常需要修改而容易形成片段的myisam和isam資料表就更是如此,它的缺點就是佔用磁碟空間。
- 對於InnoDB表,因為它的資料行內部儲存格式對固定長度的資料行和可變長度的資料行不加區分(所有資料行共用一個表頭部分,這個標題部分存放著指向各有關資料列的指標),所以使用char類型不見得會比使用varchar類型好。事實上,因為char類型通常要比varchar類型佔用更多的空 間,所以從減少空間佔用量和減少磁碟i/o的角度,使用varchar類型反而更有利。
- 表中只要存在一個varchar類型的欄位,那麼所有的char欄位都會自動變成varchar類型,因此建議定長和變長的資料分開。
III.4編碼選擇
單位元組 latin1
多位元組 utf8(漢字佔3個位元組,英文字母佔用一個位元組)
如果含有中文字元的話最好都統一採用utf8類型,避免亂碼的情況發生。
III.5主鍵選擇原則
註:這裡說的主鍵設計主要是針對INNODB引擎
- 能唯一的表示行。
- 顯式的定義一個數實值型別自增欄位的主鍵,這個欄位可以僅用於做主鍵,不做其他用途。
- MySQL主鍵應該是單列的,以便提高串連和篩選操作的效率。
- 主鍵欄位類型儘可能小,能用SMALLINT就不用INT,能用INT就不用BIGINT。
- 盡量保證不對主鍵欄位進行更新修改,防止主鍵欄位發生變化,引發資料存放區片段,降低IO效能。
- MySQL主鍵不應包含動態變化的資料,如時間戳記、建立時間列、修改時間列等。
- MySQL主鍵應當有電腦自動產生。
- 主鍵欄位放在資料表的第一順序。
推薦採用數實值型別做主鍵並採用auto_increment屬性讓其自動成長。
III.6其他需要注意的地方
儘可能設定每個欄位為NOT NULL,除非有特殊的需求,原因如下:
- 使用含有NULL列做索引的話會佔用更多的磁碟空間,因為索引NULL列需要而外的空間來儲存。
- 進行比較的時候,程式會更複雜。
- 含有NULL的列比較特殊,SQL難最佳化,如果是一個複合式索引,那麼這個NULL 類型的欄位會極大影響整個索引的效率。
索引的缺點:極大地加速了查詢,減少掃描和鎖定的資料行數。
索引的缺點:佔用磁碟空間,減慢了資料更新速度,增加了磁碟IO。
添加索引有如下原則:
- 選擇唯一性索引。
- 為經常需要排序、分組和聯合操作的欄位建立索引。
- 為常作為查詢條件的欄位建立索引。
- 限制索引的資料,索引不是越多越好。
- 盡量使用資料量少的索引,對於大欄位可以考慮首碼索引。
- 刪除不再使用或者很少使用的索引。
- 結合核心SQL優先考慮覆蓋索引。
- 忌用字串做主鍵。
適當的使用冗餘的反範式設計,以空間換時間有的時候會很高效。
IV Mysql軟體最佳化
- 開啟mysql複製,實現讀寫分離、負載平衡,將讀的負載分攤到多個從伺服器上,提高伺服器的處理能力。
- 使用推薦的GA版本,提升效能
- 利用分區新功能進行大資料的資料拆分
V Mysql配置最佳化
注意:全域參數一經設定,隨伺服器啟動預佔用資源。
mysql索引緩衝,如果是採用myisam的話要重點設定這個參數,根據(key_reads/key_read_requests)判斷
- innodb_buffer_pool_size參數
INNODB 資料、索引、日誌緩衝最重要的引擎參數,根據(hit riatos和FILE I/O)判斷
線程串連的逾時時間,盡量不要設定很大,推薦10s
伺服器允許的最大串連數,盡量不要設定太大,因為設定太大的話容易導致記憶體溢出,需要通過如下公式來確定:
SET @k_bytes = 1024;
SET @m_bytes = @k_bytes * 1024;
SET @g_bytes = @m_bytes * 1024;
SELECT
(
@@key_buffer_size + @@query_cache_size + @@tmp_table_size+
@@innodb_buffer_pool_size + @@innodb_additional_mem_pool_size+
@@innodb_log_buffer_size+
@@max_connections *
( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size+
@@join_buffer_size + @@binlog_cache_size + @@thread_stack
) )
/ @g_bytes AS MAX_MEMORY_USED_GB;
線程並發利用數量,(cpu+disk)*2,根據(os中顯示的請求隊列和tickets)判斷
獲得更快的--ORDER BY,GROUP BY,SELECT DISTINCT,UNION DISTINCT
當根據鍵進行分類操作時獲得更快的--ORDER BY
join串連使用全表掃描串連的緩衝大小,根據select_full_join判斷
全表掃描時為查詢預留的緩衝大小,根據select_scan判斷
臨時記憶體表的設定,如果超過設定就會轉化成磁碟表,根據參數(created_tmp_disk_tables)判斷
- innodb_log_file_size參數(預設5M)
記錄INNODB引擎的redo log檔案,設定較大的值意味著較長的恢復。
- innodb_flush_method參數(預設fdatasync)
Linux系統可以使用O_DIRECT處理資料檔案,避免OS層級的cache,O_DIRECT模式提高資料檔案和記錄檔的IO提交效能
- innodb_flush_log_at_trx_commit(預設1)
- 0表示每秒進行一次log寫入cache,並flush log到磁碟。
- 1表示在每次事務提交後執行log寫入cache,並flush log到磁碟。
- 2表示在每次事務提交後,執行log資料寫入到cache,每秒執行一次flush log到磁碟。
VI Mysql語句級最佳化
- 效能查的讀語句,在innodb中統計行數,建議另外弄一張統計表,採用myisam,定期做統計.一般的對統計的資料不會要求太精準的情況下適用。
- 盡量不要在資料庫中做運算。
- 避免負向查詢和%首碼模糊查詢。
- 不在索引列做運算或者使用函數。
- 不要在生產環境程式中使用select * from 的形式查詢資料。只查詢需要使用的列。
- 查詢儘可能使用limit減少返回的行數,減少資料轉送時間和頻寬浪費。
- where子句儘可能對查詢列使用函數,因為對查詢列使用函數用不到索引。
- 避免隱式類型轉換,例如字元型一定要用’’,數字型一定不要使用’’。
- 所有的SQL關鍵詞用大寫,養成良好的習慣,避免SQL語句重複編譯造成系統資源的浪費。
- 聯表查詢的時候,記得把小結果集放在前面,遵循小結果集驅動大結果集的原則。
- 開啟慢查詢,定期用explain最佳化慢查詢中的SQL語句。
1.對查詢進行最佳化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
2.應盡量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,
Sql 代碼 : select id from t where num is null;
可以在 num 上設定預設值 0,確保表中 num 列沒有 null 值,然後這樣查詢:
Sql 代碼 : select id from t where num=0;
3.應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。
4.應盡量避免在 where 子句中使用 or 來串連條件,否則將導致引擎放棄使用索引而進行全表掃描,
Sql 代碼 : select id from t where num=10 or num=20;
可以這樣查詢:
Sql 代碼 : select id from t where num=10 union all select id from t where num=20;
5.in 和 not in 也要慎用,否則會導致全表掃描,如:
Sql 代碼 : select id from t where num in(1,2,3);
對於連續的數值,能用 between 就不要用 in 了:
Sql 代碼 : select id from t where num between 1 and 3;
6.下面的查詢也將導致全表掃描:
Sql 代碼 : select id from t where name like ‘c%‘;
若要提高效率,可以考慮全文檢索索引。
7.如果在 where 子句中使用參數,也會導致全表掃描。因為 SQL 只有在運行時才會解析局部變數,但優 化程式不能將訪問計劃的選擇延遲到運行時;它必須在編譯時間進行選擇。然 而,如果在編譯時間建立訪問計 劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
Sql 代碼 : select id from t where [email protected] ;
可以改為強制查詢使用索引:
Sql 代碼 : select id from t with(index(索引名)) where [email protected] ;
8.應盡量避免在 where 子句中對欄位進行運算式操作, 這將導致引擎放棄使用索引而進行全表掃描。
Sql 代碼 : select id from t where num/2=100;
可以這樣查詢:
Sql 代碼 : select id from t where num=100*2;
9.應盡量避免在 where 子句中對欄位進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:
Sql 代碼 : select id from t where substring(name,1,3)=‘abc‘;#name 以 abc 開頭的 id
應改為:
Sql 代碼 : select id from t where name like ‘abc%‘;
10.不要在 where 子句中的“=”左邊進行函數、算術運算或其他運算式運算,否則系統將可能無法正確使用 索引。
11.在使用索引欄位作為條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個欄位作為條件 時才能保證系統使用該索引, 否則該索引將不會 被使用, 並且應儘可能的讓欄位順序與索引順序相一致。
12.不要寫一些沒有意義的查詢,如需要產生一個空表結構:
Sql 代碼 : select col1,col2 into #t from t where 1=0;
這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:
Sql 代碼 : create table #t(…);
13.很多時候用 exists 代替 in 是一個好的選擇:
Sql 代碼 : select num from a where num in(select num from b);
用下面的語句替換:
Sql 代碼 : select num from a where exists(select 1 from b where num=a.num);
14.並不是所有索引對查詢都有效,SQL 是根據表中資料來進行查詢最佳化的,當索引列有大量資料重複時, SQL 查詢可能不會去利用索引,如一表中有欄位 ***,male、female 幾乎各一半,那麼即使在 *** 上建 了索引也對查詢效率起不了作用。
15.索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要謹慎考慮,視具體情況而定。一個表的索引數最好不要超過 6 個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。
16.應儘可能的避免更新 clustered 索引資料列, 因為 clustered 索引資料列的順序就是表記錄的實體儲存體順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引資料列,那麼需要考慮是否應將該索引建為 clustered 索引。
17.盡量使用數字型欄位,若只含數值資訊的欄位盡量不要設計為字元型,這會降低查詢和串連的效能,並 會增加儲存開銷。這是因為引擎在處理查詢和串連時會逐個比較字串中每一個字元,而對於數字型而言 只需要比較一次就夠了。
18.儘可能的使用 varchar/nvarchar 代替 char/nchar , 因為首先變長欄位儲存空間小, 可以節省儲存空間, 其次對於查詢來說,在一個相對較小的欄位內搜尋效率顯然要高些。
19.任何地方都不要使用 select * from t ,用具體的欄位列表代替“*”,不要返回用不到的任何欄位。
20.盡量使用表變數來代替暫存資料表。如果表變數包含大量資料,請注意索引非常有限(只有主鍵索引)。
21.避免頻繁建立和刪除暫存資料表,以減少系統資料表資源的消耗。
22.暫存資料表並不是不可使用,適當地使用它們可以使某些常式更有效,例如,當需要重複引用大型表或常用 表中的某個資料集時。但是,對於一次性事件, 最好使用匯出表。
23.在建立暫存資料表時,如果一次性插入資料量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果資料量不大,為了緩和系統資料表的資源,應先 create table,然後 insert.
24.如果使用到了暫存資料表, 在預存程序的最後務必將所有的暫存資料表顯式刪除, 先 truncate table ,然後 drop table ,這樣可以避免系統資料表的較長時間鎖定。
25.盡量避免使用遊標,因為遊標的效率較差,如果遊標操作的資料超過 1 萬行,那麼就應該考慮改寫。
26.使用基於遊標的方法或暫存資料表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更 有效。
27.與暫存資料表一樣,遊標並不是不可使用。對小型資料集使用 FAST_FORWARD 遊標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的資料時。在結果集中包括“合計”的常式通常要比使用遊標執行的速度快。如果開發時間允許,基於遊標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。
28.在所有的預存程序和觸發器的開始處設定 SET NOCOUNT ON ,在結束時設定 SET NOCOUNT OFF .無需在執行預存程序和觸發器的每個語句後向用戶端發送 DONE_IN_PROC 訊息。
29.盡量避免大事務操作,提高系統並發能力。 sql 最佳化方法使用索引來更快地遍曆表。 預設情況下建立的索引是非群集索引,但有時它並不是最佳的。在非群集索引下,資料在物理上隨機存放在資料頁上。合理的索引設計要建立在對各種查詢的分析和預測上。一般來說:
a.有大量重複值、且經常有範圍查詢( > ,< ,> =,< =)和 order by、group by 發生的列,可考慮建立叢集索引;
b.經常同時存取多列,且每列都含有重複值可考慮建立複合式索引;
c.複合式索引要盡量使關鍵查詢形成索引覆蓋,其前置列一定是使用最頻繁的列。索引雖有助於提高效能但 不是索引越多越好,恰好相反過多的索引會導致系統低效。使用者在表中每加進一個索引,維護索引集合就 要做相應的更新工作。
30.定期分析表和檢查表。
分析表的文法:ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tb1_name[, tbl_name]...
以上語句用於分析和儲存表的關鍵字分布,分析的結果將可以使得系統得到準確的統計資訊,使得SQL能夠產生正確的執行計畫。如果使用者感覺實際執行計畫並不是預期的執行計畫,執行一次分析表可能會解決問題。在分析期間,使用一個讀取鎖定對錶進行鎖定。這對於MyISAM,DBD和InnoDB表有作用。
例如分析一個資料表:analyze table table_name
檢查表的文法:CHECK TABLE tb1_name[,tbl_name]...[option]...option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}檢查表的作用是檢查一個或多個表是否有錯誤,CHECK TABLE 對MyISAM 和 InnoDB表有作用,對於MyISAM表,關鍵字統計資料被更新
CHECK TABLE 也可以檢查視圖是否有錯誤,比如在視圖定義中被引用的表不存在。
31.定期最佳化表。
最佳化表的文法:OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tb1_name [,tbl_name]...
如果刪除了表的一大部分,或者如果已經對含有可變長度行的表(含有 VARCHAR、BLOB或TEXT列的表)進行更多更改,則應使用OPTIMIZE TABLE命令來進行表最佳化。這個命令可以將表中的空間片段進行合并,並且可以消除由於刪除或者更新造成的空間浪費,但OPTIMIZE TABLE 命令只對MyISAM、 BDB 和InnoDB表起作用。
例如: optimize table table_name
注意: analyze、check、optimize執行期間將對錶進行鎖定,因此一定注意要在MySQL資料庫不繁忙的時候執行相關的操作。
補充:
1、在海量查詢時盡量少用格式轉換。
2、ORDER BY 和 GROPU BY:使用 ORDER BY 和 GROUP BY 短語,任何一種索引都有助於 SELECT 的效能提高。
3、任何對列的操作都將導致表掃描,它包括資料庫教程函數、計算運算式等等,查詢時要儘可能將操作移 至等號右邊。
4、IN、OR 子句常會使用工作表,使索引失效。如果不產生大量重複值,可以考慮把子句拆開。拆開的子 句中應該包含索引。
5、只要能滿足你的需求,應儘可能使用更小的資料類型:例如使用 MEDIUMINT 代替 INT
6、盡量把所有的列設定為 NOT NULL,如果你要儲存 NULL,手動去設定它,而不是把它設為預設值。
7、盡量少用 VARCHAR、TEXT、BLOB 類型
8、如果你的資料只有你所知的少量的幾個。最好使用 ENUM 類型
9、正如 graymice 所講的那樣,建立索引。
10、合理用運分表與分區表提高資料存放和提取速度。
轉自:http://blog.chinaunix.net/uid-20639775-id-3154234.html
Mysql資料庫最佳化總結2