首页 > 数据库 >[MySQL]为什么主键最好是有序递增的

[MySQL]为什么主键最好是有序递增的

时间:2024-09-10 13:46:33浏览次数:10  
标签:索引 递增 节点 插入 InnoDB MySQL 数据 主键

为什么主键索引最好是有序递增的

我们在建表的时候,都会默认将主键索引设置为自增的,具体为什么要这样做呢?又什么好处?
InnoDB 创建主键索引默认为聚簇索引,数据被存放在了 B+Tree 的叶子节点上。也就是说,同一个叶子节点内的各个数据是按主键顺序存放的,因此,每当有一条新的数据插入时,数据库会根据主键将其插入到对应的叶子节点中。
如果我们使用自增主键,那么每次插入的新数据就会按顺序添加到当前索引节点的位置,不需要移动已有的数据,当页面写满,就会自动开辟一个新页面。因为每次插入一条新记录,都是追加操作,不需要重新移动数据,因此这种插入数据的方法效率非常高。
如果我们使用非自增主键,由于每次插入主键的索引值都是随机的,因此每次插入新的数据时,就可能会插入到现有数据页中间的某个位置,这将不得不移动其它数据来满足新数据的插入,甚至需要从一个页面复制数据到另外一个页面,我们通常将这种情况称为页分裂。页分裂还有可能会造成大量的内存碎片,导致索引结构不紧凑,从而影响查询效率。

举个例子,假设某个数据页中的数据是1、3、5、9,且数据页满了,现在准备插入一个数据7,则需要把数据页分割为两个数据页:

image

出现页分裂时,需要将一个页的记录移动到另外一个页,性能会受到影响,同时页空间的利用率下降,造成存储空间的浪费。
而如果记录是顺序插入的,例如插入数据11,则只需开辟新的数据页,也就不会发生页分裂:

image

因此,在使用 InnoDB 存储引擎时,如果没有特别的业务需求,建议使用自增字段作为主键。
另外,主键字段的长度不要太大,因为主键字段长度越小,意味着二级索引的叶子节点越小(二级索引的叶子节点存放的数据是主键值),这样二级索引占用的空间也就越小。

a、所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;

b、不可能在非叶子结点命中;

c、非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层。

1、如果我们定义了主键(PRIMARY KEY)

那么InnoDB会选择主键作为聚集索引、如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引、如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入而主键递增,这个ROWID不像ORACLE的ROWID那样可引用,是隐含的)。

2、数据记录本身被存于主索引(一颗B+Tree)的叶子节点上

这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)

3、如果表使用自增主键

那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页

4、如果使用非自增主键(如果身份证号或学号等)

由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置,此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。

总结:如果InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的,也就是下面这几种情况的存取效率最高:

a、使用自增列(INT/BIGINT类型)做主键,这时候写入顺序是自增的,和B+数叶子节点分裂顺序一致;

b、该表不指定自增列做主键,同时也没有可以被选为主键的唯一索引(上面的条件),这时候InnoDB会选择内置的ROWID作为主键,写入顺序和ROWID增长顺序一致;

c、如果一个InnoDB表又没有显示主键,又有可以被选择为主键的唯一索引,但该唯一索引可能不是递增关系时(例如字符串、UUID、多字段联合唯一索引的情况),该表的存取效率就会比较差。

标签:索引,递增,节点,插入,InnoDB,MySQL,数据,主键
From: https://www.cnblogs.com/DCFV/p/18405544

相关文章

  • 高版本mysql访问出现Client does not support authentication protocol requested by
    访问8.0等高版本数据库报错:Clientdoesnotsupportauthenticationprotocolrequestedbyserver;considerupgradingMySQLclient(客户端不支持服务器请求的身份验证协议;请考虑升级MySQL客户端)这种问题就是你访问的工具身份验证协议过于落后,如果是navicat之类的软件可以考虑......
  • ETL数据集成丨MySQL到MySQL的数据迁移实践
    前言MySQL数据迁移至另一MySQL数据库的过程,不仅是数据复制或移动的操作那么简单,它还涉及到一系列策略性考量和技术优化,旨在实现数据的高效、安全传输,以及确保目标系统的高性能运行。其深远意义在于为企业的数字化转型提供强有力的数据支撑,确保业务连续性与竞争力。ETLCloud作为......
  • 20240910_104851 mysql 存储过程 2006班
    修改结束符号delimiter新符号创建一个存储过程要求:查询所有的老师信息只显示id与nameDELIMITER$CREATEPROCEDUREshow1()BEGIN SELECTid,NAMEFROMteacher;END$使用存储过程CALLshow1();查看存储过程的创建语句查看名为p1的存储过程的名称showcreatep......
  • 使用公钥通过ssh通道连接MYSQL数据库报错(server sent: publickey)
    使用公钥通过ssh连接MYSQL数据库报错Disconnected:Nosupportedauthenticationmethodsavailable(serversent:publickey)1、通过命令行查看服务器ssh配置文件sudovim/etc/ssh/sshd_config2、查看PubkeyAuthentication是否启用,PubkeyAuthenticationyes,表示服务......
  • 基于java ssm vue mysql大学校医院信息管理系统毕业设计项目实战分享
    前言......
  • 2000万的行数还是 MySQL 表的限制吗?
    传闻网络上一直流传着一种观点,认为在单个MySQL表中,数据的行数一旦超过2000万,表的性能就可能受到影响。这种观点主要源于早些时候使用HDD硬盘存储时的经验。2024年了,当我们使用基于SSD的MySQL数据库时,这种判断是否依然有效。换句话说,基于现代存储技术,MySQL表的行数是否仍然需要限......
  • MySQL timestamp和datetime用法详解
    一、MySQL中如何表示当前时间?其实,表达方式还是蛮多的,汇总如下:CURRENT_TIMESTAMPCURRENT_TIMESTAMP()NOW()LOCALTIMELOCALTIME()LOCALTIMESTAMPLOCALTIMESTAMP() 二、关于TIMESTAMP和DATETIME的比较一个完整的日期格式如下:YYYY-MM-DDHH:MM:SS[.fraction],它可分为两部......
  • MySQL 8.0修改密码
    最近系统升级牵涉到MySQL升级,需要升级到MySQL8.0,涉及MySQL用户的密码修改,特地记录一下!MySQL8.0前修改密码在MySQL8.0前,执行:SETPASSWORD=PASSWORD('[新密码]')进行密码修改,在MySQL8.0后,以上的方法使用root用户修改别的用户密码是报错的,因为MySQL8.0后修改了修改密码的方......
  • 【开源dcluster】Seatunnel数据同步之MySQL同步到doris
    源码Gitee地址:https://gitee.com/zhenglv123456/dcluster在线文档:https://47.121.127.33:8090/在线体验:http://36.155.14.171:12345/dolphinscheduler/ui/login账号密码:test/test123 创建同步任务操作步骤:1.点击创建任务 2.配置同步脚本 3.设置同步时间......
  • mysql 调优
    一、缓冲池​​​​​14.5.1BufferPool缓冲池是主内存中的一个区域,InnoDB在访问表和索引数据时将其缓存。缓冲池允许直接从内存访问经常使用的数据,从而加快处理速度。在专用服务器上,高达80%的物理内存通常分配给缓冲池。为了提高大容量读取操作的效率,缓冲池被划分为可能容纳多......