1. 程式人生 > >MYSQL千萬級資料量的優化方法積累

MYSQL千萬級資料量的優化方法積累

1.對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
2.應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描

如:select id from t where num is null可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢:select id from t where num=0

3.應儘量避免在 where 子句中使用!=或<>操作符,否則引擎將放棄使用索引而進行全表掃描。
4.應儘量避免在 where 子句中使用or 來連線條件,否則將導致引擎放棄使用索引而進行全表掃描

如:select id from t where num=10 or num=20可以這樣查詢:select id from t where num=10 union all select id from t where num=20

5.in 和 not in 也要慎用,否則會導致全表掃描

如:select id from t where num in(1,2,3) 對於連續的數值,能用 between 就不要用 in 了:select id from t where num between 1 and 3

6.下面的查詢也將導致全表掃描:select id from t where name like ‘%李%’若要提高效率,可以考慮全文檢索。
7.如果在 where 子句中使用引數,也會導致全表掃描。因為SQL只有在執行時才會解析區域性變數,但優化程式不能將訪問計劃的選擇推遲到執行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。

如下面語句將進行全表掃描:select id from t where [email protected]可以改為強制查詢使用索引:select id from t with(index(索引名)) where [email protected]

8.應儘量避免在 where 子句中對欄位進行表示式操作,這將導致引擎放棄使用索引而進行全表掃描。

如:select id from t where num/2=100應改為:select id from t where num=100*2

9.應儘量避免在where子句中對欄位進行函式操作,這將導致引擎放棄使用索引而進行全表掃描。

如:select id from t where substring(name,1,3)=’abc’ ,name以abc開頭的id應改為:select id from t where name like ‘abc%’

10.不要在 where 子句中的“=”左邊進行函式、算術運算或其他表示式運算,否則系統將可能無法正確使用索引。
11.在使用索引欄位作為條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個欄位作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應儘可能的讓欄位順序與索引順序相一致。
12.不要寫一些沒有意義的查詢

如需要生成一個空表結構:select col1,col2 into #t from t where 1=0

這類程式碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:

create table #t(…)

13.很多時候用 exists 代替 in 是一個好的選擇

select num from a where num in(select num from b)

用下面的語句替換:

select num from a where exists(select 1 from b where num=a.num)

14.並不是所有索引對查詢都有效,SQL是根據表中資料來進行查詢優化的,當索引列有大量資料重複時,SQL查詢可能不會去利用索引

如一表中有欄位sex,male、female幾乎各一半,那麼即使在sex上建了索引也對查詢效率起不了作用。

索引並不是越多越好,索引固然可 以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。
應儘可能的避免更新 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.使用基於遊標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。

與臨時表一樣,遊標並不是不可使 用。對小型資料集使用 FAST_FORWARD 遊標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的資料時。在結果集中包括“合計”的例程通常要比使用遊標執行的速度快。如果開發時 間允許,基於遊標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。

28.在所有的儲存過程和觸發器的開始處設定 SET NOCOUNT ON ,在結束時設定 SET NOCOUNT OFF 。無需在執行儲存過程和觸發器的每個語句後向客戶端傳送DONE_IN_PROC 訊息。
29.儘量避免大事務操作,提高系統併發能力.
30.儘量避免向客戶端返回大資料量,若資料量過大,應該考慮相應需求是否合理。

——————————————————————————

1、分庫分表

很明顯,一個主表(也就是很重要的表,例如使用者表)無限制的增長勢必嚴重影響效能,分庫與分表是一個很不錯的解決途徑,也就是效能優化途徑,現在的案例是我們有一個1000多萬條記錄的使用者表members,查詢起來非常之慢,同事的做法是將其雜湊到100個表中,分別從members0到members99,然後根據mid分發記錄到這些表中,牛逼的程式碼大概是這樣子:

<?php

for($i=0;$i< 100; $i++ ){

//echo "CREATE TABLE db2.members{$i} LIKE db1.members<br>";

echo "INSERT INTO members{$i} SELECT * FROM members WHERE mid0={$i}<br>";

}

?>
2、不停機修改mysql表結構

同樣還是members表,前期設計的表結構不盡合理,隨著資料庫不斷執行,其冗餘>資料也是增長巨大,同事使用了下面的方法來處理:

先建立一個臨時表:

CREATE TABLE members_tmp LIKE members

然後修改members_tmp的表結構為新結構,接著使用上面那個for迴圈來匯出資料,因為1000萬的資料一次性匯出是不對的,mid是主鍵,一個區間一個區間的導,基本是一次匯出5萬條吧,這裡略去了

接著重新命名將新表替換上去:

RENAME TABLE members TO members_bak,members_tmp TO members;

就是這樣,基本可以做到無損失,無需停機更新表結構,但實際上RENAME期間表是被鎖死的,所以選擇線上少的時候操作是一個技巧。經過這個操作,使得原先8G多的表,一下子變成了2G多

另外還講到了mysql中float欄位型別的時候出現的詭異現象,就是在pma中看到的數字根本不能作為條件來查詢

3、常用SQL語句優化:

資料庫(表)設計合理

我們的表設計要符合3NF 3正規化(規範的模式) , 有時我們需要適當的逆正規化

sql語句的優化(索引,常用小技巧.)

資料的配置(快取設大)

適當硬體配置和作業系統 (讀寫分離.)

資料的3NF

1NF :就是具有原子性,不可分割.(只要使用的是關係性資料庫,就自動符合)

2NF: 在滿足1NF 的基礎上,我們考慮是否滿足2NF: 只要表的記錄滿足唯一性,也是說,你的同一張表,不可能出現完全相同的記錄, 一般說我們在 表中設計一個主鍵即可.

