1. 程式人生 > >資料庫的優化之索引!

資料庫的優化之索引!

網路資料散亂,查閱不便,所以做如下整理,內容出處均以標明,侵刪!

一、資料庫的分類

關係型資料庫

  • 關係型資料庫通過外來鍵關聯來建立表與表之間的關係,另外,表和欄位資料和資料存在著關係

非關係型資料庫

  • 非關係資料庫通常指資料以物件的形式儲存在資料庫中,而物件之間的關係通過每個物件自身的屬性來決定
資料庫型別 舉例 優點 缺點 適用範圍
關係型資料庫 Mysql、Oracle 1、資料之間有關係,進行資料的增刪改查的時候是非常方便的
2、關係型資料庫是有事務操作的,保證資料的完整性和一致性,即ACID
3、易於維護:豐富的完整性(實體完整性、參照完整性和使用者定義的完整性)大大減低了資料冗餘和資料不一致的概率
4、支援SQL,可用於複雜的查詢。
1、因為資料和資料是有關係的,底層是運行了大量的演算法大量演算法會降低系統的效率,會降低效能
2、面對海量資料的增刪改查的時候會顯的無能為力
3、海量資料對資料進行維護變得非常的無力
適合處理一般量級的資料,例如:銀行轉賬等
非關係型資料庫 redis、MangDB 1、海量資料的增刪改查,維護和處理輕鬆
2、無需經過sql層的解析,讀寫效能很高
3、基於鍵值對,資料沒有耦合性,容易擴充套件
4、儲存資料的格式:nosql的儲存格式是key,value形式、文件形式、圖片形式等等,文件形式、圖片形式等等,而關係型資料庫則只支援基礎型別
1、不提供sql支援,學習和使用成本較高;
2、無事務處理,附加功能bi和報表等支援也不好;
適合處理海量資料,保證效率,不一定安全,例如:微博資料等

二、資料庫的引擎分類

資料庫儲存引擎是資料庫底層軟體組織,資料庫管理系統(DBMS)使用資料引擎進行建立、查詢、更新和刪除資料。不同的儲存引擎提供不同的儲存機制、索引技巧、鎖定水平等功能,使用不同的儲存引擎,還可以 獲得特定的功能。現在許多不同的資料庫管理系統都支援多種不同的資料引擎。

MyISAM儲存引擎

  • MyISAM基於ISAM儲存引擎,並對其進行擴充套件。它是在Web、資料倉儲和其他應用環境下最常使用的儲存引擎之一。MyISAM擁有較高的插入、查詢速度,但不支援事物。
  • MyISAM主要特性有:

    • 1、大檔案(達到63位檔案長度)在支援大檔案的檔案系統和作業系統上被支援

    • 2、當把刪除和更新及插入操作混合使用的時候,動態尺寸的行產生更少碎片。這要通過合併相鄰被刪除的塊,以及若下一個塊被刪除,就擴充套件到下一塊自動完成

    • 3、每個MyISAM表最大索引數是64,這可以通過重新編譯來改變。每個索引最大的列數是16

    • 4、最大的鍵長度是1000位元組,這也可以通過編譯來改變,對於鍵長度超過250位元組的情況,一個超過1024位元組的鍵將被用上

    • 5、BLOB和TEXT列可以被索引

    • 6、NULL被允許在索引的列中,這個值佔每個鍵的0~1個位元組

    • 7、所有數字鍵值以高位元組優先被儲存以允許一個更高的索引壓

    • 8、每個MyISAM型別的表都有一個AUTO_INCREMENT的內部列,當INSERT和UPDATE操作的時候該列被更新,同時AUTO_INCREMENT列將被重新整理。所以說,MyISAM型別表的AUTO_INCREMENT列更新比InnoDB型別的AUTO_INCREMENT更快

    • 9、可以把資料檔案和索引檔案放在不同目錄

    • 10、每個字元列可以有不同的字符集

    • 11、有VARCHAR的表可以固定或動態記錄長度

    • 12、VARCHAR和CHAR列可以多達64KB

      使用MyISAM引擎建立資料庫,將產生3個檔案。檔案的名字以表名字開始,副檔名之處檔案型別:frm檔案儲存表定義、資料檔案的副檔名為.MYD(MYData)、索引檔案的副檔名時.MYI(MYIndex)

