Mysql索引優(yōu)化

索引類似大學(xué)圖書館建書目索引,可以提高數(shù)據(jù)檢索的效率,降低數(shù)據(jù)庫(kù)的IO成本。MySQL在300萬條記錄左右性能開始逐漸下降,雖然官方文檔說500~800w記錄,所以大數(shù)據(jù)量建立索引是非常有必要的。MySQL提供了Explain,用于顯示SQL執(zhí)行的詳細(xì)信息,可以進(jìn)行索引的優(yōu)化。

一、導(dǎo)致SQL執(zhí)行慢的原因

1.硬件問題。如網(wǎng)絡(luò)速度慢,內(nèi)存不足,I/O吞吐量小,磁盤空間滿了等。?

2.沒有索引或者索引失效。(一般在互聯(lián)網(wǎng)公司,DBA會(huì)在半夜把表鎖了,重新建立一遍索引,因?yàn)楫?dāng)你刪除某個(gè)數(shù)據(jù)的時(shí)候,索引的樹結(jié)構(gòu)就不完整了。所以互聯(lián)網(wǎng)公司的數(shù)據(jù)做的是假刪除.一是為了做數(shù)據(jù)分析,二是為了不破壞索引 )

3.數(shù)據(jù)過多(分庫(kù)分表)

4.服務(wù)器調(diào)優(yōu)及各個(gè)參數(shù)設(shè)置(調(diào)整my.cnf)

二、分析原因時(shí),一定要找切入點(diǎn)

1.先觀察,開啟慢查詢?nèi)罩?,設(shè)置相應(yīng)的閾值(比如超過3秒就是慢SQL),在生產(chǎn)環(huán)境跑上個(gè)一天過后,看看哪些SQL比較慢。

2.Explain和慢SQL分析。比如SQL語(yǔ)句寫的爛,索引沒有或失效,關(guān)聯(lián)查詢太多(有時(shí)候是設(shè)計(jì)缺陷或者不得以的需求)等等。?

3.Show Profile是比Explain更近一步的執(zhí)行細(xì)節(jié),可以查詢到執(zhí)行每一個(gè)SQL都干了什么事,這些事分別花了多少秒。

4.找DBA或者運(yùn)維對(duì)MySQL進(jìn)行服務(wù)器的參數(shù)調(diào)優(yōu)。

三、什么是索引?

? ? ? ?MySQL官方對(duì)索引的定義為:索引(Index)是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。我們可以簡(jiǎn)單理解為:快速查找排好序的一種數(shù)據(jù)結(jié)構(gòu)。Mysql索引主要有兩種結(jié)構(gòu):B+Tree索引和Hash索引。我們平常所說的索引,如果沒有特別指明,一般都是指B樹結(jié)構(gòu)組織的索引(B+Tree索引)。索引如圖所示:

? ? ? ?最外層淺藍(lán)色磁盤塊1里有數(shù)據(jù)17、35(深藍(lán)色)和指針P1、P2、P3(黃色)。P1指針表示小于17的磁盤塊,P2是在17-35之間,P3指向大于35的磁盤塊。真實(shí)數(shù)據(jù)存在于子葉節(jié)點(diǎn)也就是最底下的一層3、5、9、10、13……非葉子節(jié)點(diǎn)不存儲(chǔ)真實(shí)的數(shù)據(jù),只存儲(chǔ)指引搜索方向的數(shù)據(jù)項(xiàng),如17、35。

? ? ? ?查找過程:例如搜索28數(shù)據(jù)項(xiàng),首先加載磁盤塊1到內(nèi)存中,發(fā)生一次I/O,用二分查找確定在P2指針。接著發(fā)現(xiàn)28在26和30之間,通過P2指針的地址加載磁盤塊3到內(nèi)存,發(fā)生第二次I/O。用同樣的方式找到磁盤塊8,發(fā)生第三次I/O。

? ? ? ?真實(shí)的情況是,上面3層的B+Tree可以表示上百萬的數(shù)據(jù),上百萬的數(shù)據(jù)只發(fā)生了三次I/O而不是上百萬次I/O,時(shí)間提升是巨大的。

四、Explain 分析

前文鋪墊完成,進(jìn)入實(shí)操部分,先來插入測(cè)試需要的數(shù)據(jù):

