InnoDB索引允许NULL对性能有影响吗(1)

简介: InnoDB索引允许NULL对性能有影响吗

阅读目录

0. 初始化测试表、数据1. 问题1:索引列允许为NULL,对性能影响有多少 结论1,存储大量的NULL值,除了计算更复杂之外,数据扫描的代价也会更高一些2. 问题2:辅助索引需要MVCC多版本读的时候,为什么需要依赖聚集索引 结论2,辅助索引中不存储DB_TRX_ID,需要依托聚集索引实现MVCC3. 问题3:为什么查找数据时,一定要读取叶子节点,只读非叶子节点不行吗 结论3,在索引树中查找数据时,最终一定是要读取叶子节点才行4. 问题4:索引列允许为NULL,会额外存储更多字节吗 结论4,定义列值允许为NULL并不会增加物理存储代价,但对索引效率的影响要另外考虑5. 几点总结6. 延伸阅读

本文开始之前,有几篇文章建议先复习一下

接下来,我们一起测试验证关于辅助索引的几个特点。

0. 初始化测试表、数据

测试表结构如下:

[root@yejr.run]> CREATE TABLE `t_sk` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `c1` int(10) unsigned NOT NULL,
  `c2` int(10) unsigned NOT NULL,
  `c3` int(10) unsigned NOT NULL,
  `c4` int(10) unsigned NOT NULL,
  `c5` datetime NOT NULL,
  `c6` char(20) NOT NULL,
  `c7` varchar(30) NOT NULL,
  `c8` varchar(30) NOT NULL,
  `c9` varchar(30) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `k1` (`c1`)
) ENGINE=InnoDB;

除了主键索引外,还有个 c1 列上的辅助索引。

mysql_random_data_load 灌入50万测试数据。

1. 问题1:索引列允许为NULL,对性能影响有多少



把辅助索引列 c1 修改为允许NULL,并且随机更新5万条数据,将 c1 列设置为NULL

[root@yejr.run]> alter table t_sk modify c1 int unsigned;


[root@yejr.run]> update t_sk set c1 = NULL order by rand() limit 50000;
Query OK, 50000 rows affected (2.83 sec)
Rows matched: 50000 Changed: 50000 Warnings: 0

#随机1/10为null
[root@yejr.run]> select count(*) from t_sk where c1 is null;
+----------+
| count(*) |
+----------+
| 50000 |
+----------+



好,现在观察辅助索引的索引数据页结构。

[root@yejr.run]# innblock test/t_sk.ibd scan 16
...
Datafile Total Size:100663296
===INDEX_ID:46 --聚集索引(主键索引)
level2 total block is (1) --根节点,层高2(共3层),共1个page
block_no: 3,level: 2|*|
level1 total block is (5) --中间节点,层高1,共5个page
block_no: 261,level: 1||block_no: 262,level: 1||block_no: 263,level: 1|*|
block_no: 264,level: 1||block_no: 265,level: 1||
level0 total block is (5020) --叶子节点,层高0,共5020个page
block_no: 5,level: 0||block_no: 6,level: 0||block_no: 7,level: 0|*|
...
===INDEX_ID:47 --辅助索引
level1 total block is (1) --根节点,层高1(共2层),共1个page
block_no: 4,level: 1|*|
level0 total block is (509) --叶子节点,层高0,共509个page
block_no: 18,level: 0||block_no: 19,level: 0||block_no: 31,level: 0|*|
...



观察辅助索引的根节点里的数据

[root@yejr.run]# innodb_space -s ibdata1 -T test/t_sk -p 4 page-dump
...
records:
{:format=>:compact,
:offset=>126, --第一条记录
:header=>
{:next=>428,
:type=>:node_pointer,
:heap_number=>2,
:n_owned=>0,
:min_rec=>true, --min_rec表示最小记录
:deleted=>false,
:nulls=>["c1"],
:lengths=>{},
:externs=>[],
:length=>6},
:next=>428,
:type=>:secondary,
:key=>[{:name=>"c1", :type=>"INT UNSIGNED", :value=>:NULL}], --对应c1列值为NULL
:row=>[{:name=>"id", :type=>"INT UNSIGNED", :value=>9}], --对应id=9
:sys=>[],
:child_page_number=>18, --指向叶子节点 pageno = 18
:length=>8}
...
{:format=>:compact,
:offset=>6246, --最后一条记录(next=>112,指向supremum)
:header=>
{:next=>112,
:type=>:node_pointer,
:heap_number=>346,
:n_owned=>0,
:min_rec=>false,
:deleted=>false,
:nulls=>[],
:lengths=>{},
:externs=>[],
:length=>6},
:next=>112,
:type=>:secondary,
:key=>[{:name=>"c1", :type=>"INT UNSIGNED", :value=>2142714688}], --对应c1=2142714688
:row=>[{:name=>"id", :type=>"INT UNSIGNED", :value=>73652}], --对应id=73652
:sys=>[],
:child_page_number=>2935, --指向叶子节点2935
:length=>12}



经过统计,根节点中c1列值为NULL的记录共有33条,其余476条是c1列值为非NULL,共509条记录。