InnoDB儲存引擎

  • InnoDB是事務型資料庫的首選引擎,支援事務安全表(ACID),支援行鎖定和外來鍵,InnoDB是預設的MySQL引擎。
  • InnoDB主要特點:

    • 1、InnoDB給MySQL提供了具有提交、回滾和崩潰恢復能力的事物安全(ACID相容)儲存引擎。InnoDB鎖定在行級並且也在SELECT語句中提供一個類似Oracle的非鎖定讀。這些功能增加了多使用者部署和效能。在SQL查詢中,可以自由地將InnoDB型別的表和其他MySQL的表型別混合起來,甚至在同一個查詢中也可以混合

    • 2、InnoDB是為處理巨大資料量的最大效能設計。它的CPU效率可能是任何其他基於磁碟的關係型資料庫引擎鎖不能匹敵的

    • 3、InnoDB儲存引擎完全與MySQL伺服器整合,InnoDB儲存引擎為在主記憶體中快取資料和索引而維持它自己的緩衝池。InnoDB將它的表和索引在一個邏輯表空間中,表空間可以包含數個檔案(或原始磁碟檔案)。這與MyISAM表不同,比如在MyISAM表中每個表被存放在分離的檔案中。InnoDB表可以是任何尺寸,即使在檔案尺寸被限制為2GB的作業系統上

    • 4、InnoDB支援外來鍵完整性約束,儲存表中的資料時,每張表的儲存都按主鍵順序存放,如果沒有顯示在表定義時指定主鍵,InnoDB會為每一行生成一個6位元組的ROWID,並以此作為主鍵

    • 5、InnoDB被用在眾多需要高效能的大型資料庫站點上

      InnoDB不建立目錄,使用InnoDB時,MySQL將在MySQL資料目錄下建立一個名為ibdata1的10MB大小的自動擴充套件資料檔案,以及兩個名為ib_logfile0和ib_logfile1的5MB大小的日誌檔案

MEMORY儲存引擎

  • MEMORY儲存引擎將表中的資料儲存到記憶體中,未查詢和引用其他表資料提供快速訪問。
  • MEMORY主要特性有:

    • 1、MEMORY表的每個表可以有多達32個索引,每個索引16列,以及500位元組的最大鍵長度

    • 2、MEMORY儲存引擎執行HASH和BTREE縮影

    • 3、可以在一個MEMORY表中有非唯一鍵值

    • 4、MEMORY表使用一個固定的記錄長度格式

    • 5、MEMORY不支援BLOB或TEXT列

    • 6、MEMORY支援AUTO_INCREMENT列和對可包含NULL值的列的索引

    • 7、MEMORY表在所由客戶端之間共享(就像其他任何非TEMPORARY表)

    • 8、MEMORY表記憶體被儲存在記憶體中,記憶體是MEMORY表和伺服器在查詢處理時的空閒中,建立的內部表共享

    • 9、當不再需要MEMORY表的內容時,要釋放被MEMORY表使用的記憶體,應該執行DELETE FROM或TRUNCATE TABLE,或者刪除整個表(使用DROP TABLE)

MERGE儲存引擎

  • Merge儲存引擎是一組MyISAM表的組合,這些MyISAM表必須結構完全相同,merge表本身並沒有資料,對merge型別的表可以進行查詢,更新,刪除操作,這些操作實際上是對內部的MyISAM表進行的。

三、索引優化資料庫

mysql邏輯架構圖

整理如下:

