參考了多篇文章,分別記錄,如下。
下面是第一篇的總結 http://www.jb51.net/article/76007.htm:
在MySQL中,InnoDB引擎表是(聚集)索引組織表(clustered index organize table),而MyISAM引擎表則是堆組織表(heap organize table)。
聚集索引是一種索引組織形式,索引的鍵值邏輯順序決定了表數據行的物理存儲順序;
而非聚集索引則就是普通索引了,僅僅只是對數據列創建相應的索引,不影響整個表的物理存儲順序。
IOT表里數據物理存儲順序和主鍵索引的順序一致,所以如果新增數據是離散的,會導致數據塊趨於離散,而不是趨於順序。
而HOT表數據寫入的順序是按寫入時間順序存儲的。
IOT表相比HOT表的優勢是:
范圍查詢效率更高;
數據頻繁更新(聚集索引本身不更新)時,更不容易產生碎片;
特別適合有一小部分熱點數據頻繁讀寫的場景;
通過主鍵訪問數據時快速可達;
IOT表的不足則有:
數據變化如果是離散為主的話,那么效率會比HOT表差;
HOT表的不足有:
索引回表讀開銷很大;
大部分數據讀取時隨機的,無法保證被順序讀取,開銷大;
每張InnoDB表只能創建一個聚集索引,聚集索引可以由一列或多列組成。
InnoDB是聚集索引組織表,它的聚集索引選擇規則是這樣的:
首先選擇顯式定義的主鍵索引做為聚集索引;
如果沒有,則選擇第一個不允許NULL的唯一索引;
還是沒有的話,就采用InnoDB引擎內置的ROWID作為聚集索引;
可以看到,在這個索引結構的葉子節點中,節點key值是主鍵的值,而節點的value則存儲其余列數據,以及額外的ROWID、rollback pointer、trx id等信息。
結合這個圖,以及上面所述,我們可以知道:在InnoDB表中,其聚集索引相當於整張表,而整張表也是聚集索引。
主鍵必然是聚集索引,而聚集索引則未必是主鍵。
MyISAM是堆組織表,它沒有聚集索引的概念。
以下是這一篇的總結 http://www.jb51.net/article/76702.htm:
作為免費又高效的數據庫,mysql基本是首選。良好的安全連接,自帶查詢解析、sql語句優化,使用讀寫鎖(細化到行)、事物隔離和多版本並發控制提高並發,完備的事務日志記錄,強大的存儲引擎提供高效查詢(表記錄可達百萬級),如果是InnoDB,還可在崩潰后進行完整的恢復,優點非常多。 首先,mysql的查詢流程大致是:mysql客戶端通過協議與mysql服務器建立連接,發送查詢語句,先檢查查詢緩存,如果命中,直接返回結果,否則進行語句解析,有一系列預處理,比如檢查語句是否寫正確了,然后是查詢優化(比如是否使用索引掃描,如果是一個不可能的條件,則提前終止),生成查詢計划,然后查詢引擎啟動,開始執行查詢,從底層存儲引擎調用API獲取數據,最后返回給客戶端。怎么存數據、怎么取數據,都與存儲引擎有關。然后,mysql默認使用的BTREE索引,並且一個大方向是,無論怎么折騰sql,至少在目前來說,mysql最多只用到表中的一個索引。 mysql通過存儲引擎取數據,自然跟存儲引擎有很大關系,不同的存儲引擎索引也不一樣,如MyISAM的全文索引,即便索引叫一個名字內部組織方式也不盡相同,最常用的當然就是InnoDB了(還有完全兼容mysql的MariaDB,它的默引擎是XtraDB,跟InnoDB很像),這里寫的是InnoDB引擎。而索引的實現也跟存儲引擎,按照實現方式分,InnoDB的索引目前只有兩種:BTREE索引和HASH索引。通常我們說的索引不出意外指的就是B樹索引,InnoDB的BTREE索引,實際是用B+樹實現的,因為在查看表索引時,mysql一律打印BTREE,所以簡稱為B樹索引。 結構中,每一層節點均從左往右從小到大排列,key1 < key2 < ... < keyN,對於小於key1或在[key1,key2)或其他的值的節點,在進入葉子節點查找,是一個范圍分布,同時,同一層節點之間可直接訪問,因為他們之間有指針指向聯系(MyISAM的BTREE索引沒有)。每次搜索是一個區間搜索,有的話就找到了,沒有的話就是空。 對於InnoDB,它指向的就是表數據,因為InnoDB的表數據本身就是索引文件,這是與MyISAM索引的顯著區別,MyISAM的索引指向的是表數據的地址(val指向的是類似於0x7DFF..之類)。 InnoDB的索引節點val值直接指向表數據,即它的葉子節點就是表數據,它們連在一起,表記錄行沒有再單獨放在其他地方,葉子節點(數據)之間可訪問。
一些注意點:
如果非自增或不是整數索引,如非自增整數、類似MD5的字符串,以他們作為索引值時,因為待插入的下一條數據的值不一定比上一條大,甚至比當前頁所有值都小,需要跑到前幾頁去比較而找到合適位置,InnoDB無法簡單的把新行插入到上一行后面,而找到並插入索引后,可能導致該頁達到分裂因子閥值,需要頁分裂,進一步導致后面所有的索引頁的分裂和排序,數據量小也許沒什么問題,數據量大的話可能會浪費大量時間,產生許多碎片。 主鍵總是唯一且非空,InnoDB自動對它建立了索引(primary key),對於非主鍵字段上建立的索引,又稱輔助索引,索引排列也是順序排列,只是它還附帶一個本條記錄的主鍵值的數據域,不是指向本數據行的指針。 在使用輔助索引查找時,先找到對應這一列的索引值,再根據索引節點上的另一個數據域---主鍵值,來查找該行記錄,即每次查找實際經過查找了兩次。 額外的數據域存儲主鍵值的好處是,當頁分裂發生時,無需修改數據域的值,因為即使頁分裂,該行的主鍵值是不變的,而地址就變了。
創建索引的語法:
mysql> create index user_last_name_index on user(last_name);
索引類型的說明:
唯一索引(UNIQUE):不允許兩行具有相同的索引值(創建了唯一約束,系統將自動創建唯一索引)
主鍵索引:主鍵索引要求主鍵中的每個值是唯一的,(創建主鍵自動創建主鍵索引)
聚集索引(CLUSTERED):表中各行的物理順序與鍵值的邏輯(索引)順序相同,表中只能包含一個聚集索引,主鍵列默認為聚集索引
非聚集索引(NONCLUSTERED):表中各行的物理順序與鍵值的邏輯(索引)順序不匹配,表中可以有249個非聚集索引
非聯合索引,分析如下幾種情況:
首先索引情況是:
mysql> show index from tx \G *************************** 1. row *************************** Table: tx Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 4 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: tx Non_unique: 1 Key_name: tx_num_index Seq_in_index: 1 Column_name: num Collation: A Cardinality: 9 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) mysql> show index from user \G *************************** 1. row *************************** Table: user Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 4 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: user Non_unique: 1 Key_name: user_last_name_index Seq_in_index: 1 Column_name: last_name Collation: A Cardinality: 4 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec)
然后,explain sql語句的結果如下:
mysql> explain select * from tx where num = 3; +----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+ | 1 | SIMPLE | tx | ref | tx_num_index | tx_num_index | 9 | const | 1 | Using where; Using index | +----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+ 1 row in set (0.00 sec) mysql> explain select num from tx where num = 3; +----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+ | 1 | SIMPLE | tx | ref | tx_num_index | tx_num_index | 9 | const | 1 | Using where; Using index | +----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+ 1 row in set (0.00 sec) mysql> explain select id from tx where id = 3; +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | tx | const | PRIMARY | PRIMARY | 8 | const | 1 | Using index | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from user where last_name = 'gates'; +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+-------------+ | 1 | SIMPLE | user | ref | user_last_name_index | user_last_name_index | 138 | const | 1 | Using where | +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+-------------+ 1 row in set (0.01 sec)
mysql> explain select last_name from user where last_name = 'gates';
+----+-------------+-------+------+----------------------+----------------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------+----------------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | user | ref | user_last_name_index | user_last_name_index | 138 | const | 1 | Using where; Using index |
+----+-------------+-------+------+----------------------+----------------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
關於Using index和Using where,下面的解釋比較清楚:
Extra:using index 也就意味着,現在直接訪問索引就中足夠獲取到所需要的數據,不需要索引回表, using index也就是平常說的覆蓋索引掃描。只訪問必須訪問的數據 ,在一般情況下,減少不必要的數據訪問能夠提升效率 Extra :using where 表示優化器需要通過索引回表查詢 兩種都存在:(我的理解)要輸出的列都在索引里。不需要再回原表拿數據。using where 只是表示了一個lookup的條件,而不是行為。
Using where只是過濾元組,和是否讀取數據文件或索引文件沒有關系。 英文原文: Using index The column information is retrieved from the table using only information in the index tree without having to do an additional seek to read the actual row. This strategy can be used when the query uses only columns that are part of a single index. Using where A WHERE clause is used to restrict which rows to match against the next table or send to the client. Unless you specifically intend to fetch or examine all rows from the table, you may have something wrong in your query if the Extra value is not Using where and the table join type is ALL or index. 但MySQL手冊在Using index處又說: If the Extra column also says Using where, it means the index is being used to perform lookups of key values. Without Using where, the optimizer may be reading the index to avoid reading data rows but not using it for lookups. For example, if the index is a covering index for the query, the optimizer may scan it without using it for lookups.
復合索引:
包含一列的索引稱為單列索引,多列的稱為復合索引,因為BTREE索引是順序排列的,所以比較適合范圍查詢,
但是在復合索引中,還應注意列數目、列的順序以及前面范圍查詢的列對后邊列的影響。
再回來談談聯合索引(復合索引)。先建表:
mysql> create table staffs( -> id int primary key auto_increment, -> name varchar(24) not null default '' comment '姓名', -> age int not null default 0 comment '年齡', -> pos varchar(20) not null default '' comment '職位', -> add_time timestamp not null default current_timestamp comment '入職時間' -> ) engine= InnoDB charset utf8 comment '員工記錄表'; Query OK, 0 rows affected (0.07 sec) mysql> select * from staffs; Empty set (0.00 sec) mysql> alter table staffs add index idx_nap(name, age, pos); Query OK, 0 rows affected (0.27 sec) Records: 0 Duplicates: 0 Warnings: 0
注意以上有一種新的加索引的方式。用alter table 而不是之前的 create index.
mysql> insert into staffs(name, age, pos) values('bill', 20, 'ceo'); Query OK, 1 row affected (0.10 sec) mysql> explain select * from staffs; +----+-------------+--------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+------+---------+------+------+-------+ | 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 1 | | +----+-------------+--------+------+---------------+------+---------+------+------+-------+ 1 row in set (0.00 sec)
注:無索引,無Extra mysql> explain select * from staffs where name = 'bill' and age = 20 and pos = 'ceo'; +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 140 | const,const,const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ 1 row in set (0.00 sec)
注:有索引,Extra有where mysql> explain select * from staffs where name = 'bill' and age = 20; +----+-------------+--------+------+---------------+---------+---------+-------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 78 | const,const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------------+------+-------------+ 1 row in set (0.00 sec)
注:用了索引中前2個字段 mysql> explain select * from staffs where name = 'bill' and pos = 'ceo'; +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 74 | const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ 1 row in set (0.00 sec)
注:索引中1,3字段使得只用了索引中第1個字段
mysql> explain select * from staffs where age = 20; +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 1 | Using where | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
注:索引中第2個字段,用不了索引。索引必須從第1個字段開始。 mysql> explain select * from staffs where name = 'b%'; +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 74 | const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ 1 row in set (0.00 sec)
注:第1個字段 字符開頭%,可以用到索引。 mysql> explain select * from staffs where name = '%b'; +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 74 | const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ 1 row in set (0.00 sec)
注:第1個字段,%開頭字符串,可以用到索引(有的文章說不能用,親測可以,可能是InnoDB的緣故)。
mysql> explain select * from staffs where name = '%b' and age = 20;
+----+-------------+--------+------+---------------+---------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+---------+---------+-------------+------+-------------+
| 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 78 | const,const | 1 | Using where |
+----+-------------+--------+------+---------------+---------+---------+-------------+------+-------------+
1 row in set (0.00 sec)
注:第1個字段,%開頭字符串,不影響使用2個以上字段作為索引。
mysql> explain select * from staffs where name > 'bill'; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | idx_nap | idx_nap | 74 | NULL | 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec)
注:>可以用到索引 mysql> explain select * from staffs where name >= 'bill'; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | idx_nap | idx_nap | 74 | NULL | 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.01 sec)
注:>=可以用到索引 mysql> explain select * from staffs where name > 'bill' and age = 20; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | idx_nap | idx_nap | 74 | NULL | 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.01 sec)
注:> 之后,第二個字段不能再被使用加到索引里 mysql> explain select * from staffs where name >= 'bill' and age = 20; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | idx_nap | idx_nap | 78 | NULL | 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec)
注:>= 之后,第二個字段可以被使用加到索引里
以下,是進一步的鞏固。
mysql> explain select * from staffs where name >= 'bill' and age > 20; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | idx_nap | idx_nap | 78 | NULL | 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from staffs where name >= 'bill' and age >= 20; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | idx_nap | idx_nap | 78 | NULL | 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from staffs where name = 'bill' and age > 20; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | idx_nap | idx_nap | 78 | NULL | 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from staffs where name >= 'bill' and age >= 20 and pos > 'ceo'; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | idx_nap | idx_nap | 140 | NULL | 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec)
下面,對於索引使用順序換了一下。有的文章說就用不了索引了,但是InnoDB親測,完全可以用到索引:
mysql> explain select * from staffs where age = 20 and name = 'bill' and pos = 'ceo'; +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 140 | const,const,const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ 1 row in set (0.00 sec)
注:順序相當於 b,a,c,可以用到索引 mysql> explain select * from staffs where age = 20 and pos = 'ceo' and name = 'bill'; +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 140 | const,const,const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ 1 row in set (0.00 sec)
注:順序相當於b,c,a,可以用到索引
mysql> explain select * from staffs where pos = 'ceo' and age = 20 and name = 'bill';
+----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+
| 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 140 | const,const,const | 1 | Using where |
+----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+
1 row in set (0.00 sec)
注:順序相當於c,b,a,可以用到索引
一些聯合索引對於查詢是否會使用到的總結:
以上為常見的使用索引的方式,有這么些情況不能用或不能全用,有的就是上面情況的反例,以key(a, b, c)為例
1. 跳過列,where a = 1 and c = 3,最多用到索引列a;where b = 2 and c = 3,一個也用不到,必須從最左列開始 2. 前面是范圍查詢,where a = 1 and b > 2 and c = 3,最多用到 a, b兩個索引列; 3. 順序顛倒,where c = 3 and b = 2 and a = 1,一個也用不到;(注,上面提到了,測試結果不是這樣) 4. 索引列上使用了表達式,如where substr(a, 1, 3) = 'hhh',where a = a + 1,表達式是一大忌諱,再簡單mysql也不認。有時數據量不是大到嚴重影響速度時,一般可以先查出來,比如先查所有有訂單記錄的數據,再在程序中去篩選以'cp1001'開頭的訂單,而不是寫sql過濾它; 5. 模糊匹配時,盡量寫 where a like 'J%',字符串放在左邊,這樣才可能用得到a列索引,甚至可能還用不到,當然這得看數據類型,最好測試一下。(注:InnoDB放前后都可以用到索引)
對於like,不會使用索引:
mysql> explain select * from staffs where id like '1%';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | PRIMARY | NULL | NULL | NULL | 2 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
有order操作的情況:
mysql> explain select * from staffs where name = 'a' order by age; +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 74 | const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ 1 row in set (0.00 sec)
注:有order不影響where使用的索引 mysql> explain select * from staffs order by name; +----+-------------+--------+------+---------------+------+---------+------+------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+------+---------+------+------+----------------+ | 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 2 | Using filesort | +----+-------------+--------+------+---------------+------+---------+------+------+----------------+ 1 row in set (0.00 sec)
注:直接order 非主鍵 不使用索引。(name換成age也不使用) mysql> explain select * from staffs order by id; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ | 1 | SIMPLE | staffs | index | NULL | PRIMARY | 4 | NULL | 2 | | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ 1 row in set (0.01 sec)
注:order主鍵,使用索引
mysql> explain select * from staffs order by name,age,pos;
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 2 | Using filesort |
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)
注:聯合索引都寫全了,也不使用索引
覆蓋索引,應該不會讀數據文件了:
前面說到,mysql索引從結構上只有兩類,BTREE與HASH,覆蓋索引只是在查詢時,要查詢的列剛好與使用的索引列完全一致,mysql直接掃描索引,然后就可返回數據,
大大提高效率,因為不需再去原表查詢、過濾,這種形式下的索引稱作覆蓋索引,比如key(a,b),查詢時select a,b from tab where a = 1 and b > 2,
本質原因:BTREE索引存儲了原表數據。 聚簇索引也不是單獨的索引,前面簡要寫到,BTREE索引會把數據放在索引中,即索引的葉子頁中,包括主鍵,主鍵是跟表數據緊挨着放在一起的,
因為表數據只有一份,一列鍵值要跟每一行數據都緊挨在一起,所以一張表只有一個聚簇索引,對於mysql來說,就是主鍵列,它是默認的。
哈希索引:
簡要說下,類似於數據結構中簡單實現的HASH表(散列表)一樣,當我們在mysql中用哈希索引時,也是對索引列計算一個散列值(類似md5、sha1、crc32),然后對這個散列值以順序(默認升序)排列,同時記錄該散列值對應數據表中某行的指針,當然這只是簡略模擬圖 哈希索引的結構決定了它的特點: 1. hash索引只是hash值順序排列,跟表數據沒有關系,無法應用於order by; 2. hash索引是對它的所有列計算哈希值,因此在查詢時,必須帶上所有列,比如有(a, b)哈希索引,查詢時必須 where a = 1 and b = 2,少任何一個不行; 3. hash索引只能用於比較查詢 = 或 IN,其他范圍查詢無效,本質還是因不存儲表數據; 4. 一旦出現碰撞,hash索引必須遍歷所有的hash值,將地址所指向數據一一比較,直到找到所有符合條件的行。
上面explain結果里面key_len的解釋:
前面提到通過explain的key_len字段,可大致估計出用了哪些列,索引列的長度跟索引列的數據類型直接相關,一般,我們說int是4字節,bigint8字節,char是1字節,考慮到建表時要指定字符集,比如utf8,還跟選的字符集有關(==!),在utf8下邊,一個char是3字節。 比如對於 varchar(24)的索引,key_len就是72,但是貌似實際是74,加上了2. int的key_len是4,放在第二位,合起來是78.放在第一位就是4. 第三位var(20),長度60,也是加上2得到62. 跟前面78合起來,就是140.
新的一篇 http://www.jb51.net/article/81779.htm
我們知道一般的應用系統,讀寫比例在10:1左右,而且插入操作和一般的更新操作很少出現性能問題,
遇到最多的,也是最容易出問題的,還是一些復雜的查詢操作,所以查詢語句的優化顯然是重中之重。
注意,like還是用不到索引的:
mysql> explain select * from staffs where id like 1;
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | PRIMARY | NULL | NULL | NULL | 2 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
一次查詢能用多個索引嗎?
不能
in能夠用到索引,not in不行:
mysql> explain select * from staffs where id in (2, 3); +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from staffs where id not in (2, 3); +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | ALL | PRIMARY | NULL | NULL | NULL | 2 | Using where | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
另外,like x, like x%, like %x 都不能用到索引
mysql> explain select * from staffs where id like 'a'; +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | ALL | PRIMARY | NULL | NULL | NULL | 2 | Using where | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from staffs where id like 'a%'; +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | ALL | PRIMARY | NULL | NULL | NULL | 2 | Using where | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from staffs where id like '%a'; +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 2 | Using where | +----+-------------+--------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.01 sec)
!=是可以用到索引的:
mysql> explain select * from staffs where id != 3;
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | range | PRIMARY | PRIMARY | 4 | NULL | 3 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)
用or的話,即使其中存在索引,也用不到:
mysql> explain select * from staffs where id = 1 or name='a';
+----+-------------+--------+------+-----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+-----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | PRIMARY,idx_nap | NULL | NULL | NULL | 2 | Using where |
+----+-------------+--------+------+-----------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
NULL帶來的問題:
NULL 的問題
NULL會導致索引形同虛設,所以在設計表結構時應避免NULL 的存在(用其他方式表達你想表達的NULL,比如 -1?)
我試了,存在null還是會使用索引:
mysql> select * from staffs; +----+------+-----+-----+---------------------+ | id | name | age | pos | add_time | +----+------+-----+-----+---------------------+ | 1 | bill | 20 | ceo | 2016-10-21 20:02:26 | | 2 | mark | 21 | cfo | 2016-10-21 22:29:50 | | 3 | tom | 22 | | 2016-10-21 23:36:21 | +----+------+-----+-----+---------------------+ 3 rows in set (0.00 sec) mysql> explain select * from staffs where name='a' and age = 10 and pos = 'coo'; +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_nap | idx_nap | 140 | const,const,const | 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------------------+------+-------------+ 1 row in set (0.00 sec)
下面是這篇 http://blog.csdn.net/xqy1522/article/details/6750252
堆表(heap table)數據插入時時存儲位置是隨機的,主要是數據庫內部塊的空閑情況決定,獲取數據是按照命中率計算,全表掃表時不見得先插入的數據先查到。
索引表(iot)數據存儲是把表按照索引的方式存儲的,數據是有序的,數據的位置是預先定好的,與插入的順序沒有關系。
索引表的查詢效率逼堆表高(相當於查詢索引的效率),插入數據的速度比堆表慢。
Oracle支持堆表,也支持索引組織表
PostgreSQL只支持堆表,不支持索引組織表
Innodb只支持索引組織表
下面是這篇 http://www.infoq.com/cn/news/2013/12/mysql-vs-postgresql/
Postgresql 與 Mysql的對比:
使用快速的索引序列訪問方法(ISAM),不支持ACID。經過早期快速的發展之后,MySQL開始支持更多的存儲引擎,並通過InnoDB引擎實現了ACID。
MySQL還支持其他存儲引擎,提供了臨時表的功能(使用MEMORY存儲引擎),
通過MyISAM引擎實現了高速讀的數據庫,此外還有其他的核心存儲引擎與第三方引擎。
PostgreSQL是完全由社區驅動的開源項目,由全世界超過1000名貢獻者所維護。它提供了單個完整功能的版本,
而不像MySQL那樣提供了多個不同的社區版、商業版與企業版。 PostgreSQL基於版權:PostgreSQL基於自由的BSD/MIT許可,組織可以使用、復制、修改和重新分發代碼,只需要提供一個版權聲明即可。 Mysql基於版權:其核心代碼基於GPL許可,對於那些不想使用GPL許可的開發者與廠商來說還有商業許可可供使用。
可靠性是PostgreSQL的最高優先級。它以堅如磐石的品質和良好的工程化而聞名,支持高事務、任務關鍵型應用。
數據一致性與完整性也是PostgreSQL的高優先級特性。
PostgreSQL是完全支持ACID特性的,它對於數據庫訪問提供了強大的安全性保證,充分利用了企業安全工具,如Kerberos與OpenSSL等。
應用對比:
MySQL與PostgreSQL都出現在一些高流量的Web站點上:
MySQL:Slashdot、Twitter、Facebook與Wikipedia
PostgreSQL:Yahoo使用了一個修改的PostgreSQL數據庫來處理每天數以億計的事件,還有Reddit和Disqus
MySQL在一個領域上要比PostgreSQL更進一步,那就是它的觸角延伸到了嵌入式領域,這是通過libmysqld實現的。
PostgreSQL不支持嵌入式應用,依然堅守在傳統的客戶端/服務器架構上。
特性對比
MySQL通常被認為是針對網站與應用的快速數據庫后端,能夠進行快速的讀取和大量的查詢操作,不過在復雜特性與數據完整性檢查方面不太盡如人意。
PostgreSQL是針對事務型企業應用的嚴肅、功能完善的數據庫,支持強ACID特性和很多數據完整性檢查。
他們二者都在某些任務上具有很快的速度,MySQL不同存儲引擎的行為有較大差別。
MyISAM引擎是最快的,因為它只執行很少的數據完整性檢查,適合於后端讀操作較多的站點,不過對於包含敏感數據的讀/寫數據庫來說就是個災難了,
因為MyISAM表最終可能會損壞。
MySQL提供了修復MySQL表的工具,不過對於敏感數據來說,支持ACID特性的InnoDB則是個更好的選擇。
與之相反,PostgreSQL則是個只有單一存儲引擎的完全集成的數據庫。
你可以通過調整postgresql.conf文件的參數來改進性能,也可以調整查詢與事務。PostgreSQL文檔對於性能調優提供了非常詳盡的介紹。
MySQL與PostgreSQL都是高可配置的,並且可以針對不同的任務進行相應的優化。他們都支持通過擴展來添加額外的功能。
標准兼容性
PostgreSQL旨在實現SQL兼容性(當前標准是ANSI-SQL:2008)。 MySQL則兼容大部分SQL,不過還有自己的擴展,可以支持NoSQL特性,這在參考手冊中都有介紹。 注:在MySQL5.6.2版本增加了通過Memcached協議直接訪問原生Innodb API的功能。 參考:http://www.cnblogs.com/sunli/archive/2011/05/11/mysql-nosql.html 每種方式都有優缺點。兼容標准會讓數據庫管理員、數據庫開發者與應用開發者更舒服一些,因為這意味着他們只需學習一套標准、一套特性和命令即可。
這會節省時間,提升效率,也不會被鎖定在特定的廠商上。 支持使用非標准的自定義功能的人們認為這樣可以快速采用新的特性,而不必等待標准進程完成。ANSI/ISO標准在不斷演化,因此標准兼容性也是個變化的目標:
知名的關系型數據庫Microsoft SQL Server、Oracle與IBM DB2也只是部分兼容於標准。
結論:
雖然有不同的歷史、引擎與工具,不過並沒有明確的參考能夠表明這兩個數據庫哪一個能夠適用於所有情況。
很多組織喜歡使用PostgreSQL,因為它的可靠性好,在保護數據方面很擅長,而且是個社區項目,不會陷入廠商的牢籠之中。
MySQL更加靈活,提供了更多選項來針對不同的任務進行裁剪。
很多時候,對於一個組織來說,對某個軟件使用的熟練程度要比特性上的原因更重要。
聯合主鍵、自增長 http://blog.sina.com.cn/s/blog_7a9af9b10101epem.html:
Mysql多列可以被設為聯合主鍵。
如果需要對主鍵設成自增長,因為自增長是列維度的,所以單獨設置;
如果自增長設的不是第一列,那么對於第一列增加時,當前列自動變成1;
對於InnoDB引擎,不允許聯合主鍵中設置自增長;因為規定自增長的列,必須為key。
對於MyISAM引擎,運行,並且行為如上面描述。
關於數據庫的引擎,比如Innodb,MyIsam的技術細節,對比等,另開一篇描述。