CREATE?TABLE?`user_info`?(

??`id`???BIGINT(20)??NOT?NULL?AUTO_INCREMENT,

??`name`?VARCHAR(50)?NOT?NULL?DEFAULT?'',

??`age`??INT(11)??????????????DEFAULT?NULL,

??PRIMARY KEY?(`id`),

??KEY?`name_index`?(`name`)

)ENGINE?=?InnoDB?DEFAULT?CHARSET?=?utf8;


INSERT INTO user_info?(name,?age)?VALUES?('xys',?20);

INSERT INTO user_info?(name,?age)?VALUES?('a',?21);

INSERT INTO user_info?(name,?age)?VALUES?('b',?23);

INSERT INTO user_info?(name,?age)?VALUES?('c',?50);

INSERT INTO user_info?(name,?age)?VALUES?('d',?15);

INSERT INTO user_info?(name,?age)?VALUES?('e',?20);

INSERT INTO user_info?(name,?age)?VALUES?('f',?21);

INSERT INTO user_info?(name,?age)?VALUES?('g',?23);

INSERT INTO user_info?(name,?age)?VALUES?('h',?50);

INSERT INTO user_info?(name,?age)?VALUES?('i',?15);


CREATE?TABLE?`order_info`?(

??`id`???????????BIGINT(20)??NOT?NULL?AUTO_INCREMENT,

??`user_id`??????BIGINT(20)???????????DEFAULT?NULL,

??`product_name`?VARCHAR(50)?NOT?NULL?DEFAULT?'',

??`productor`????VARCHAR(30)??????????DEFAULT?NULL,

??PRIMARY KEY?(`id`),

??KEY?`user_product_detail_index`?(`user_id`,?`product_name`,?`productor`)

)ENGINE?=?InnoDB?DEFAULT?CHARSET?=?utf8;


INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(1,?'p1',?'WHH');

INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(1,?'p2',?'WL');

INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(1,?'p1',?'DX');

INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(2,?'p1',?'WHH');

INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(2,?'p5',?'WL');

INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(3,?'p3',?'MA');

INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(4,?'p1',?'WHH');

INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(6,?'p1',?'WHH');

INSERT INTO order_info?(user_id,?product_name,?productor)?VALUES?(9,?'p8',?'TE');

初體驗(yàn),執(zhí)行Explain的效果:

索引使用情況在possible_keys、key和key_len三列,接下來我們先從左到右依次講解。

1.id

--id相同,執(zhí)行順序由上而下

explain?select?u.*,o.*?from?user_info?u,order_info?o?where?u.id=o.user_id;


--id不同,值越大越先被執(zhí)行

explain select *?from??user_info??where?id=(select user_id from order_info where??product_name?='p8');

2.select_type

可以看id的執(zhí)行實(shí)例,總共有以下幾種類型:

SIMPLE:?表示此查詢不包含 UNION 查詢或子查詢

PRIMARY:?表示此查詢是最外層的查詢

SUBQUERY:?子查詢中的第一個(gè) SELECT

UNION:?表示此查詢是 UNION 的第二或隨后的查詢

DEPENDENT UNION:?UNION 中的第二個(gè)或后面的查詢語(yǔ)句, 取決于外面的查詢

UNION RESULT, UNION 的結(jié)果

DEPENDENT SUBQUERY: 子查詢中的第一個(gè) SELECT, 取決于外面的查詢. 即子查詢依賴于外層查詢的結(jié)果.

DERIVED:衍生,表示導(dǎo)出表的SELECT(FROM子句的子查詢)

3.table

table表示查詢涉及的表或衍生的表:

explain select tt.* from (select u.* from user_info u,order_info o where u.id=o.user_id and u.id=1) tt

id為1的<derived2>的表示id為2的u和o表衍生出來的。

4.type

type 字段比較重要,它提供了判斷查詢是否高效的重要依據(jù)依據(jù)。 通過 type 字段,我們判斷此次查詢是 全表掃描 還是 索引掃描等。

type 常用的取值有:?

system: 表中只有一條數(shù)據(jù),?這個(gè)類型是特殊的 const 類型。

const: 針對(duì)主鍵或唯一索引的等值查詢掃描,最多只返回一行數(shù)據(jù)。 const 查詢速度非??欤?因?yàn)樗鼉H僅讀取一次即可。例如下面的這個(gè)查詢,它使用了主鍵索引,因此 type 就是 const 類型的:explain select * from user_info where id = 2;

