这次的查询需要扫描12个page,除去1个根节点外,还需要扫描12个叶子节点,只是为了返回一条数据而已,这代价有点大。
如果把SQL微调改成下面这样
[root@yejr.run]> select id,c1 from t_sk where c1 is null limit 10000,1; +-------+------+ | id | c1 | +-------+------+ | 99671 | NULL | +-------+------+
可以看到还是需要扫描12个page。
InnoDB_pages_distinct: 12
...select id,c1 from t_sk where c1 is null limit 10000,1;
SQL3, 查询 c1 任意非NULL值
如果把 c1列条件改成正常的int值,结果就不太一样了
[root@yejr.run]> select id, c1 from t_sk where c1 = 907299016;
+--------+-----------+
| id | c1 |
+--------+-----------+
| 365115 | 907299016 |
+--------+-----------+
1 row in set (0.00 sec)
slow log是这样的
InnoDB_pages_distinct: 2
...
select id, c1 from t_sk where c1 = 907299016;
可以看到,只需要扫描2个page,这个看起来就正常了。
结论1,存储大量的NULL值,除了计算更复杂之外,数据扫描的代价也会更高一些
另外,如果要查询的c1值正好介于两个page的临界位置,那么需要多读取一个page。
扫描第31号page,确认该数据页中的最小和最大物理记录
[root@yejr.run]# innodb_space -s ibdata1 -T test/t_sk -p 31 page-dump
...
records:
{:format=>:compact,
:offset=>126,
:header=>
{:next=>9996,
:type=>:conventional,
:heap_number=>2,
:n_owned=>0,
:min_rec=>false,
:deleted=>false,
:nulls=>[],
:lengths=>{},
:externs=>[],
:length=>6},
:next=>9996,
:type=>:secondary,
:key=>[{:name=>"c1", :type=>"INT UNSIGNED", :value=>1531865685}],
:row=>[{:name=>"id", :type=>"INT UNSIGNED", :value=>1507}],
:sys=>[],
:length=>8}
...
{:format=>:compact,
:offset=>5810,
:header=>
{:next=>112,
:type=>:conventional,
:heap_number=>408,
:n_owned=>0,
:min_rec=>false,
:deleted=>false,
:nulls=>[],
:lengths=>{},
:externs=>[],
:length=>6},
:next=>112,
:type=>:secondary,
:key=>[{:name=>"c1", :type=>"INT UNSIGNED", :value=>1536700825}],
:row=>[{:name=>"id", :type=>"INT UNSIGNED", :value=>361382}],
:sys=>[],
:length=>8}
指定c1的值为 1531865685、1536700825 执行查询,查看slow log,确认都需要扫描3个page,而如果换成介于这两个值之间的数据,则只需要扫描2个page。
InnoDB_pages_distinct: 3
...
select id, c1 from t_sk where c1 = 1531865685;
InnoDB_pages_distinct: 3
...
select id, c1 from t_sk where c1 = 1536700825;
InnoDB_pages_distinct: 2
...
select id, c1 from t_sk where c1 = 1536630003;
InnoDB_pages_distinct: 2
...
select id, c1 from t_sk where c1 = 1536575377;
这是因为辅助索引是非唯一的,即便是在等值查询时,也需要再读取下一条记录,以确认已获取所有符合条件的数据。
还有,当利用辅助索引读取数据时,如果要读取整行数据,则需要回表。
也就是说,除了扫描辅助索引数据页之外,还需要扫描聚集索引数据页。
来个例子看看就知道了。
#无需回表时
InnoDB_pages_distinct: 2
...
select id, c1 from tnull where c1 = 1536630003;
#需要回表时
InnoDB_pages_distinct: 5
...
select * from t_sk where c1 = 1536630003;
需要回表时,除了扫描辅助索引页2个page外,还需要回表扫描聚集索引页,而聚集索引是个3层树,因此总共需要扫描5个page。
</div>