AAAAOrder2020-0000001之面试题
摘要
- 本文内容基于10.4.8-MariaDB
Q: 假设一个订单的编号规则是AAAAOrder2020-0000001,AAAAOrder2020-0000002…后面的数字是自增长,如果订单号码达到AAAAOrder2020-1000000(100万),数据库中应该有100万条数据,此时我随机删除2条数据(物理删除,且不考虑日志和备份),请问怎么找到删掉的数据的编号?给出解题思路即可,答案需要在1秒内运行得到。
思路
-
其实查找丢失数据方法有很多,不过这里重点强调1秒内运行得到,就限制成了只能在数据库层面完成,实现方法基本上就是sql或者存储过程,而且要尽量减少表的扫描次数和扫描范围、尽可能的使用索引。
-
订单编号的序号是自增长,所以可以认为数据是按照编号自增的顺序插入数据表的,如果数据表存在单独的自增主键Id,则可以基于错位法得到缺失的主键Id,这样可以使用主键索引,而且主键自增索引的查询效率是最高的。
-
如果没有独立的主键Id,而是使用的orderId作为主键,则需要对orderId截断后进行比较,查询条件一定要使用完整的orderId,以便使用索引,也可以使用行号和截断后的订单编号进行比较的方法。具体实现见下文。
参考答案,作者水平有限,仅供学习交流
-
存在自增主键Id,与orderId序号一致,可以通过错位法计算不存在的Id值。
运行时间基本符合要求。
1 | select id-1 as id_del from test_order where id not in (select id+1 from test_order); |
-
如果不存在自增主键ID,而是使用orderId作为主键,则也可以通过错位法计算不存在的Id值,主要比较时一定要用完整的orderId,否则不能使用索引。
运行时间超过要求。
1 | SELECT RIGHT(orderId, 7) - 1 AS id_del FROM test_order |
-
如果不存在自增主键ID,而是使用orderId作为主键,也可以比较orderId的序号与行号,获取到不一致的第一条记录,然后在从该记录开始继续比较获得下一条不一致的记录,也可以通过存储过程实现。
运行时间符合要求。
sql示例1
1 | -- 查询第一条丢失记录 |
sql示例2
1 | -- 可以直接粘贴到mysql终端执行,也可以保存到文件,然后在mysql终端执行source /xx/xx/xx.sql |
1 | source /Users/hanqf/Desktop/exec.sql |
存储过程示例
1 | DROP PROCEDURE IF EXISTS find_delete_order_id; |
1 | CALL `find_delete_order_id`(@del_ids); |
表结构
1 | DROP TABLE IF EXISTS `test_order`; |
数据初始化
说明:
-
直接将100w的数据插入数据表有点慢,实测130多秒吧,这里可以先将数据初始化到内存表中,然后再从内存表导入即可,总时间大概只需要12秒左右。
-
因为数据都是写入内存中,所以写入数据时可能会报内存不足,解决方法如下:
a.永久修改,在my.cnf中增加如下配置:
tmp_table_size=1G
max_heap_table_size=1Gb.临时修改,mysql终端执行:
set tmp_table_size = 1073741824;
set max_heap_table_size = 1073741824;
show variables like “%table_size%”;
1 | -- 创建内存表,重启数据库后,内存表中的数据会被清空,但是表结构依旧存在,不需要时可以drop掉 |
1 | -- 创建1000000条数据 |