eq_ref: 此類型通常出現(xiàn)在多表的 join 查詢,表示對(duì)于前表的每一個(gè)結(jié)果,都只能匹配到后表的一行結(jié)果。并且查詢的比較操作通常是 =,查詢效率較高。例如:explain select * from user_info, order_info where user_info.id = order_info.user_id;

ref: 此類型通常出現(xiàn)在多表的 join 查詢,針對(duì)于非唯一或非主鍵索引,或者是使用了 最左前綴 規(guī)則索引的查詢。例如下面這個(gè)例子中, 就使用到了 ref 類型的查詢:explain select * from user_info, order_info where user_info.id = order_info.user_id AND order_info.user_id = 5

range: 表示使用索引范圍查詢,通過索引字段范圍獲取表中部分?jǐn)?shù)據(jù)記錄。這個(gè)類型通常出現(xiàn)在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中。例如下面的例子就是一個(gè)范圍查詢:explain select * from user_info ?where id between 2 and 8;

index: 表示全索引掃描(full index scan),和 ALL 類型類似,只不過 ALL 類型是全表掃描,而 index 類型則僅僅掃描所有的索引, 而不掃描數(shù)據(jù)。index 類型通常出現(xiàn)在:所要查詢的數(shù)據(jù)直接在索引樹中就可以獲取到, 而不需要掃描數(shù)據(jù)。當(dāng)是這種情況時(shí),Extra 字段 會(huì)顯示 Using index。

ALL: 表示全表掃描,這個(gè)類型的查詢是性能最差的查詢之一。通常來說, 我們的查詢不應(yīng)該出現(xiàn) ALL 類型的查詢,因?yàn)檫@樣的查詢?cè)跀?shù)據(jù)量大的情況下,對(duì)數(shù)據(jù)庫(kù)的性能是巨大的災(zāi)難。 如一個(gè)查詢是 ALL 類型查詢, 那么一般來說可以對(duì)相應(yīng)的字段添加索引來避免。?

通常來說, 不同的 type 類型的性能關(guān)系如下:

ALL < index < range ~ index_merge < ref < eq_ref < const < system

ALL 類型因?yàn)槭侨頀呙瑁?因此在相同的查詢條件下,它是速度最慢的。而 index 類型的查詢雖然不是全表掃描,但是它掃描了所有的索引,因此比 ALL 類型的稍快.后面的幾種類型都是利用了索引來查詢數(shù)據(jù),因此可以過濾部分或大部分?jǐn)?shù)據(jù),因此查詢效率就比較高了。

5.possible_keys

它表示 mysql 在查詢時(shí),可能使用到的索引。 注意,即使有些索引在 possible_keys 中出現(xiàn),但是并不表示此索引會(huì)真正地被 mysql 使用到。?mysql 在查詢時(shí)具體使用了哪些索引,由 key 字段決定。

6.key

此字段是 mysql 在當(dāng)前查詢時(shí)所真正使用到的索引。比如請(qǐng)客吃飯,possible_keys是應(yīng)到多少人,key是實(shí)到多少人。當(dāng)我們沒有建立索引時(shí):

explain?select?o.*?from?order_info?o?where??o.product_name=?'p1'?and??o.productor='whh';

create index idx_name_productor on order_info(productor);

drop index idx_name_productor on?order_info;

建立復(fù)合索引后再查詢:

7.key_len

表示查詢優(yōu)化器使用了索引的字節(jié)數(shù),這個(gè)字段可以評(píng)估組合索引是否完全被使用。

8.ref

這個(gè)表示顯示索引的哪一列被使用了,如果可能的話,是一個(gè)常量。前文的type屬性里也有ref,注意區(qū)別。

9.rows

rows 也是一個(gè)重要的字段,mysql 查詢優(yōu)化器根據(jù)統(tǒng)計(jì)信息,估算 sql 要查找到結(jié)果集需要掃描讀取的數(shù)據(jù)行數(shù),這個(gè)值非常直觀的顯示 sql 效率好壞, 原則上 rows 越少越好??梢詫?duì)比key中的例子,一個(gè)沒建立索引錢,rows是9,建立索引后,rows是4。

10.extra

explain 中的很多額外的信息會(huì)在 extra 字段顯示, 常見的有以下幾種內(nèi)容:

using filesort :表示 mysql 需額外的排序操作,不能通過索引順序達(dá)到排序效果。一般有 using filesort都建議優(yōu)化去掉,因?yàn)檫@樣的查詢 cpu 資源消耗大。

using index:覆蓋索引掃描,表示查詢?cè)谒饕龢渲芯涂刹檎宜钄?shù)據(jù),不用掃描表數(shù)據(jù)文件,往往說明性能不錯(cuò)。

using temporary:查詢有使用臨時(shí)表, 一般出現(xiàn)于排序, 分組和多表 join 的情況, 查詢效率不高,建議優(yōu)化。

using where :表名使用了where過濾。

五、優(yōu)化案例

explain select u.*,o.* from user_info u LEFT JOIN? order_info o on u.id=o.user_id;

執(zhí)行結(jié)果,type有ALL,并且沒有索引:

開始優(yōu)化,在關(guān)聯(lián)列上創(chuàng)建索引,明顯看到type列的ALL變成ref,并且用到了索引,rows也從掃描9行變成了1行:

這里面一般有個(gè)規(guī)律是:左鏈接索引加在右表上面,右鏈接索引加在左表上面。

六、是否需要?jiǎng)?chuàng)建索引?

索引雖然能非常高效的提高查詢速度,同時(shí)卻會(huì)降低更新表的速度。實(shí)際上索引也是一張表,該表保存了主鍵與索引字段,并指向?qū)嶓w表的記錄,所以索引列也是要占用空間的。

七? 索引和查詢優(yōu)化

1. 不使用順序查找,因?yàn)轫樞虿檎冶容^慢,通過特定數(shù)據(jù)結(jié)構(gòu)的特點(diǎn)來提升查詢速度,這種數(shù)據(jù)結(jié)構(gòu)就是可以理解成索引。

2. 索引一般以文件形式存儲(chǔ)在磁盤上,索引檢索需要磁盤I/O操作,為了盡量減少磁盤I/O。磁盤往往不是嚴(yán)格按需讀取,而是每次都會(huì)預(yù)讀,而且主存和磁盤以頁(yè)為單位交換數(shù)據(jù),所以在讀取的數(shù)據(jù)不在主存中時(shí),會(huì)從磁盤中讀取一批數(shù)據(jù)(頁(yè))到主存中。

3. 不管在哪種程序優(yōu)化上,要想快速挺高性能,直接將常用的、少變更的數(shù)據(jù)直接讀取到內(nèi)存中,使用的時(shí)候就直接在內(nèi)存上讀取,而不去磁盤上讀取,減少I/O操作,這樣就能使程序快上10倍以上。但由于內(nèi)存容量的限制,也不可能將所有的數(shù)據(jù)都放內(nèi)存中。

MySQL索引分類

普通索引:最基本的索引,沒有任何限制。

唯一索引:與”普通索引”類似,不同的就是:索引列的值必須唯一,但允許有空值。

主鍵索引:它是一種特殊的唯一索引,不允許有空值。

全文索引:僅可用于 MyISAM 表,針對(duì)較大的數(shù)據(jù),生成全文索引很耗時(shí)好空間。

組合索引:為了更多的提高mysql效率可建立組合索引,遵循”最左前綴“原則。

覆蓋索引(Covering Indexes)就是直接走的索引,直接在內(nèi)存中就拿到值,不需要查詢數(shù)據(jù)庫(kù)。如分頁(yè)就要走覆蓋索引,因?yàn)樾阅鼙容^高。

聚簇索引(Clustered Indexes),主鍵就是聚集索引。聚簇索引保證關(guān)鍵字的值相近的元組存儲(chǔ)的物理位置也相同(所以字符串類型不宜建立聚簇索引,特別是隨機(jī)字符串,會(huì)使得系統(tǒng)進(jìn)行大量的移動(dòng)操作),且一個(gè)表只能有一個(gè)聚簇索引。因?yàn)橛纱鎯?chǔ)引擎實(shí)現(xiàn)索引,所以,并不是所有的引擎都支持聚簇索引。目前,只有solidDB和InnoDB支持。

