Clustered Index: 把索引和数据存储在一起。说白了,就是B-树的叶子结点上直接挂了数据。
它对性能的好处是:
1. 从存储的角度来看,索引即表。索引找到了,数据也就找到了,少做一次i/o
2. 如果索引值相邻,记录也相邻;获取一堆索引值相邻的数据时,由于存储地点相邻没有分散,I/O性能会比较高。比如说,获取 id between (3,10)的数据,可能只需要读取一个page.
Innodb中总是有一个Clustered Index; 如果你定义了primary key, 那么primary key就是Clustered Index; 如果没定义,那innodb也会跟你生成一个隐式的primary key, 并使它作为Clustered Index.
在Innodb中,
secondary index没有指针直接指向数据,它只会记录primary index的值。
1. 坏处:基于secondary index的查找实际上是两次查找:先按secondary index值找到primary index的值;再按primary index的值找到记录。
2. 好处:由于secondary index没有指针直接指向数据,但数据的存储位置变化时(值不变),secondary index不需要更新。
Innodb使用Primary Index作为Clustered Index隐含了一个结论:应该先插入主键值小的记录,再插入主键值大的记录。否则,会导致插入过程中的page split和最终存储时的data fragmentation,影响插入性能和查询性能。
例如,假设1条记录占半个page; 把id=1的记录插入到page1的上半部分后,再插入id=3的记录,很自然地,系统会把它安排在page1的下半部分,以保证相邻数据放在一起;最后要插入id=2的记录时,你不能把它安排在page2,因为id=2和id=1的记录必须相邻,不能让id=3挡在中间,这时系统就会把page1拆成两个page, 以把id=2插入到id=1和id=3之间; 这三条记录也可能各占一个page, 并且有些空白的page可能间隔其中(取决于磁盘上数据移动的具体策略)。
如果严格按照id=1,id=2,id=3的顺序来,就不会有这些问题,因为数据总是插在当前最后一个存储位置的后面,不需要拆散谁。
所以说,
主键最好是auto incremented并且不可修改的,这样就可以确保数据移动不会发生;如果做不到这一点,也应该时不时地运行一下optimize table,使得已经有所分散的数据能够重新紧凑地聚合在一起。
至于具体的性能区别,High Performance MySQL做了一下benchmark,1个用自增主键,另1个用UUID,结论是插入性能相差四倍。当然,主键数据类型及长度差异也是性能差别的原因之一,但主要因素还是非顺序插入导致的数据移动。