1. 程式人生 > >mysql索引的最左字首原則

mysql索引的最左字首原則

聯合索引有一個最左字首原則,所以建立聯合索引的時候,這個聯合索引的欄位順序非常重要
下面寫了例子說明這個:

CREATE TABLE `test_myisam` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `conference_id` varchar(200) NOT NULL,
  `account` varchar(100) DEFAULT NULL,
  `status` int(2) DEFAULT NULL COMMENT '0:invite,  1:cancel_invite,  2:decline,  3:connect',
  `duration`
bigint(20) unsigned DEFAULT NULL, `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=myisam AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

以上表結構,我想通過三列進行查詢 account ,status,create_time進行查詢統計。
如何建立索引?
因為我們有可能按照acccount單獨統計,或者按照account status,或者是account,status,create_time進行統計,如何建立索引???

下面是建立索引前後的對比600萬資料
如何生成:執行如下指令碼,account和日期不同還有status不同,分別生成一百萬。



 CREATE  PROCEDURE `add_data_myisam_cp_27`()
    begin
    declare v_rows int(10) default 1000000;
    declare v_count int(10) default 0;
    id_loop:LOOP
    insert into test_myisam values(null,round(rand()*1000000000),'cloudp',round(rand()*3),round(rand()*100000
),'2016-07-27 00:00:22'); set v_count= v_count + 1; if v_count>v_rows then leave id_loop; end if; end loop id_loop; end;

測試結果利用建立的索引效能提高了三倍:

MariaDB [prf]> select count(1) from test_myisam where account='cloudp' and status =3 and date(create_time)='2016-07-27';
+----------+
| count(1) |
+----------+
|   167400 |
+----------+
1 row in set (1.28 sec)

MariaDB [prf]> create index as_index on test_myisam(account,status,create_time);
Query OK, 6000006 rows affected (31.60 sec)
Records: 6000006  Duplicates: 0  Warnings: 0

MariaDB [prf]> select count(1) from test_myisam where account='cloudp' and status =3 and date(create_time)='2016-07-27';
+----------+
| count(1) |
+----------+
|   167400 |
+----------+
1 row in set (0.42 sec)

MariaDB [prf]> explain select count(1) from test_myisam where account='cloudp' and status =3 and date(create_time)='2016-07-27';
+------+-------------+-------------+------+---------------+----------+---------+-------------+--------+--------------------------+
| id   | select_type | table       | type | possible_keys | key      | key_len | ref         | rows   | Extra                    |
+------+-------------+-------------+------+---------------+----------+---------+-------------+--------+--------------------------+
|    1 | SIMPLE      | test_myisam | ref  | as_index      | as_index | 308     | const,const | 520216 | Using where; Using index |
+------+-------------+-------------+------+---------------+----------+---------+-------------+--------+--------------------------+
1 row in set (0.00 sec)

從1.28秒下降到0.42秒
但是這個date(create_time)會對每一列都會轉換後對比,這裡會比較消耗效能;
如何利用上索引??
修改為:


MariaDB [prf]> explain select count(1) from test_myisam where account='cloudp' and status =3 and date(create_time)='2016-07-27';
+------+-------------+-------------+------+---------------+----------+---------+-------------+--------+--------------------------+
| id   | select_type | table       | type | possible_keys | key      | key_len | ref         | rows   | Extra                    |
+------+-------------+-------------+------+---------------+----------+---------+-------------+--------+--------------------------+
|    1 | SIMPLE      | test_myisam | ref  | as_index      | as_index | 308     | const,const | 520216 | Using where; Using index |
+------+-------------+-------------+------+---------------+----------+---------+-------------+--------+--------------------------+
1 row in set (0.00 sec)

MariaDB [prf]> select count(1) from test_myisam where account='cloudp' and status =3 and create_time  between '2016-07-27' and '2016-07-28';
+----------+
| count(1) |
+----------+
|   167400 |
+----------+
1 row in set (0.15 sec)

MariaDB [prf]> explain select count(1) from test_myisam where account='cloudp' and status =3 and create_time  between '2016-07-27' and '2016-07-28';
+------+-------------+-------------+-------+---------------+----------+---------+------+--------+--------------------------+
| id   | select_type | table       | type  | possible_keys | key      | key_len | ref  | rows   | Extra                    |
+------+-------------+-------------+-------+---------------+----------+---------+------+--------+--------------------------+
|    1 | SIMPLE      | test_myisam | range | as_index      | as_index | 312     | NULL | 174152 | Using where; Using index |
+------+-------------+-------------+-------+---------------+----------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)

如上效率又提高了三倍,是因為掃描的資料行數減少了,最後一個create_time如果不用索引需要掃描52016行,如果使用了索引掃描174152行,命中的行數為:167400行,命中率非常高了。

這裡有個疑問:
如果按照天進行統計,create_time作為聯合索引的第一列,如何使用上這個索引呢????
至今沒有想清楚,如果這一列是date型別可以直接用上索引,如果在oracle中可以date(create_time)建立函式式索引。但是mysql貌似不支援函式式索引。

一個解決方式是: create_time定義為 date型別,在每一列存入的時候,通過觸發器自動把這一行修改為date型別。

如果有好的注意歡迎留言探討,目前沒有好的方式加上create_time,可以從業務上解決,就是每天的統計計算完成以後,直接把資料推到歷史表中,統計結果單獨存放。