3NF: 在滿足2NF 的基礎上,我們考慮是否滿足3NF:即我們的欄位資訊可以通過關聯的關係,派生即可.(通常我們通過外來鍵來處理)

逆正規化: 為什麼需呀逆正規化:

(相簿的功能對應資料庫的設計)

適當的逆正規化.

sql語句的優化

sql語句有幾類

ddl (資料定義語言) [create alter drop]

dml(資料操作語言)[insert delete upate ]

select

dtl(資料事務語句) [commit rollback savepoint]

dcl(資料控制語句) [grant revoke]

show status命令

該命令可以顯示你的mysql資料庫的當前狀態.我們主要關心的是 “com”開頭的指令

show status like ‘Com%’ <=> show session status like ‘Com%’ //顯示當前控制檯的情況

show global status like ‘Com%’ ; //顯示資料庫從啟動到 查詢的次數

顯示連線資料庫次數

show status like ‘Connections’;

這裡我們優化的重點是在 慢查詢. (在預設情況下是10 ) mysql5.5.19

顯示檢視慢查詢的情況

show variables like ‘long_query_time’

為了教學,我們搞一個海量表(mysql儲存過程)

目的,就是看看怎樣處理,在海量表中,查詢的速度很快!

select * from emp where empno=123456;

需求:如何在一個專案中,找到慢查詢的select , mysql資料庫支援把慢查詢語句,記錄到日誌中,程式設計師分析. (但是注意,預設情況下不啟動.)

步驟:

要這樣啟動mysql

進入到 mysql安裝目錄

啟動 xx>binmysqld.exe –slow-query-log 這點注意
測試 ,比如我們把

select * from emp where empno=34678 ;

用了1.5秒,我現在優化.

快速體驗: 在emp表的 empno建立索引.

alter table emp add primary key(empno);

//刪除主鍵索引

alter table emp drop primary key

然後,再查速度變快.

l 索引的原理

介紹一款非常重要工具explain, 這個分析工具可以對 sql語句進行分析,可以預測你的sql執行的效率.

他的基本用法是:

explain sql語句G

//根據返回的資訊,我們可知,該sql語句是否使用索引,從多少記錄中取出,可以看到排序的方式.

l 在什麼列上新增索引比較合適

① 在經常查詢的列上加索引.

② 列的資料,內容就只有少數幾個值,不太適合加索引.

③ 內容頻繁變化,不合適加索引

l 索引的種類

① 主鍵索引 (把某列設為主鍵,則就是主鍵索引)

② 唯一索引(unique) (即該列具有唯一性,同時又是索引)

③ index (普通索引)

④ 全文索引(FULLTEXT)

select * from article where content like ‘%李連杰%’;

hello, i am a boy

l 你好,我是一個男孩 =>中文 sphinx

⑤ 複合索引(多列和在一起)

create index myind on 表名 (列1,列2);

l 如何建立索引

如果建立unique / 普通/fulltext 索引

create [unique|FULLTEXT] index 索引名 on 表名 (列名…)
alter table 表名 add index 索引名 (列名…)
//如果要新增主鍵索引

alter table 表名 add primary key (列…)

刪除索引

drop index 索引名 on 表名

alter table 表名 drop index index_name;

alter table 表名 drop primary key

顯示索引

show index(es) from 表名

show keys from 表名

desc 表名

如何查詢某表的索引

show indexes from 表名

l 使用索引的注意事項

查詢要使用索引最重要的條件是查詢條件中需要使用索引。

下列幾種情況下有可能使用到索引:

1,對於建立的多列索引,只要查詢條件使用了最左邊的列,索引一般就會被使用。

2,對於使用like的查詢,查詢如果是 ‘�a’ 不會使用到索引 aaa%’ 會使用到索引。

下列的表將不使用索引:

1,如果條件中有or,即使其中有條件帶索引也不會使用。

2,對於多列索引,不是使用的第一部分,則不會使用索引。

3,like查詢是以%開頭

4,如果列型別是字串,那一定要在條件中將資料使用引號引用起來。否則不使用索引。

5,如果mysql估計使用全表掃描要比使用索引快,則不使用索引。

l 如何檢測你的索引是否有效

結論: Handler_read_key 越大越少

Handler_read_rnd_next 越小越好

fdisk

find

l MyISAM 和 Innodb區別是什麼

MyISAM 不支援外來鍵, Innodb支援

MyISAM 不支援事務,不支援外來鍵.

對資料資訊的儲存處理方式不同.(如果儲存引擎是MyISAM的,則建立一張表,對於三個檔案..,如果是Innodb則只有一張檔案 *.frm,資料存放到ibdata1)

對於 MyISAM 資料庫,需要定時清理

optimize table 表名

l 常見的sql優化手法

使用order by null 禁用排序

比如 select * from dept group by ename order by null

在精度要求高的應用中,建議使用定點數(decimal)來儲存數值,以保證結果的準確性

3. 如果欄位是字元型別的索引,用作條件查詢時一定要加單引號,不然索引無效。

主鍵索引如果沒用到,再查詢for update這種情況,會造成表鎖定。容易造成卡死。
1000000.32 萬

create table sal(t1 float(10,2));

create table sal2(t1 decimal(10,2));

問?在php中 ,int 如果是一個有符號數,最大值. int- 4*8=32 2 31 -1

l 表的水平劃分

l 垂直分割表

如果你的資料庫的儲存引擎是MyISAM的,則當建立一個表,後三個檔案. .frm 記錄表結構. .myd 資料 *.myi 這個是索引.

mysql5.5.19的版本,他的資料庫檔案,預設放在 (看 my.ini檔案中的配置.)