SQL優化

  • SQL優化,主要是優化索引

  • 索引:相當於書的目錄

  • 索引:index是幫助MySQL高效獲取資料的資料結構。索引是資料結構(樹:B樹、Hash樹…)

  • 索引的弊端:
    • 1.索引本身很大,可以存放在記憶體/硬碟(通常為硬碟)
    • 2.索引不是所有情況均適用:a.少量資料 b.頻繁更新的欄位 c.很少適用的欄位
    • 3.索引會降低增刪改的效率
  • 索引的優勢:
    • 提高查詢效率(降低IO使用率)
    • 降低CPU使用率(… order by age desc ,因為B樹索引本身就是排好序的結構,在排序時即可直接使用)
  • 為什麼要優化sql

    • 原因:效能低、執行時間長太長、等待時間太長、SQL語句欠佳(連線查詢)、索引失效、伺服器引數設定不合理(緩衝區、執行緒數)
    • sql編寫過程:

      select dinstinct .. from ..join .. on .. where .. group by .. having .. order by .. limit ..

    • sql解析過程:

      from .. on .. join ..where .. group by .. having .. select dinstinct .. order by limit ..

  • 索引分類
    • 單值索引:單列,age;一個表可以有多個單隻索引,name
    • 主鍵索引:不能重複。Id 不能是null (如果不加索引,預設是主鍵索引)
    • 唯一索引:不能重複。Id 可以是null
    • 複合索引:多個列構成的所有(相當於二級索引: z: zhao)(name,age)(a,b,c,d,……)
  • 建立索引:

    建立的方式一:

    create 索引型別 索引名 on 表(欄位)

    單值:

    create  index  dept_index  on  tb(dept);
    

    唯一:

    create  unique index  name_index  on  tb(name);
    

    複合索引:

    create  index  dept_name_index  on  tb(dept,name);
    

    建立的方式二:

    alter table 表名 索引型別 索引名(欄位)

    單值:

    alter  table  tb  add  index  dept_index(dept);
    

    唯一:

    alter  table  tb  add  unique  index  name_index(name);
    

    複合索引:

    alter  table  tb  add  index  dept_name_index(dept,name);
    

    注意:如果一個欄位是primary key ,則改欄位預設就是 主鍵索引

  • 刪除索引

    • drop index 索引名 on 表名 ;
    • drop index name_index on tb ;
  • 查詢索引:

    • Show index from 表名;

建立索引以後,在查詢時,就會有速度上的提升,由於機器原因,測試這項內容需要很大的資料量(上百萬條)。網上有很多測試的例項,也可以直接去訪問這裡的第三點,比較具體的測試了資料優化的展示,測試資料量為300W。

優化的注意具體事項

若想利用索引達到預想的提高查詢速度的效果,我們在新增索引時,必須遵循以下原則

1、最左字首匹配原則,非常重要的原則,

create index ix_name_email on s1(name,email,)
- 最左字首匹配:必須按照從左到右的順序匹配
select * from s1 where name='egon'; #可以
select * from s1 where name='egon' and email='asdf'; #可以
select * from s1 where email='[email protected]'; #不可以
mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配,
比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,
d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。

2、=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式

3.儘量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),

表示欄位不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、
性別欄位可能在大資料面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不同,
這個值也很難確定,一般需要join的欄位我們都要求是0.1以上,即平均1條掃描10條記錄

4、索引列不能參與計算,保持列“乾淨”

比如from_unixtime(create_time) = ’2014-05-29’

就不能使用到索引,原因很簡單,b+樹中存的都是資料表中的欄位值,

但進行檢索時,需要把所有元素都應用函式才能比較,顯然成本太大。

所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);

最左字首示範

mysql> select * from s1 where id>3 and name='egon' and email='[email protected]' and gender='male';
Empty set (0.39 sec)#sec是單位秒

