跳至主要內容
自增id用完怎么办

MySQL 里有很多自增的 id,每个自增 id 都是定义了初始值,然后不停地往上加步长。虽然自然数是没有上限的,但是在计算机里,只要定义了表示这个数的字节长度,那它就有上限。比如,无符号整型 (unsigned int) 是 4 个字节,上限就是 232-1。

既然自增 id 有上限,就有可能被用完。但是,自增 id 用完了会怎么样呢?

今天这篇文章,我们就来看看 MySQL 里面的几种自增 id,一起分析一下它们的值达到上限以后,会出现什么情况。

表定义自增值id

说到自增 id,你第一个想到的应该就是表结构定义里的自增字段,也就是我在第 39 篇文章《自增主键为什么不是连续的?》中和你介绍过的自增主键 id。


Znyoung大约 13 分钟Java数据库
问题答疑总结

join的写法

在第 35 篇文章《join 语句怎么优化?》中,我在介绍 join 执行顺序的时候,用的都是 straight_join。同学在文后提出了两个问题:

  1. 如果用 left join 的话,左边的表一定是驱动表吗?
  2. 如果两个表的 join 包含多个条件的等值匹配,是都要写到 on 里面呢,还是只把一个条件写到 on 里面,其他条件写到 where 部分?

为了同时回答这两个问题,我来构造两个表 a 和 b:

create table a(f1 int, f2 int, index(f1))engine=innodb;
create table b(f1 int, f2 int)engine=innodb;
insert into a values(1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
insert into b values(3,3),(4,4),(5,5),(6,6),(7,7),(8,8);

Znyoung大约 10 分钟Java数据库
要不要使用分区表

我经常被问到这样一个问题:分区表有什么问题,为什么公司规范不让使用分区表呢?今天,我们就来聊聊分区表的使用行为,然后再一起回答这个问题。

分区表是什么?

为了说明分区表的组织形式,我先创建一个表 t:

CREATE TABLE `t` (
                     `ftime` datetime NOT NULL,
                     `c` int(11) DEFAULT NULL,
                     KEY (`ftime`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1
PARTITION BY RANGE (YEAR(ftime))
(PARTITION p_2017 VALUES LESS THAN (2017) ENGINE = InnoDB,
 PARTITION p_2018 VALUES LESS THAN (2018) ENGINE = InnoDB,
 PARTITION p_2019 VALUES LESS THAN (2019) ENGINE = InnoDB,
PARTITION p_others VALUES LESS THAN MAXVALUE ENGINE = InnoDB);
insert into t values('2017-4-1',1),('2018-4-1',1);

Znyoung大约 11 分钟Java数据库
grant之后要跟着flushPrivileges吗

在 MySQL 里面,grant 语句是用来给用户赋权的。不知道你有没有见过一些操作文档里面提到,grant 之后要马上跟着执行一个 flush privileges 命令,才能使赋权语句生效。我最开始使用 MySQL 的时候,就是照着一个操作文档的说明按照这个顺序操作的。

那么,grant 之后真的需要执行 flush privileges 吗?如果没有执行这个 flush 命令的话,赋权语句真的不能生效吗?

接下来,我就先和你介绍一下 grant 语句和 flush privileges 语句分别做了什么事情,然后再一起来分析这个问题。

为了便于说明,我先创建一个用户:


Znyoung大约 9 分钟Java数据库
怎么最快地复制一张表

我在上一篇文章最后,给你留下的问题是怎么在两张表中拷贝数据。如果可以控制对源表的扫描行数和加锁范围很小的话,我们简单地使用 insert … select 语句即可实现。

当然,为了避免对源表加读锁,更稳妥的方案是先将数据写到外部文本文件,然后再写回目标表。这时,有两种常用的方法。接下来的内容,我会和你详细展开一下这两种方法。

为了便于说明,我还是先创建一个表 db1.t,并插入 1000 行数据,同时创建一个相同结构的表 db2.t。

create database db1;
use db1;

create table t(id int primary key, a int, b int, index(a))engine=innodb;
delimiter ;;
  create procedure idata()
  begin
    declare i int;
set i=1;
while(i<=1000)do
insert into t values(i,i,i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();

create database db2;
create table db2.t like db1.t;

Znyoung大约 11 分钟Java数据库
insert语句的锁为什么这么多

在上一篇文章中,我提到 MySQL 对自增主键锁做了优化,尽量在申请到自增 id 以后,就释放自增锁。

因此,insert 语句是一个很轻量的操作。不过,这个结论对于“普通的 insert 语句”才有效。也就是说,还有些 insert 语句是属于“特殊情况”的,在执行过程中需要给其他资源加锁,或者无法在申请到自增 id 以后就立马释放自增锁。

那么,今天这篇文章,我们就一起来聊聊这个话题。

insert...select语句

我们先从昨天的问题说起吧。表 t 和 t2 的表结构、初始化数据语句如下,今天的例子我们还是针对这两个表展开。


Znyoung大约 10 分钟Java数据库
自增主键为什么不是连续的

在第 4 篇文章中,我们提到过自增主键,由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧凑。

之前我见过有的业务设计依赖于自增主键的连续性,也就是说,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不能保证连续递增。

今天这篇文章,我们就来说说这个问题,看看什么情况下自增主键会出现 “空洞”?

为了便于说明,我们创建一个表 t,其中 id 是自增主键字段、c 是唯一索引。

CREATE TABLE `t` (
                     `id` int(11) NOT NULL AUTO_INCREMENT,
                     `c` int(11) DEFAULT NULL,
                     `d` int(11) DEFAULT NULL,
                     PRIMARY KEY (`id`),
                     UNIQUE KEY `c` (`c`)
) ENGINE=InnoDB;

Znyoung大约 14 分钟Java数据库
Memory引擎

我在上一篇文章末尾留给你的问题是:两个 group by 语句都用了 order by null,为什么使用内存临时表得到的语句结果里,0 这个值在最后一行;而使用磁盘临时表得到的结果里,0 这个值在第一行?

内存表的数据组织结构

为了便于分析,我来把这个问题简化一下,假设有以下的两张表 t1 和 t2,其中表 t1 使用 Memory 引擎, 表 t2 使用 InnoDB 引擎。

create table t1(id int primary key, c int) engine=Memory;
create table t2(id int primary key, c int) engine=innodb;
insert into t1 values(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);
insert into t2 values(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);

Znyoung大约 12 分钟Java数据库
什么时候会使用内部临时表

在第 16和第 34篇文章中,我分别和你介绍了 sort buffer、内存临时表和 join buffer。这三个数据结构都是用来存放语句执行过程中的中间数据,以辅助 SQL 语句的执行的。其中,我们在排序的时候用到了 sort buffer,在使用 join 语句的时候用到了 join buffer。

然后,你可能会有这样的疑问,MySQL 什么时候会使用内部临时表呢?

今天这篇文章,我就先给你举两个需要用到内部临时表的例子,来看看内部临时表是怎么工作的。然后,我们再来分析,什么情况下会使用内部临时表。

union执行流程

为了便于量化分析,我用下面的表 t1 来举例。


Znyoung大约 11 分钟Java数据库
为什么临时表可以重名?

在上一篇文章中,我们在优化 join 查询的时候使用到了临时表。当时,我们是这么用的:

create temporary table temp_t like t1;
alter table temp_t add index(b);
insert into temp_t select * from t2 where b>=1 and b<=2000;
select * from t1 join temp_t on (t1.b=temp_t.b);

Znyoung大约 13 分钟Java数据库
2
3
4
5
...
8