1. 程式人生 > >數據庫第一天-數據庫索引

數據庫第一天-數據庫索引

ddr 建表 數據庫 這樣的 原來 where子句 矛盾 例如 csdn

一、使用索引的好處

創建索引可以大大提高系統的性能。第一,通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。第二,可以大大加快數據的檢索速度,這也是創建索引的最主要的原因。第三,可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。

二、索引的原理

數據在磁盤上是以塊的形式存儲的(涉及到了數據結構和操作系統)。為確保對磁盤操作的原子性,訪問數據的時候會一並訪問所有數據塊。

鑒於很多記錄只能做到按一個字段排序,所以要查詢某個未經排序的字段,就需要使用線性查找,即要訪問N個數據塊

然而,對於經過排序的字段,可以使用二分查找,因此只要訪問log2 N個數據塊。

首先,來看一個示例數據庫表的模式:

字段名              數據類型         在磁盤上的大小
id (Primary key)   Unsigned INT     4 字節
firstName          Char(50)         50 字節
lastName           Char(50)         50 字節
emailAddress       Char(100)        100 字節

註意:這裏用char而不用varchar是為了精確地描述數據占用磁盤的大小。這個示例數據庫中包含500萬行記錄,而且沒有建立索引。接下來我們就分析針對這個表的兩個查詢:一個查詢使用id

(經過排序的鍵字段),另一個查詢使用firstName(未經排序的非鍵字段)。

示例分析一

對於這個擁有r = 5 000 000條記錄的示例數據庫,在磁盤上要為每條記錄分配 R = 204字節的固定存儲空間。這個表保存在MyISAM數據庫中,而這個數據庫默認的數據庫塊大小為 B = 1024字節。於是,我們可計算出這個表的分塊因數為 bfr = (B/R) = 1024/204 = 5,即磁盤上每個數據塊保存5條記錄。那麽,保存整個表所需的數據塊數就是 N = (r/bfr) = 5000000/5 = 1 000 000。

使用線性查找搜索id字段——這個字段是鍵字段(每個字段的值唯一),需要訪問 N/2 = 500 000個數據塊才能找到目標值。不過,因為這個字段是經過排序的,所以可以使用二分查找法,而這樣平均只需要訪問log2 1000000 = 19.93 = 20 個塊。顯然,這會給性能帶來極大的提升。

再來看看firstName字段,這個字段是未經排序的,因此不可能使用二分查找,況且這個字段的值也不是唯一的,所以要從表的開頭查找末尾,即要訪問 N = 1 000 000個數據塊。這種情況通過建立索引就能得到改善。

如果一條索引記錄只包含索引字段和一個指向原始記錄的指針,那麽這條記錄肯定要比它所指向的包含更多字段的記錄更小。也就是說,索引本身占用的磁盤空間比原來的表更少,因此需要遍歷的數據塊數也比搜索原來的表更少。以下是firstName字段索引的模式:

字段名         數據類型        在磁盤上的大小
firstName     Char(50)        50 字節
(記錄指針)    Special         4 字節

註意:在MySQL中,根據表的大小,指針的大小可能是2、3、4或5字節。

示例分析二

對於這個擁有r = 5 000 000條記錄的示例數據庫,每條索引記錄要占用 R = 54字節磁盤空間,而且同樣使用默認的數據塊大小 B = 1024字節。那麽索引的分塊因數就是 bfr = (B/R) = 1024/54 = 18。最終這個表的索引需要占用 N = (r/bfr) = 5000000/18 = 277 778個數據塊。

現在,再搜索firstName字段就可以使用索引來提高性能了。對索引使用二分查找,需要訪問 log2 277778 = 18.09 = 19個數據塊。再加上為找到實際記錄的地址還要訪問一個數據塊,總共要訪問 19 + 1 = 20個數據塊,這與搜索未索引的表需要訪問277 778個數據塊相比,不啻於天壤之別。

三、什麽時候建索引

