MySQL知识点整理

1.MySQL自增id用完之后,再插入数据会发生什么事情?

自增ID的容量是有限制的,比如int unsigned,那么最大值是 2^32-1=4294967295,大概就是42亿,当id达到最大值的时候,再插入数据,此时生成的ID还是最大值2^32-1,此时插入会报主键冲突错误。

可以考虑使用 bigint unsigned 类型

2.MySQL如果没有指定主键,那么会发生什么事情?

如果是这种情况,InnoDB会自动帮你创建一个不可见的、长度为6字节的row_id,而且InnoDB 维护了一个全局的 dictsys.row_id,所以未定义主键的表都共享该row_id,每次插入一条数据,都把全局row_id当成主键id,然后全局row_id加1

该全局row_id在代码实现上使用的是bigint unsigned类型,但实际上只给row_id留了6字节,这种设计就会存在一个问题:如果全局row_id一直涨,一直涨,直到2的48幂次-1时,这个时候再+1,row_id的低48位都为0,结果在插入新一行数据时,拿到的row_id就为0,存在主键冲突的可能性。

3.我们还是应该自己定义每一张表的自增id

因为如果共用row_id的话,row_id是会从0~2^48-1...0~2^48-1之间重复(因为row_id程序定义的是64位,但是使用到id里边的只有低位的48位,所以在超过2^48-1之后低48位就都变成0了,也就是从0开始了新的id自增),这样基于row_id这种方式重复产生的重复id,出数据插入的时候跟表自带的自增id有所区别:

在 InnoDB 逻辑里,申请到 row_id=N 后,就将这行数据写入表中;如果表中已经存在 row_id=N 的行,新写入的行就会覆盖原有的行。

从这个角度看,我们还是应该在 InnoDB 表中主动创建自增主键。因为,

表自增 id 到达上限后,再插入数据时报主键冲突错误,是更能被接受的。毕竟覆盖数据,就意味着数据丢失,影响的是数据可靠性;报主键冲突,是插入失败,影响的是可用性。而一般情况下,可靠性优先于可用性。

4.MYSQL各种自增id的应用场景

4.1 表的自增 id 达到上限后,再申请时它的值就不会改变,进而导致继续插入数据时报主键冲突的错误。

4.2 row_id 达到上限后,则会归 0 再重新递增,如果出现相同的 row_id,后写的数据会覆盖之前的数据。

4.3 Xid 只需要不在同一个 binlog 文件中出现重复值即可。虽然理论上会出现重复值,但是概率极小,可以忽略不计。(内存中的值,重启后从0开始,重启后重新启动一个binlog文件,所以一个binlog文件中不会有重复的Xid)

4.4 InnoDB 的 max_trx_id 递增值每次 MySQL 重启都会被保存起来,所以我们文章中提到的脏读的例子就是一个必现的 bug,好在留给我们的时间还很充裕。

4.5 thread_id 是我们使用中最常见的,而且也是处理得最好的一个自增 id 逻辑了。

5.mysql 分页到后边页数很大的时候,很慢

select * from t_record where age > 10 offset 10000 limit 10

例如上边这个例子,age有索引,但是执行起来就很慢

想下mysql如何执行offset 10000的?

要知道,索引是B+树,mysql在查找第N个数据,也就是offset N的步骤如下:

1.也就是要先在索引树中找到第一个满足 age>10的数据

2.然后跳过其中的10000个,才找到满足条件的第N个元素(这里N就是10000)

3. 找出第N个元素之后的10 个元素

这里有一点需要注意的是,在mysql中,有一点不符合常理的存在,

步骤2中要跳过N个的数据,是通过B+树叶子节点组成的链表进行遍历,理论上时间复杂度只有O(N),

不过不幸的是,这里的N个数据不仅仅只是在age这颗B+索引树上执行,N中的每一条数据都需要回表进行查询(回表指的是从age索引找到主键id索引之后,再根据id索引去找到记录),这就涉及到了N个回表操作,就是N个随机IO读盘操作,非常慢!!!

奇怪的是,这些个要跳过的数据明明是不需要回表就可以,因为age索引上的字段可以覆盖我们的where查询条件,据说是跟mysql的优化器有关,目前mysql还没有优化(应该是8.0版本之前都还没有优化)。

想到覆盖索引,那么我们可以有如下优化:

select * from t_record id in

(select id from t_record where age > 10 offset 10000 limit 10

子查询里边只查询了id,这样age索引可以覆盖,也就是子查询里边就绕过了N条记录回表

参考上边的执行步骤,在子查询中还是会使用age索引树的叶子组成的链表进行遍历,时间复杂度只有O(N),而且这些二级索引一般来说都是在内存中的,没有读盘操作。因为没有回表所以没有随机IO读盘。

你可能感兴趣的:(mysql,mysql)