叶子节点中,每个page大约可以存储1547条记录,共有5万条记录值为NULL,因此需要至少33个page来保存(ceiling(50000/1547) = 33)。

看下这个SQL的查询计划

[root@yejr.run]> desc select count(*) from t_sk where c1 is null\G
1. row **
id: 1
select_type: SIMPLE
table: t_sk
partitions: NULL
type: ref
possible_keys: k1
key: k1
key_len: 5
ref: const
rows: 99112
filtered: 100.00
Extra: Using where; Using index



从上面的输出中,我们能看到,当索引列设置允许为NULL时,是会对其纳入索引统计信息,并且值为NULL的记录,都是存储在索引树的最左边。

接下来,跑几个SQL查询。



SQL1,统计所有NULL值数量

[root@yejr.run]> select count(*) from t_sk where c1 is null;
+----------+
| count(*) |
+----------+
| 50000 |
+----------+



查看slow log

InnoDB_pages_distinct: 34
...
select count(*) from t_sk where c1 is null;

共需要扫描34个page,根节点(1)+叶子节点(33),正好34个page。

备注:需要用Percona版本才能在slow query log中有InnoDB_pages_distinct信息。



SQL2, 查询 c1 is null

[root@yejr.run]> select id,c1 from t_sk where c1 is null limit 1;
+------+------+
| id | c1 |
+------+------+
| 9607 | NULL |
+------+------+

查看slow log

InnoDB_pages_distinct: 12
...
select id,c1 from t_sk where c1 is null limit 1;




            </div>
相关文章
|
6月前
|
存储 算法 关系型数据库
深入理解InnoDB索引数据结构和算法
1. **索引定义**:索引是提升查询速度的有序数据结构,帮助数据库系统快速找到数据。 2. **索引类型**:包括普通索引、唯一索引、主键索引、空间索引和全文索引,每种有特定应用场景。 3. **数据结构**:InnoDB使用B+树作为索引结构,确保所有节点按顺序排列,降低查询时的磁盘I/O。 4. **B+树特性**:所有数据都在叶子节点,非叶子节点仅存储索引,提供高效范围查询。 5. **索引优势**:通过减少查找数据所需的磁盘I/O次数,显著提高查询性能。 **总结:**InnoDB索引通过B+树结构,优化了数据访问,使得查询速度快,尤其适合大数据量的场景。
361 0
深入理解InnoDB索引数据结构和算法
|
6月前
|
存储 关系型数据库 MySQL
索引大战:探秘InnoDB数据库中B树和Hash索引的优劣
索引大战:探秘InnoDB数据库中B树和Hash索引的优劣
64 0
|
6月前
|
存储 SQL 关系型数据库
系统设计场景题—MySQL使用InnoDB,通过二级索引查第K大的数,时间复杂度是多少?
系统设计场景题—MySQL使用InnoDB,通过二级索引查第K大的数,时间复杂度是多少?
82 1
系统设计场景题—MySQL使用InnoDB,通过二级索引查第K大的数,时间复杂度是多少?
|
6月前
|
存储 算法 关系型数据库
InnoDb行格式、数据页结构、索引底层原理和如何建立索引
InnoDb行格式、数据页结构、索引底层原理和如何建立索引
117 0
|
存储 关系型数据库 MySQL
6.2.2 【MySQL】InnoDB中的索引方案
6.2.2 【MySQL】InnoDB中的索引方案
82 0
|
存储 关系型数据库 MySQL
6.2.3 【MySQL】InnoDB的B+树索引的注意事项
6.2.3 【MySQL】InnoDB的B+树索引的注意事项
82 0
|
3月前
|
SQL 存储 关系型数据库
"MySQL增列必锁表?揭秘InnoDB在线DDL,让你的数据库操作飞一般,性能无忧!"
【8月更文挑战第11天】在数据库领域,MySQL凭借其稳定高效的表现深受开发者喜爱。对于是否会在给数据表添加列时锁表的问题,MySQL的行为受版本、存储引擎等因素影响。从5.6版起,InnoDB支持在线DDL,可在改动表结构时保持表的可访问性,避免长时间锁表。而MyISAM等则需锁表完成操作。例如,在使用InnoDB的表上运行`ALTER TABLE users ADD COLUMN email VARCHAR(255);`时,通常不会完全锁表。虽然在线DDL提高了灵活性,但复杂操作或大表变更仍可能暂时影响性能。因此,进行结构变更前应评估其影响并择机执行。
72 6
|
6月前
|
存储 监控 关系型数据库
如何优化InnoDB的整体性能?
【5月更文挑战第14天】如何优化InnoDB的整体性能?
155 2
|
5月前
|
存储 关系型数据库 MySQL
【MySQL技术内幕】5.1-InnoDB存储引擎索引概述
【MySQL技术内幕】5.1-InnoDB存储引擎索引概述
57 0
|
6月前
|
SQL 存储 关系型数据库
【深入浅出MySQL】「底层原理」InnoDB索引原理全程实操指南,带你从入门到精通
【深入浅出MySQL】「底层原理」InnoDB索引原理全程实操指南,带你从入门到精通
215 1