索引是建立在數據庫表中的某些列的上面。因此,在創建索引的時候,應該仔細考慮在哪些列上可以創建索引,在哪些列上不能創建索引。一般來說,應該在這些列上創建索引,例如:在經常需要搜索的列上,可以加快搜索的速度;在作為主鍵的列上,強制該列的唯一性和組織表中數據的排列結構;在經常用在連接的列上,這些列主要是一些外鍵,可以加快連接的速度;在經常需要根據範圍進行搜索的列上創建索引,因為索引已經排序,其指定的範圍是連續的;在經常需要排序的列上創建索引,因為索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;在經常使用在WHERE子句中的列上面創建索引,加快條件的判斷速度。
同樣,對於有些列不應該創建索引。一般來說,不應該創建索引的的這些列具有下列特點:第一,對於那些在查詢中很少使用或者參考的列不應該創建索引。這是因為,既然這些列很少使用到,因此有索引或者無索引,並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。第二,對於那些只有很少數據值的列也不應該增加索引。這是因為,由於這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的數據行占了表中數據行的很大比例,即需要在表中搜索的數據行的比例很大。增加索引,並不能明顯加快檢索速度。第三,對於那些定義為text, image和bit數據類型的列不應該增加索引。這是因為,這些列的數據量要麽相當大,要麽取值很少。第四,當修改性能遠遠大於檢索性能時,不應該創建索引。這是因為,修改性能和檢索性能是互相矛盾的。當增加索引時,會提高檢索性能,但是會降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。因此,當修改性能遠遠大於檢索性能時,不應該創建索引。

四、索引的常用種類及創建方法

這是最基本的索引,它沒有任何限制。它有以下幾種創建方式:

◆創建索引

CREATE INDEX indexName ON mytable(username(length)); 如果是CHAR,VARCHAR類型,length可以小於字段實際長度;如果是BLOB和TEXT類型,必須指定 length,下同。

◆修改表結構

ALTER mytable ADD INDEX [indexName] ON (username(length)) ◆創建表的時候直接指定

CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, INDEX [indexName] (username(length)) ); 刪除索引的語法:

DROP INDEX [indexName] ON mytable;

(2)唯一索引

它與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。它有以下幾種創建方式:

◆創建索引

CREATE UNIQUE INDEX indexName ON mytable(username(length)) ◆修改表結構

ALTER mytable ADD UNIQUE [indexName] ON (username(length)) ◆創建表的時候直接指定

CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, UNIQUE [indexName] (username(length)) );

(3)主鍵索引

它是一種特殊的唯一索引,不允許有空值。一般是在建表的時候同時創建主鍵索引:

CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, PRIMARY KEY(ID) ); 當然也可以用 ALTER 命令。記住:一個表只能有一個主鍵。

(4)組合索引

為了形象地對比單列索引和組合索引,為表添加多個字段:

CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, city VARCHAR(50) NOT NULL, age INT NOT NULL ); 為了進一步榨取mysql的效率,就要考慮建立組合索引。就是將 name, city, age建到一個索引裏:

ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age); 建表時,usernname長度為 16,這裏用 10。這是因為一般情況下名字的長度不會超過10,這樣會加速索引查詢速度,還會減少索引文件的大小,提高INSERT的更新速度。

如果分別在 usernname,city,age上建立單列索引,讓該表有3個單列索引,查詢時和上述的組合索引效率也會大不一樣,遠遠低於我們的組合索引。雖然此時有了三個索引,但MySQL只能用到其中的那個它認為似乎是最有效率的單列索引。

建立這樣的組合索引,其實是相當於分別建立了下面三組組合索引:

usernname,city,age usernname,city usernname 為什麽沒有 city,age這樣的組合索引呢?這是因為MySQL組合索引“最左前綴”的結果。簡單的理解就是只從最左面的開始組合。並不是只要包含這三列的查詢都會用到該組合索引,下面的幾個SQL就會用到這個組合索引:

SELECT * FROM mytable WHREE username="admin" AND city="鄭州" SELECT * FROM mytable WHREE username="admin" 而下面幾個則不會用到:

SELECT * FROM mytable WHREE age=20 AND city="鄭州" SELECT * FROM mytable WHREE city="鄭州"

數據庫第一天-數據庫索引