非聚簇索引:二級(jí)索引葉子節(jié)點(diǎn)保存的不是指行的物理位置的指針,而是行的主鍵值。這意味著通過二級(jí)索引查找行。InnoDB對(duì)主鍵建立聚簇索引。如果你不指定主鍵,InnoDB會(huì)用一個(gè)具有唯一且非空值的索引來代替。如果不存在這樣的索引,InnoDB會(huì)定義一個(gè)隱藏的主鍵,然后對(duì)其建立聚簇索引。一般來說,DBMS都會(huì)以聚簇索引的形式來存儲(chǔ)實(shí)際的數(shù)據(jù),它是其它二級(jí)索引的基礎(chǔ)。

優(yōu)化要注意的一些事(重點(diǎn))

1. 索引其實(shí)就是一種歸類方式,當(dāng)某一個(gè)字段屬性都不能歸類,建立索引后是沒什么效果的,或歸類就二種(0和1),且各自都數(shù)據(jù)對(duì)半分,建立索引后的效果也不怎么強(qiáng)。

2. 主鍵的索引是不一樣的,要區(qū)別理解。

3. 當(dāng)時(shí)間存儲(chǔ)為時(shí)間戳保存的可以建立前綴索引。

4. 在什么是字段上建立索引,需要根據(jù)查詢條件而定,不要一上來就建立索引,浪費(fèi)內(nèi)存還有可能用不到。

5. 大字段(blob)不要建立索引,查詢也不會(huì)走索引。

6. 常用建立索引的地方:

主鍵的聚集索引

外鍵索引

類別只有0和1就不要建索引了,沒有意義,對(duì)性能沒有提升,還影響寫入性能

用模糊其實(shí)是可以走前綴索引

7. 唯一索引一定要小心使用,它帶有唯一約束,由于前期需求不明等情況下,可能造成我們對(duì)于唯一列的誤判。

8. 由于我們建立索引并想讓索引能達(dá)到最高性能,這個(gè)時(shí)候我們應(yīng)當(dāng)充分考慮該列是否適合建立索引,可以根據(jù)列的區(qū)分度來判斷,區(qū)分度太低的情況下可以不考慮建立索引,區(qū)分度越高效率越高。

SELECT COUNT(DISTINCT 列_xx)/COUNT(*) FROM 表

9. 寫入比較頻繁的時(shí)候,不能開啟MySQL的查詢緩存,因?yàn)樵诿恳淮螌懭氲臅r(shí)候不光要寫入磁盤還的更新緩存中的數(shù)據(jù)。

10. 建索引的目的:

1)加快查詢速度,使用索引后查詢有跡可循。

2)減少I/O操作,通過索引的路徑來檢索數(shù)據(jù),不是在磁盤中隨機(jī)檢索。

3)消除磁盤排序,索引是排序的,走完索引就排序完成。

11. 其實(shí)建索引的原理就是將磁盤I/O操作的最小化,不在磁盤中排序,而是在內(nèi)存中排好序,通過排序的規(guī)則去指定磁盤讀取就行,也不需要在磁盤上隨機(jī)讀取。

12. 由于磁盤整理磁盤碎片,所有有的時(shí)候我們也可以通過建立聚集索引來減少這一類的問題。

13. 當(dāng)一個(gè)表中有100萬數(shù)據(jù),而經(jīng)常用到的數(shù)據(jù)只有40萬或40萬以下,是不用考慮建立索引的,沒什么性能提升。

14. 什么時(shí)候不適合建立索引:

1)頻繁更新的字段不適合建立索引

2)where條件中用不到的字段不適合建立索引,都用不到建立索引沒有意義還浪費(fèi)空間

3)表數(shù)據(jù)可以確定比較少的不需要建索引

4)數(shù)據(jù)重復(fù)且發(fā)布比較均勻的的字段不適合建索引(唯一性太差的字段不適合建立索引),例如性別,真假值

5)參與列計(jì)算的列不適合建索引,如:

select * from table where amount+100>1000,-- 這樣是不走索引的,可以改造為:select * from table where amount>1000-100。

15. 使用count統(tǒng)計(jì)數(shù)據(jù)量的時(shí)候建議使用count(*)而不是count(列),因?yàn)閏ount(*)MySQL是做了優(yōu)化的。

16. 二次SQL查詢區(qū)別不大的時(shí)候,不能按照二次執(zhí)行的時(shí)間來判斷優(yōu)化結(jié)果,沒準(zhǔn)第一次查詢后又保存緩存數(shù)據(jù),導(dǎo)致第二次查詢速度比第二次快,很多時(shí)候我們看到的都是假象。