mysql> create index idx on s1(id,name,email,gender); #未遵循最左字首
Query OK, 0 rows affected (15.27 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> select * from s1 where id>3 and name='egon' and email='[email protected]' and gender='male';
Empty set (0.43 sec)


mysql> drop index idx on s1;
Query OK, 0 rows affected (0.16 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index idx on s1(name,email,gender,id); #遵循最左字首
Query OK, 0 rows affected (15.97 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> select * from s1 where id>3 and name='egon' and email='[email protected]' and gender='male';
Empty set (0.03 sec)
最左字首匹配
 2 index(id,age,email,name)
 3 #條件中一定要出現id(只要出現id就會提升速度)
 4 id
 5 id age
 6 id email
 7 id name
 8 
 9 email #不行  如果單獨這個開頭就不能提升速度了
10 mysql> select count(*) from s1 where id=3000;
11 +----------+
12 | count(*) |
13 +----------+
14 |        1 |
15 +----------+
16 1 row in set (0.11 sec)
17 
18 mysql> create index xxx on s1(id,name,age,email);
19 Query OK, 0 rows affected (6.44 sec)
20 Records: 0  Duplicates: 0  Warnings: 0
21 
22 mysql>  select count(*) from s1 where id=3000;
23 +----------+
24 | count(*) |
25 +----------+
26 |        1 |
27 +----------+
28 1 row in set (0.00 sec)
29 
30 mysql>  select count(*) from s1 where name='egon';
31 +----------+
32 | count(*) |
33 +----------+
34 |   299999 |
35 +----------+
36 1 row in set (0.16 sec)
37 
38 mysql>  select count(*) from s1 where email='[email protected]';
39 +----------+
40 | count(*) |
41 +----------+
42 |        1 |
43 +----------+
44 1 row in set (0.15 sec)
45 
46 mysql>  select count(*) from s1 where id=1000 and email='[email protected]';
47 +----------+
48 | count(*) |
49 +----------+
50 |        0 |
51 +----------+
52 1 row in set (0.00 sec)
53 
54 mysql>  select count(*) from s1 where email='[email protected]' and id=3000;
55 +----------+
56 | count(*) |
57 +----------+
58 |        0 |
59 +----------+
60 1 row in set (0.00 sec)

索引無法命中的情況需要注意:

- like '%xx'
    select * from tb1 where email like '%cn';


- 使用函式
    select * from tb1 where reverse(email) = 'wupeiqi';


- or
    select * from tb1 where nid = 1 or name = '[email protected]';


    特別的:當or條件中有未建立索引的列才失效,以下會走索引
            select * from tb1 where nid = 1 or name = 'seven';
            select * from tb1 where nid = 1 or name = '[email protected]' and email = 'alex'


- 型別不一致
    如果列是字串型別,傳入條件是必須用引號引起來,不然...
    select * from tb1 where email = 999;

普通索引的不等於不會走索引
- !=
    select * from tb1 where email != 'alex'

    特別的:如果是主鍵,則還是會走索引
        select * from tb1 where nid != 123
- >
    select * from tb1 where email > 'alex'


    特別的:如果是主鍵或索引是整數型別,則還是會走索引
        select * from tb1 where nid > 123
        select * from tb1 where num > 123

#排序條件為索引,則select欄位必須也是索引欄位,否則無法命中
- order by
    select name from s1 order by email desc;
    當根據索引排序時候,select查詢的欄位如果不是索引,則不走索引
    select email from s1 order by email desc;
    特別的:如果對主鍵排序,則還是走索引:
        select * from tb1 order by nid desc;

- 組合索引最左字首
    如果組合索引為:(name,email)
    name and email       -- 使用索引
    name                 -- 使用索引
    email                -- 不使用索引


- count(1)或count(列)代替count(*)在mysql中沒有差別了

- create index xxxx  on tb(title(19)) #text型別,必須制定長度
- 避免使用select *
- count(1)或count(列) 代替 count(*)
- 建立表時儘量時 char 代替 varchar
- 表的欄位順序固定長度的欄位優先
- 組合索引代替多個單列索引(經常使用多個條件查詢時)
- 儘量使用短索引
- 使用連線(JOIN)來代替子查詢(Sub-Queries)
- 連表時注意條件型別需一致
- 索引雜湊值(重複少)不適合建索引,例:性別不適合