【发布时间】:2016-04-21 10:36:24
【问题描述】:
我的想法是实现一个基本的“矢量时钟”,其中时间戳是基于时钟的,始终向前并保证是唯一的。
例如,在一个简单的表格中:
CREATE TABLE IF NOT EXISTS timestamps (
last_modified TIMESTAMP UNIQUE
);
我使用触发器在插入之前设置时间戳值。当两个插入同时到达时,它基本上只是进入未来:
CREATE OR REPLACE FUNCTION bump_timestamp()
RETURNS trigger AS $$
DECLARE
previous TIMESTAMP;
current TIMESTAMP;
BEGIN
previous := NULL;
SELECT last_modified INTO previous
FROM timestamps
ORDER BY last_modified DESC LIMIT 1;
current := clock_timestamp();
IF previous IS NOT NULL AND previous >= current THEN
current := previous + INTERVAL '1 milliseconds';
END IF;
NEW.last_modified := current;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS tgr_timestamps_last_modified ON timestamps;
CREATE TRIGGER tgr_timestamps_last_modified
BEFORE INSERT OR UPDATE ON timestamps
FOR EACH ROW EXECUTE PROCEDURE bump_timestamp();
然后我在两个单独的客户端中运行大量插入:
DO
$$
BEGIN
FOR i IN 1..100000 LOOP
INSERT INTO timestamps DEFAULT VALUES;
END LOOP;
END;
$$;
正如预期的那样,我遇到了碰撞:
ERROR: duplicate key value violates unique constraint "timestamps_last_modified_key"
État SQL :23505
Détail :Key (last_modified)=(2016-01-15 18:35:22.550367) already exists.
Contexte : SQL statement "INSERT INTO timestamps DEFAULT VALUES"
PL/pgSQL function inline_code_block line 4 at SQL statement
@rach suggested 将current_clock() 与SEQUENCE 对象混合,但这可能意味着摆脱TIMESTAMP 类型。即使我真的不知道它是如何解决隔离问题的......
有避免这种情况的通用模式吗?
感谢您的见解:)
【问题讨论】:
-
只有一个序列有什么问题?你真的需要时间吗?在 2 列(时间戳、序列)上使用一个键怎么样?否则你有 V1 UUID。
-
你为什么不直接使用
now()?INSERT INTO timestamps now();或将该字段的默认值设置为now()最后,您不能插入重复的 now() 值,因为它会随着每笔交易而变化。 -
只需在客户端捕获异常并重试。真的就是这么简单。
-
你用什么进行同步?为什么时间戳比序列更有用?
-
除了@jcaron 所说的,任何计算机上的时钟都会漂移,当计算机同步其时钟时,操作系统时间会跳跃。时钟可能会向任何方向漂移,因此可以从未来或过去获取时间戳,即无序。我认为,sequence 是保证唯一性的方法,如果需要,可以添加时间戳,但不要假设它是唯一的。
标签: postgresql race-condition vector-clock