17. 什么時(shí)候開MySQL的查詢緩存,交易系統(tǒng)(寫多、讀少)、SQL優(yōu)化測(cè)試,建議關(guān)閉查詢緩存,論壇文章類系統(tǒng)(寫少、讀多),建議開啟查詢緩存。

18. Explain 執(zhí)行計(jì)劃只能解釋SELECT操作。

19. 查詢優(yōu)化可以考慮讓查詢走索引,走索引能提升查詢速度,索引覆蓋是最快的,如下就是讓分頁(yè)走覆蓋索引提高查詢速度。

Select * from fentrust e

Inner join (select fid from fentrust limit 4100000, 10) a on a.fid = e.fid

20. 子查詢比join快,雖然規(guī)律不絕對(duì),但對(duì)大表多數(shù)有效

21. 復(fù)雜SQL語(yǔ)句優(yōu)化的思路:

1)首先考慮在一個(gè)表中能不能取到有關(guān)的信息,盡量少關(guān)聯(lián)表

2)關(guān)聯(lián)條件爭(zhēng)取都走主鍵或外鍵查詢條件,能走到對(duì)應(yīng)的索引

3)爭(zhēng)取在滿足業(yè)務(wù)上走小集合數(shù)據(jù)查找

4)INNER JOIN 和子查詢哪個(gè)更快,場(chǎng)景不一致速度也不同

22. where條件多條件一定要按照小結(jié)果集排大結(jié)果集前面

23. 盡量避免大事務(wù)操作,提高系統(tǒng)并發(fā)能力,有時(shí)無法避免,改用定時(shí)器延遲處理。

24. 什么情況不走索引:

SELECT ` famount ` FROM ` fentrust ` WHERE ` famount `+10=30;-- 不會(huì)使用索引,因?yàn)樗兴饕袇⑴c了計(jì)算?

SELECT `famount` FROM `fentrust` WHERE LEFT(`fcreateTime`,4) <1990; -- 不會(huì)使用索引,因?yàn)槭褂昧撕瘮?shù)運(yùn)算,原理與上面相同?

SELECT * FROM ` fuser` WHERE `floginname` LIKE‘138%' -- 走索引?

SELECT * FROM ` fuser ` WHERE ` floginname ` LIKE "%7488%" -- 不走索引 -- 正則表達(dá)式不使用索引,這應(yīng)該很好理解,所以為什么在SQL中很難看到regexp關(guān)鍵字的原因 -- 字符串與數(shù)字比較不使用索引;?

EXPLAIN SELECT * FROM `a` WHERE `a`=1 -- 不走索引?

select * from fuser where floginname='xxx' or femail='xx' or fstatus=1 --如果條件中有or,即使其中有條件帶索引也不會(huì)使用。換言之,就是要求使用的所有字段,都必須建立索引, 我們建議大家盡量避免使用or 關(guān)鍵字

25. 如果MySQL估計(jì)使用全表掃描要比使用索引快,則不使用索引。

26. 使用UNION ALL 替換OR多條件查詢并集。

27. 在大數(shù)據(jù)表刪除也是一個(gè)問題,避免刪除過程數(shù)據(jù)庫(kù)奔潰,可以考慮分配刪除,一次刪1000條,刪完后等一會(huì)繼續(xù)刪除

delete from logs where log_date <= ’2012-11-01’ limit 1000

28. 大數(shù)據(jù)表優(yōu)化:

1)建立匯總表

2)建立流水表

3)分庫(kù)分表

29. 建立匯總表,首先不用考慮分庫(kù)分表,使用定時(shí)器定時(shí)去匯總。

30. 分表,可以按水平或垂直切分。垂直分表其實(shí)就是將經(jīng)常使用的數(shù)據(jù)和很少使用的數(shù)據(jù)進(jìn)行垂直的切分,切分到不同的庫(kù),提高單庫(kù)的數(shù)據(jù)容量,如:前3個(gè)月之前的交易記錄就可以放另一個(gè)庫(kù)中。

31. 建立流水表,數(shù)據(jù)冗余,有這個(gè)表記錄流水變更就不用去寫復(fù)雜SQL計(jì)算流水。

32. 分庫(kù),多數(shù)據(jù)庫(kù)相同庫(kù)結(jié)構(gòu),分發(fā)處理并發(fā)能力,但同時(shí)帶來了數(shù)據(jù)同步問題,也可以使用分庫(kù)做主備分離

