我有一张这样的表
mysql> describe obj;
+----------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| data | blob | YES | | NULL | |
| sequence | int(11) | YES | UNI | NULL | |
| created | datetime | YES | | NULL | |
+----------+----------+------+-----+---------+----------------+
我希望字段sequence
中的值是连续的序列,即没有跳过和跳跃,并且基于created
时间戳。目前我在应用程序级别获得此功能。每次插入后:
while (true)
lastSequence = max(sequence)
if (update obj with lastSequence + 1)
break
这种方式无法跳过,但无法保证sequence
订单与created
订单相同。另一方面,我可以通过created
在子查询中进行排序,但是在一个cuncurrent环境中,如果我没有序列化所有写入,子查询执行时可能会发生奇怪的事情,但我认为这会扼杀性能。关于这个问题的任何想法?
答案 0 :(得分:2)
没有灵丹妙药。
自动增加ID号无效,因为如果事务回滚,您将获得间隙。
交易外的应用程序代码不起作用,因为它无法保证“序列”的顺序与“已创建”的顺序相匹配。
在单个可序列化的事务中完成所有工作将起作用,但肯定会影响性能。它是否如此影响性能是不可用的是一个悬而未决的问题;测试和测量。
这样的事情(PostgreSQL)应该可行,但可能不够快。 (按照下面的时间用一粒盐。没有并发用户,小桌子。)
set transaction isolation level serializable;
begin;
insert into obj (sequence, time_created)
select min(n), current_timestamp
from serial_integers where time_stamp is null;
-- 0.125ms
update serial_integers
set time_stamp = current_timestamp
where n = (select min(n)
from serial_integers where time_stamp is null);
-- 0.161ms
commit;
-- 15ms
实际上,我测试将它包装在一个函数(存储过程)中,部分是为了消除UPDATE语句中的子查询。
答案 1 :(得分:1)
在MySQL中,您使用auto_increment就像在ID字段中使用一样。
在oracle中你会使用音序器。
这个问题已存在多年。问题是当你确定MAX(值)时你必须锁定东西,因为你不能确定另一个进程没有插入另一个记录。
如果您删除记录,会发生 差距。如果要使用日期/时间值,则必须降低到亚毫秒级别,因为数据库非常快。 Oracle使用systimestamp,它至少将秒数带到6个位置。