postgresl SERIAL是否保证单个插入语句中没有间隙?

时间:2018-03-09 20:41:44

标签: postgresql auto-increment

让我们从:

开始
CREATE TABLE "houses" (
  "id" serial NOT NULL PRIMARY KEY, 
  "name" character varying NOT NULL)

想象一下,我尝试同时(!)在一个语句中插入多个记录(可能10个可能是1000个)。

INSERT INTO houses (name) VALUES
    ('B6717'),
    ('HG120');

当单个线程在单个语句中插入X记录时(同时其他线程同时尝试将其他记录插入到同一个表中),这些记录将具有从A到A + X编号的ID,是否可以保证-1?或者是否有可能A + 100将被线程1和A + 99线程2占用?

2 个答案:

答案 0 :(得分:1)

使用两个PgAdmin连接一次插入10000条记录似乎足以证明串行类型不能保证PostgreSQL 9.5上批处理的连续性

DO
$do$
BEGIN 
FOR i IN 1..200 LOOP
  EXECUTE format('INSERT INTO houses (name) VALUES %s%s;', repeat('(''a' || i || '''),', 9999), '(''a' || i || ''')');  
END LOOP;
END
$do$;

以上结果导致属于两个不同批次的ID之间频繁重叠

SELECT * FROM houses WHERE id BETWEEN 34370435 AND 34370535 ORDER BY id;

34370435;"b29"
34370436;"b29"
34370437;"b29"
34370438;"a100"
34370439;"b29"
34370440;"b29"
34370441;"a100"
...

答案 1 :(得分:1)

我认为这将更难以证明,但事实证明它无法保证。

我使用ruby脚本让4个线程同时插入数千个记录,并检查单个语句创建的记录是否有间隙,并且确实存在。

  Thread.new do
    100.times do |u|
      House.import(1000.times.map do |i|
        {
          tenant: "#{t}-#{u}",
          name: i,
        }
      end)
    end
  end
end.each(&:join)

House.distinct.pluck(:tenant).all? do |t|
  recs = House.where(
    tenant: t,
  ).order('id').to_a
  recs.first.id - recs.first.name.to_i == recs.last.id - recs.last.name.to_i
end

差距的例子:

[#<House:0x00007fd2341b5e00
  id: 177002,
  tenant: "0-43",
  name: "0",>,
 #<House:0x00007fd2341b5c48
  id: 177007,
  tenant: "0-43",
  name: "1">,
  ...

正如您所看到的,在同一个INSERT语句中插入的第一行和第二行之间的GAP为5。