思考及测试
基本设定
数据库需要使用自定义的自增值。
用GUID浪费资源,大家测试一下取10000条记录时,返回GUID与使用数据库10000的自增值比较就懂了,这个场景多出现在导入Excel或其他情况,使用自增值时,可以一次性生成一个数值段,避免了重复生成。
使用MySQL自增字段会导致在数据库中最后插入数值的ID大于规划的ID时,无法从规划的ID进行自增,这个对分布式多数据库影响很大。
解决计划
使用MySQL提供锁,如下所示:
select * from auto_id for update;
首先,让我们建一个表
CREATE TABLE `auto_id` (
`idname` VARCHAR(30) NOT NULL DEFAULT '',
`id` BIGINT(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`idname`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;;
插入一条记录
insert into auto_id (idname, id) values('abc', 0);
测试自增锁
打开两个工具分别运行:
select * from auto_id for update;
update auto_id set id = id + 1 where idname = 'abc';
select * from auto_id;
这个测试的两个客户端都自增了,并不能说明自增是正确的,这是为什么呢?原因很简单,这是因为:MySQL的默认级别是:REPEATABLE-READ,意思是说只要Update就会马上提交,因此无法测试我们的结果,因此我们应该把测试脚本修改为:
start transaction;
select * from auto_id for update;
update auto_id set id = id + 1 where idname = 'abc';
select * from auto_id;
原理很简单,先对其进行锁处理,然后再进行Update,在select中使用for update,这时会发现测试窗口A中的记录执行成功了,测试窗口B的脚本,运行到 select * from auto_id for update; 就运行不下去了,这说明锁起了作用,当并发产生时,未能进入锁操作的事务被挂起了。这时,只需要在窗口A中运行commit,窗口B的脚本就可以运行下去了。
结论
使用事务与for update语句来处理MySQL的并发问题是可行的
最终解决脚本
存储过程代码
CREATE PROCEDURE `p_sys_get_increment_id`(
IN `idname_in` VARCHAR(20),
IN `count_in` BIGINT,
OUT `id_out` BIGINT
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT '取自增ID'
BEGIN
declare oldid bigint;
start transaction;
select id into oldid from auto_id where idname=idname_in for update;
if oldid is NULL then
insert into auto_id(idname, id) value(idname_in, count_in + 1);
set id_out = 1;
else
update auto_id set id = id + count_in where idname = idname_in;
set id_out = oldid;
end if;
commit;
END
测试
call p_sys_get_increment_id('abc', 10000, @key);
select @key;
结果非常快,达到之前设定的目标