32. SQL優(yōu)化順序:

1)盡量少作計(jì)算。

2)盡量少 join。

3)盡量少排序。

4)盡量避免 select *。

5)盡量用 join 代替子查詢。

6)盡量少 or。

7)盡量用 union all 代替 union。

8)盡量早過濾。

9)避免類型轉(zhuǎn)換。

10)優(yōu)先優(yōu)化高并發(fā)的 SQL,而不是執(zhí)行頻率低某些“大”SQL。

11)從全局出發(fā)優(yōu)化,而不是片面調(diào)整。

12)盡可能對(duì)每一條運(yùn)行在數(shù)據(jù)庫(kù)中的SQL進(jìn)行 Explain。

33. 如下是30條大數(shù)據(jù)表優(yōu)化要點(diǎn):

1)對(duì)查詢進(jìn)行優(yōu)化,應(yīng)盡量避免全表掃描,首先應(yīng)考慮在 where 及 order by 涉及的列上建立索引。

2)應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行 null 值判斷,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描,如:select id from t where num is null可以在num上設(shè)置默認(rèn)值0,確保表中num列沒有null值,然后這樣查詢:select id from t where num=0

3)應(yīng)盡量避免在 where 子句中使用!=或<>操作符,否則引擎將放棄使用索引而進(jìn)行全表掃描。

4)應(yīng)盡量避免在 where 子句中使用or 來連接條件,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描,如: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 也要慎用,否則會(huì)導(dǎo)致全表掃描,如:select id from t where num in(1,2,3) 對(duì)于連續(xù)的數(shù)值,能用 between 就不要用 in 了:select id from t where num between 1 and 3

6)下面的查詢也將導(dǎo)致全表掃描:select id from t where name like '李%'若要提高效率,可以考慮全文檢索。

7)如果在 where 子句中使用參數(shù),也會(huì)導(dǎo)致全表掃描。因?yàn)镾QL只有在運(yùn)行時(shí)才會(huì)解析局部變量,但優(yōu)化程序不能將訪問計(jì)劃的選擇推遲到運(yùn)行時(shí);它必須在編譯時(shí)進(jìn)行選擇。然 而,如果在編譯時(shí)建立訪問計(jì)劃,變量的值還是未知的,因而無法作為索引選擇的輸入項(xiàng)。如下面語(yǔ)句將進(jìn)行全表掃描:select id from t where num=@num可以改為強(qiáng)制查詢使用索引:select id from t with(index(索引名)) where num=@num

8)應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行表達(dá)式操作,這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:select id from t where num/2=100應(yīng)改為:select id from t where num=100*2

9)應(yīng)盡量避免在where子句中對(duì)字段進(jìn)行函數(shù)操作,這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:select id from t where substring(name,1,3)='abc' ,name以abc開頭的id 應(yīng)改為: select id from t where name like 'abc%'

10)不要在 where 子句中的“=”左邊進(jìn)行函數(shù)、算術(shù)運(yùn)算或其他表達(dá)式運(yùn)算,否則系統(tǒng)將可能無法正確使用索引。

11)在使用索引字段作為條件時(shí),如果該索引是復(fù)合索引,那么必須使用到該索引中的第一個(gè)字段作為條件時(shí)才能保證系統(tǒng)使用該索引,否則該索引將不會(huì)被使用,并且應(yīng)盡可能的讓字段順序與索引順序相一致。

12)不要寫一些沒有意義的查詢,如需要生成一個(gè)空表結(jié)構(gòu):select col1,col2 into #t from t where 1=0 這類代碼不會(huì)返回任何結(jié)果集,但是會(huì)消耗系統(tǒng)資源的,應(yīng)改成這樣: create table #t(...)

13)很多時(shí)候用 exists 代替 in 是一個(gè)好的選擇:select num from a where num in(select num from b) 用下面的語(yǔ)句替換: select num from a where exists(select 1 from b where num=a.num)

14)并不是所有索引對(duì)查詢都有效,SQL是根據(jù)表中數(shù)據(jù)來進(jìn)行查詢優(yōu)化的,當(dāng)索引列有大量數(shù)據(jù)重復(fù)時(shí),SQL查詢可能不會(huì)去利用索引,如一表中有字段sex,male、female幾乎各一半,那么即使在sex上建了索引也對(duì)查詢效率起不了作用。

