Mysql combine index
先说骗子。这年头骗子真多。刚才拨打一个网上搜索的特价飞机订票热线400-6064-***,对方叫我直接去建设银行上汇钱再给出票,还给了一个个人的建设银行账户。明显就是骗子,还好老婆提醒我不贪图小利,警防骗子,不然我真去银行傻汇了。
在此提醒大家出行注意安全。
Oracle的组合索引操作有2种,access和filter,当然access更好。Mysql在组合索引上,只有access,没有filter。因此要注意MYSQL处理where clause包括多个range条件的SQL语句。
详见测试案例和truss中对MYD文件的pread输出。
表结构
mysql> show create table test;
+――-+――――-+――-+――――+
| Table | Create Table
+――-+――――-+――-+――――+
| test | CREATE TABLE “test” (
“a” int(11) NOT NULL,
“b” int(11) NOT NULL,
“c” int(11) default NULL,
“d” char(20) default NULL,
KEY “test3_idx” USING BTREE (”a”,”b”,”c”)
) ENGINE=MYISAM DEFAULT CHARSET=utf8 |
+――-+――――++――-+――――-+
1 row in set (0.00 sec)
mysql> show table status;
+――-+――-|――――+――-+―――――-+
| Name | Engine | Row_format | Rows | Avg_row_length |
+――-+――-|――――+――-+―――――-+
| test | MyISAM | Fixed | 1598 | 73 |
+――-+――-|――――+――-+―――――-+
3 rows in set (0.00 sec)
在列a,b,c上存在一个复合索引。每行需73 bytes的固定长度。下面是各种组合查询的explain plan.
mysql> show table status;
+――-+――-|――――+――-+―――――-+
| Name | Engine | Row_format | Rows | Avg_row_length |
+――-+――-|――――+――-+―――――-+
| test | MyISAM | Fixed | 1598 | 73 |
+――-+――-|――――+――-+―――――-+
3 rows in set (0.00 sec)
mysql> explain select a,b,c from test where a=1;
+―-+――――-+――-+――+―――――+―――-+―――+――-+――+――-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+―-+――――-+――-+――+―――――+―――-+―――+――-+――+――-+
| 1 | SIMPLE | test | ref | test3_idx | test3_idx | 4 | const | 1595 | |
+―-+――――-+――-+――+―――――+―――-+―――+――-+――+――-+
1 row in set (0.00 sec)
mysql> explain select a,b,c from test where a=1 and b=1555 ;
+―-+――――-+――-+――+―――――+―――-+―――+――――-+――+――-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+―-+――――-+――-+――+―――――+―――-+―――+――――-+――+――-+
| 1 | SIMPLE | test | ref | test3_idx | test3_idx | 8 | const,const | 1 | |
+―-+――――-+――-+――+―――――+―――-+―――+――――-+――+――-+
1 row in set (0.00 sec)
mysql> explain select a,b,c from test where a=1 and b>1555 ;
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
| 1 | SIMPLE | test | range | test3_idx | test3_idx | 8 | NULL | 1 | Using where |
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
1 row in set (0.00 sec)
mysql> explain select a,b,c from test where a=1 and b=1555 and c=3000;
+―-+――――-+――-+――+―――――+―――-+―――+――――――-+――+――――-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+―-+――――-+――-+――+―――――+―――-+―――+――――――-+――+――――-+
| 1 | SIMPLE | test | ref | test3_idx | test3_idx | 13 | const,const,const | 1 | Using where |
+―-+――――-+――-+――+―――――+―――-+―――+――――――-+――+――――-+
1 row in set (0.01 sec)
mysql> explain select a,b,c from test where a=1 and b=1555 and c<=1554;
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
| 1 | SIMPLE | test | range | test3_idx | test3_idx | 13 | NULL | 1 | Using where |
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
1 row in set (0.00 sec)
mysql> explain select a,b,c from test where a=1 and b>1500 and c<=1000;
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
| 1 | SIMPLE | test | range | test3_idx | test3_idx | 8 | NULL | 6 | Using where |
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
1 row in set (0.00 sec)
mysql> explain select * from test where a=1 and b>1500 and c=1000;
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
| 1 | SIMPLE | test | range | test3_idx | test3_idx | 8 | NULL | 102 | Using where |
+―-+――――-+――-+――-+―――――+―――-+―――+――+――+――――-+
1 row in set (0.00 sec)
从explain中的key_len列,可以看到会被access的组合索引的列长度。 key_len=13的,表示a,b,c 3列都在access-list中。key_len=8的表示只有a,b 两列参与access。
那最后2个SQL可有不同?SQL A >select a,b,c from test where a=1 and b>1500 and c=1000; 和SQL B >select * from test where a=1 and b>1500 and c=1000。
通过truss,可以看到SQL A并没有读MYD文件(表段),只读取了MYI文件(索引文件);SQL B 既读取了MYI,也读取了MYD文件。而且在pread(MYD) 文件的时候,读取了 select couunt(*) from test where a=1 and b>1500次。
表示SQL B,MYSQL在组合索引上通过a=1 and b>1500 条件返回N条记录,然后通过记录在索引中的offset去一一读取表段MYD,判断条件C=1000是否满足。 虽然列C在组合索引中,但仍然需要访问表。
SQL A 没有读取MYD文件,是因为select的字段全部包括在组合索引中,mysql判断出能够在组合索引中完成过滤filter,也算是智能了。不过这个filtter根Oracle的filter完全不同。
如下是truss SQL B 读取MYD文件的输出。
/7: read(45, ” 5\\0\\0\\0″, 4) = 4
/7: read(45, “03 s e l e c t * f”.., 53) = 53
/7: time() = 1252743991
/7: time() = 1252743991
/7: pread(48, “F901\\0\\0\\0DD05\\0\\0DD05\\0″.., 73, 109500) = 73
/7: pread(48, “F901\\0\\0\\0DE05\\0\\0DE05\\0″.., 73, 109573) = 73
/7: pread(48, “F901\\0\\0\\0DF05\\0\\0DF05\\0″.., 73, 109646) = 73
/7: pread(48, “F901\\0\\0\\0E005\\0\\0E005\\0″.., 73, 109719) = 73
/7: pread(48, “F901\\0\\0\\0E105\\0\\0E105\\0″.., 73, 109792) = 73
/7: pread(48, “F901\\0\\0\\0E205\\0\\0E205\\0″.., 73, 109865) = 73
/7: pread(48, “F901\\0\\0\\0E305\\0\\0E305\\0″.., 73, 109938) = 73
/7: pread(48, “F901\\0\\0\\0E405\\0\\0E405\\0″.., 73, 110011) = 73
/7: pread(48, “F901\\0\\0\\0E505\\0\\0E505\\0″.., 73, 110084) = 73
/7: pread(48, “F901\\0\\0\\0E605\\0\\0E605\\0″.., 73, 110157) = 73
/7: pread(48, “F901\\0\\0\\0E705\\0\\0E705\\0″.., 73, 110230) = 73
/7: pread(48, “F901\\0\\0\\0E805\\0\\0E805\\0″.., 73, 110303) = 73
/7: pread(48, “F901\\0\\0\\0E905\\0\\0E905\\0″.., 73, 110376) = 73
/7: pread(48, “F901\\0\\0\\0EA05\\0\\0EA05\\0″.., 73, 110449) = 73
/7: pread(48, “F901\\0\\0\\0EB05\\0\\0EB05\\0″.., 73, 110522) = 73
/7: pread(48, “F901\\0\\0\\0EC05\\0\\0EC05\\0″.., 73, 110595) = 73
/7: pread(48, “F901\\0\\0\\0ED05\\0\\0ED05\\0″.., 73, 110668) = 73
/7: pread(48, “F901\\0\\0\\0EE05\\0\\0EE05\\0″.., 73, 110741) = 73
/7: pread(48, “F901\\0\\0\\0EF05\\0\\0EF05\\0″.., 73, 110814) = 73
/7: pread(48, “F901\\0\\0\\0F005\\0\\0F005\\0″.., 73, 110887) = 73
/7: pread(48, “F901\\0\\0\\0F105\\0\\0F105\\0″.., 73, 110960) = 73
/7: pread(48, “F901\\0\\0\\0F205\\0\\0F205\\0″.., 73, 111033) = 73
/7: pread(48, “F901\\0\\0\\0F305\\0\\0F305\\0″.., 73, 111106) = 73
/7: pread(48, “F901\\0\\0\\0F405\\0\\0F405\\0″.., 73, 111179) = 73
/7: pread(48, “F901\\0\\0\\0F505\\0\\0F505\\0″.., 73, 111252) = 73
/7: pread(48, “F901\\0\\0\\0F605\\0\\0F605\\0″.., 73, 111325) = 73
/7: pread(48, “F901\\0\\0\\0F705\\0\\0F705\\0″.., 73, 111398) = 73
/7: pread(48, “F901\\0\\0\\0F805\\0\\0F805\\0″.., 73, 111471) = 73
/7: pread(48, “F901\\0\\0\\0F905\\0\\0F905\\0″.., 73, 111544) = 73
/7: pread(48, “F901\\0\\0\\0FA05\\0\\0FA05\\0″.., 73, 111617) = 73
/7: pread(48, “F901\\0\\0\\0FB05\\0\\0FB05\\0″.., 73, 111690) = 73
/7: pread(48, “F901\\0\\0\\0FC05\\0\\0FC05\\0″.., 73, 111763) = 73
/7: pread(48, “F901\\0\\0\\0FD05\\0\\0FD05\\0″.., 73, 111836) = 73
/7: pread(48, “F901\\0\\0\\0FE05\\0\\0FE05\\0″.., 73, 111909) = 73
/7: pread(48, “F901\\0\\0\\0FF05\\0\\0FF05\\0″.., 73, 111982) = 73
/7: pread(48, “F901\\0\\0\\0\\006\\0\\0\\006\\0″.., 73, 112055) = 73
/7: pread(48, “F901\\0\\0\\00106\\0\\00106\\0″.., 73, 112128) = 73
/7: pread(48, “F901\\0\\0\\00206\\0\\00206\\0″.., 73, 112201) = 73
/7: pread(48, “F901\\0\\0\\00306\\0\\00306\\0″.., 73, 112274) = 73
/7: pread(48, “F901\\0\\0\\00406\\0\\00406\\0″.., 73, 112347) = 73
/7: pread(48, “F901\\0\\0\\00506\\0\\00506\\0″.., 73, 112420) = 73
/7: pread(48, “F901\\0\\0\\00606\\0\\00606\\0″.., 73, 112493) = 73
/7: pread(48, “F901\\0\\0\\00706\\0\\00706\\0″.., 73, 112566) = 73
/7: pread(48, “F901\\0\\0\\0\\b06\\0\\0\\b06\\0″.., 73, 112639) = 73
/7: pread(48, “F901\\0\\0\\0\\t06\\0\\0\\t06\\0″.., 73, 112712) = 73
/7: pread(48, “F901\\0\\0\\0\\n06\\0\\0\\n06\\0″.., 73, 112785) = 73
/7: pread(48, “F901\\0\\0\\0\\v06\\0\\0\\v06\\0″.., 73, 112858) = 73
/7: pread(48, “F901\\0\\0\\0\\f06\\0\\0\\f06\\0″.., 73, 112931) = 73
/7: pread(48, “F901\\0\\0\\0\\r06\\0\\0\\r06\\0″.., 73, 113004) = 73
/7: pread(48, “F901\\0\\0\\00E06\\0\\00E06\\0″.., 73, 113077) = 73
/7: pread(48, “F901\\0\\0\\00F06\\0\\00F06\\0″.., 73, 113150) = 73
/7: pread(48, “F901\\0\\0\\01006\\0\\01006\\0″.., 73, 113223) = 73
/7: pread(48, “F901\\0\\0\\01106\\0\\01106\\0″.., 73, 113296) = 73
/7: pread(48, “F901\\0\\0\\01206\\0\\01206\\0″.., 73, 113369) = 73
/7: pread(48, “F901\\0\\0\\01306\\0\\01306\\0″.., 73, 113442) = 73
/7: pread(48, “F901\\0\\0\\01406\\0\\01406\\0″.., 73, 113515) = 73
/7: pread(48, “F901\\0\\0\\01506\\0\\01506\\0″.., 73, 113588) = 73
/7: pread(48, “F901\\0\\0\\01606\\0\\01606\\0″.., 73, 113661) = 73
/7: pread(48, “F901\\0\\0\\01706\\0\\01706\\0″.., 73, 113734) = 73
/7: pread(48, “F901\\0\\0\\01806\\0\\01806\\0″.., 73, 113807) = 73
/7: pread(48, “F901\\0\\0\\01906\\0\\01906\\0″.., 73, 113880) = 73
/7: pread(48, “F901\\0\\0\\01A06\\0\\01A06\\0″.., 73, 113953) = 73
/7: pread(48, “F901\\0\\0\\01B06\\0\\01B06\\0″.., 73, 114026) = 73
/7: pread(48, “F901\\0\\0\\01C06\\0\\01C06\\0″.., 73, 114099) = 73
/7: pread(48, “F901\\0\\0\\01D06\\0\\01D06\\0″.., 73, 114172) = 73
/7: pread(48, “F901\\0\\0\\01E06\\0\\01E06\\0″.., 73, 114245) = 73
/7: pread(48, “F901\\0\\0\\01F06\\0\\01F06\\0″.., 73, 114318) = 73
/7: pread(48, “F901\\0\\0\\0 06\\0\\0 06\\0″.., 73, 114391) = 73
/7: pread(48, “F901\\0\\0\\0 !06\\0\\0 !06\\0″.., 73, 114464) = 73
/7: pread(48, “F901\\0\\0\\0 “06\\0\\0 “06\\0″.., 73, 114537) = 73
/7: pread(48, “F901\\0\\0\\0 #06\\0\\0 #06\\0″.., 73, 114610) = 73
/7: pread(48, “F901\\0\\0\\0 $06\\0\\0 $06\\0″.., 73, 114683) = 73
/7: pread(48, “F901\\0\\0\\0 %06\\0\\0 %06\\0″.., 73, 114756) = 73
/7: pread(48, “F901\\0\\0\\0 &06\\0\\0 &06\\0″.., 73, 114829) = 73
/7: pread(48, “F901\\0\\0\\0 ‘06\\0\\0 ‘06\\0″.., 73, 114902) = 73
/7: pread(48, “F901\\0\\0\\0 (06\\0\\0 (06\\0″.., 73, 114975) = 73
/7: pread(48, “F901\\0\\0\\0 )06\\0\\0 )06\\0″.., 73, 115048) = 73
/7: pread(48, “F901\\0\\0\\0 *06\\0\\0 *06\\0″.., 73, 115121) = 73
/7: pread(48, “F901\\0\\0\\0 +06\\0\\0 +06\\0″.., 73, 115194) = 73
/7: pread(48, “F901\\0\\0\\0 ,06\\0\\0 ,06\\0″.., 73, 115267) = 73
/7: pread(48, “F901\\0\\0\\0 -06\\0\\0 -06\\0″.., 73, 115340) = 73
/7: pread(48, “F901\\0\\0\\0 .06\\0\\0 .06\\0″.., 73, 115413) = 73
/7: pread(48, “F901\\0\\0\\0 /06\\0\\0 /06\\0″.., 73, 115486) = 73
/7: pread(48, “F901\\0\\0\\0 006\\0\\0 006\\0″.., 73, 115559) = 73
/7: pread(48, “F901\\0\\0\\0 106\\0\\0 106\\0″.., 73, 115632) = 73
/7: pread(48, “F901\\0\\0\\0 206\\0\\0 206\\0″.., 73, 115705) = 73
/7: pread(48, “F901\\0\\0\\0 306\\0\\0 306\\0″.., 73, 115778) = 73
/7: pread(48, “F901\\0\\0\\0 406\\0\\0 406\\0″.., 73, 115851) = 73
/7: pread(48, “F901\\0\\0\\0 506\\0\\0 506\\0″.., 73, 115924) = 73
/7: pread(48, “F901\\0\\0\\0 606\\0\\0 606\\0″.., 73, 115997) = 73
/7: pread(48, “F901\\0\\0\\0 706\\0\\0 706\\0″.., 73, 116070) = 73
/7: pread(48, “F901\\0\\0\\0 806\\0\\0 806\\0″.., 73, 116143) = 73
/7: pread(48, “F901\\0\\0\\0 906\\0\\0 906\\0″.., 73, 116216) = 73
/7: pread(48, “F901\\0\\0\\0 :06\\0\\0 :06\\0″.., 73, 116289) = 73
/7: pread(48, “F901\\0\\0\\0 ;06\\0\\0 ;06\\0″.., 73, 116362) = 73
/7: pread(48, “F901\\0\\0\\0 <06\\0\\0 <06\\0″.., 73, 116435) = 73
/7: pread(48, “F901\\0\\0\\0 =06\\0\\0 =06\\0″.., 73, 116508) = 73
/7: pread(48, “F901\\0\\0\\0 >06\\0\\0 >06\\0″.., 73, 116581) = 73
/7: write(45, “01\\0\\00104 $\\0\\00203 d e”.., 183) = 183
/7: time() = 1252743991
/7: port_associate(3, 4, 0×0000002D, 0×00000001, 0xFFFFFD7FFDF10150) = 0
/7: port_get(3, 0xFFFFFD7FFD5BBF60, 0×00000000) (sleeping…)
正好98个pread(48,..,73,..),每次读一行(73bytes). pfiles可以看到文件48正好是 test.MYD文件.
mysql> select count(*) from test where a=1 and b>1500 ;
+―――-+
| count(*) |
+―――-+
| 98 |
+―――-+
1 row in set (0.00 sec)
下面是对explain的简单解释。
let’s break this down column by column.
* table - This is the name of the table. This will become important when you have large joins, as each table will get a row.
* type - The type of the join. Here’s what the MySQL documentation has to say about the ref type: “All rows with matching index values will be read from this table for each combination of rows from the previous tables. ref is used if the join uses only a leftmost prefix of the key, or if the key is not UNIQUE or a PRIMARY KEY (in other words, if the join cannot select a single row based on the key value). If the key that is used matches only a few rows, this join type is good.” In this case, since our index isn’t UNIQUE, this is the best join type we can get. In summary, if the join type is listed as “ALL” and you aren’t trying to select most of the rows in the table, then MySQL is doing a full table scan which is usually very bad. You can fix this by adding more indexes. If you want more information, the MySQL manual covers this value with much more depth.
* possible_keys - The name of the indexes that could possibly be used. This is where nicknaming your index helps. If you leave the name field blank, the name defaults to the name of the first column in the index (in this case, it would be “firstname”), which isn’t very descriptive.
* key - This shows the name of the index that MySQL actually uses. If this is empty (or NULL), then MySQL isn’t using an index.
* key_len - The length, in bytes, of the parts of the index being used. In this case, it’s 102 because firstname takes 50 bytes, lastname takes 50, and age takes 2. If MySQL were only using the firstname part of the index, this would be 50.
* ref - This shows the name of the columns (or the word “const”) that MySQL will use to select the rows.
Here, MySQL references three constants to find the rows.
* rows - The number of rows MySQL thinks it has to go through before knowing it has the correct rows. Obviously, one is the best you can get.
* Extra - There are many different options here, most of which will have an adverse effect on the query.
In this case, MySQL is simply reminding us that it used the WHERE clause to limit the results.
建议继续学习:
- 由浅入深探究mysql索引结构原理、性能分析与优化 (阅读:15123)
- 浅谈MySQL索引背后的数据结构及算法 (阅读:9961)
- 由浅入深理解索引的实现(2) (阅读:6457)
- HBase二级索引与Join (阅读:5846)
- 如何建立合适的索引? (阅读:5457)
- InnODB和MyISAM索引统计集合 (阅读:5295)
- Innodb 表和索引结构 (阅读:4861)
- mysql查询中利用索引的机制 (阅读:4818)
- MySQL索引背后的数据结构及算法原理 (阅读:4485)
- mysql索引浅析 (阅读:4136)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:yumianfeilong 来源: 玉面飞龙的BLOG
- 标签: 索引
- 发布时间:2009-10-17 14:25:40
- [69] Twitter/微博客的学习摘要
- [67] IOS安全–浅谈关于IOS加固的几种方法
- [65] 如何拿下简短的域名
- [65] android 开发入门
- [63] find命令的一点注意事项
- [62] Go Reflect 性能
- [61] 流程管理与用户研究
- [60] Oracle MTS模式下 进程地址与会话信
- [59] 图书馆的世界纪录
- [57] 读书笔记-壹百度:百度十年千倍的29条法则