15)索引并不是越多越好,索引固然可 以提高相應(yīng)的 select 的效率,但同時(shí)也降低了 insert 及 update 的效率,因?yàn)?insert 或 update 時(shí)有可能會(huì)重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個(gè)表的索引數(shù)最好不要超過6個(gè),若太多則應(yīng)考慮一些不常使用到的列上建的索引是否有 必要。

16)應(yīng)盡可能的避免更新 clustered 索引數(shù)據(jù)列,因?yàn)?clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲(chǔ)順序,一旦該列值改變將導(dǎo)致整個(gè)表記錄的順序的調(diào)整,會(huì)耗費(fèi)相當(dāng)大的資源。若應(yīng)用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列,那么需要考慮是否應(yīng)將該索引建為 clustered 索引。

17)盡量使用數(shù)字型字段,若只含數(shù)值信息的字段盡量不要設(shè)計(jì)為字符型,這會(huì)降低查詢和連接的性能,并會(huì)增加存儲(chǔ)開銷。這是因?yàn)橐嬖谔幚聿樵兒瓦B接時(shí)會(huì)逐個(gè)比較字符串中每一個(gè)字符,而對(duì)于數(shù)字型而言只需要比較一次就夠了。

18)盡可能的使用 varchar/nvarchar 代替 char/nchar ,因?yàn)槭紫茸冮L(zhǎng)字段存儲(chǔ)空間小,可以節(jié)省存儲(chǔ)空間,其次對(duì)于查詢來說,在一個(gè)相對(duì)較小的字段內(nèi)搜索效率顯然要高些。

19)任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。

20)盡量使用表變量來代替臨時(shí)表。如果表變量包含大量數(shù)據(jù),請(qǐng)注意索引非常有限(只有主鍵索引)。

21)避免頻繁創(chuàng)建和刪除臨時(shí)表,以減少系統(tǒng)表資源的消耗。

22)臨時(shí)表并不是不可使用,適當(dāng)?shù)厥褂盟鼈兛梢允鼓承├谈行?,例如,?dāng)需要重復(fù)引用大型表或常用表中的某個(gè)數(shù)據(jù)集時(shí)。但是,對(duì)于一次性事件,最好使用導(dǎo)出表。

23)在新建臨時(shí)表時(shí),如果一次性插入數(shù)據(jù)量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數(shù)據(jù)量不大,為了緩和系統(tǒng)表的資源,應(yīng)先create table,然后insert。

24)如果使用到了臨時(shí)表,在存儲(chǔ)過程的最后務(wù)必將所有的臨時(shí)表顯式刪除,先 truncate table ,然后 drop table ,這樣可以避免系統(tǒng)表的較長(zhǎng)時(shí)間鎖定。

25)盡量避免使用游標(biāo),因?yàn)橛螛?biāo)的效率較差,如果游標(biāo)操作的數(shù)據(jù)超過1萬行,那么就應(yīng)該考慮改寫。

26)使用基于游標(biāo)的方法或臨時(shí)表方法之前,應(yīng)先尋找基于集的解決方案來解決問題,基于集的方法通常更有效。

27)與臨時(shí)表一樣,游標(biāo)并不是不可使 用。對(duì)小型數(shù)據(jù)集使用 FAST_FORWARD 游標(biāo)通常要優(yōu)于其他逐行處理方法,尤其是在必須引用幾個(gè)表才能獲得所需的數(shù)據(jù)時(shí)。在結(jié)果集中包括“合計(jì)”的例程通常要比使用游標(biāo)執(zhí)行的速度快。如果開發(fā)時(shí) 間允許,基于游標(biāo)的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。

28)在所有的存儲(chǔ)過程和觸發(fā)器的開始處設(shè)置 SET NOCOUNT ON ,在結(jié)束時(shí)設(shè)置 SET NOCOUNT OFF 。無需在執(zhí)行存儲(chǔ)過程和觸發(fā)器的每個(gè)語(yǔ)句后向客戶端發(fā)送DONE_IN_PROC 消息。

29)盡量避免大事務(wù)操作,提高系統(tǒng)并發(fā)能力。

30)盡量避免向客戶端返回大數(shù)據(jù)量,若數(shù)據(jù)量過大,應(yīng)該考慮相應(yīng)需求是否合理。

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

版權(quán)聲明:本文為CSDN博主「zb313982521」的原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/zb313982521/article/details/105415629

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容