PostgreSQL的VALIDATE CONSTRAINT检查已经检查过的行吗?

时间:2014-04-23 16:22:12

标签: postgresql foreign-keys

PostgreSQL's documentation表明,对于随后更新或插入的行,仍会检查添加为NOT VALID的约束。

我有一张很高的桌子" churn"。也就是说,我知道在两周内,大多数当前存在的未经检查的行都将被删除。并且会存在许多新行(已经过检查)。

所以我的问题是:如果我延迟运行VALIDATE CONSTRAINT直到2周后,PostgreSQL是否仍然需要检查从现在到现在之间插入的所有行?或者它是否足够智能以了解哪些行已经被检查过,因此在运行VALIDATE CONSTRAINT时减少了排他锁所需的时间?

1 个答案:

答案 0 :(得分:0)

我通过测试找到了答案。我用Ruby和Active Record编写了以下脚本:

require "active_record"
require "logger"

ActiveRecord::Base.establish_connection(adapter: "postgresql", database: "test")
ActiveRecord::Base.logger = Logger.new($stderr)

ActiveRecord::Schema.define do
  create_table :befores, force: true do |t|
    t.integer :other_id
  end

  create_table :afters, force: true do |t|
    t.integer :other_id
  end

  create_table :others, force: true
end

conn = ActiveRecord::Base.connection

conn.execute(
  "ALTER TABLE befores " \
  "ADD CONSTRAINT befores_others_fk " \
  "FOREIGN KEY (other_id) " \
  "REFERENCES others (id) " \
  "NOT VALID"
)

ActiveRecord::Base.logger = nil

N = (ENV["N"] || 1_000_000).to_i

print "Inserting #{N} rows..."
N.times do |i|
  conn.execute("INSERT INTO others (id) VALUES (#{i})")
  conn.execute("INSERT INTO befores (id, other_id) VALUES (#{i}, #{i})")
  conn.execute("INSERT INTO afters (id, other_id) VALUES (#{i}, #{i})")
end
puts "done"

ActiveRecord::Base.logger = Logger.new($stderr)

conn.execute(
  "ALTER TABLE afters " \
  "ADD CONSTRAINT afters_others_fk " \
  "FOREIGN KEY (other_id) " \
  "REFERENCES others (id) " \
  "NOT VALID"
)

conn.execute "ALTER TABLE befores VALIDATE CONSTRAINT befores_others_fk"
conn.execute "ALTER TABLE afters VALIDATE CONSTRAINT afters_others_fk"

然后我用100k行运行它:

$ N=100000 ruby test.rb 
-- create_table(:befores, {:force=>true})
D, [2014-04-23T23:00:41.925325 #6411] DEBUG -- :    (6.3ms)  DROP TABLE "befores"
D, [2014-04-23T23:00:41.935557 #6411] DEBUG -- :    (9.6ms)  CREATE TABLE "befores" ("id" serial primary key, "other_id" integer) 
   -> 0.0322s
-- create_table(:afters, {:force=>true})
D, [2014-04-23T23:00:41.940595 #6411] DEBUG -- :    (3.3ms)  DROP TABLE "afters"
D, [2014-04-23T23:00:41.948406 #6411] DEBUG -- :    (7.5ms)  CREATE TABLE "afters" ("id" serial primary key, "other_id" integer) 
   -> 0.0127s
-- create_table(:others, {:force=>true})
D, [2014-04-23T23:00:41.952883 #6411] DEBUG -- :    (3.0ms)  DROP TABLE "others"
D, [2014-04-23T23:00:41.960750 #6411] DEBUG -- :    (7.5ms)  CREATE TABLE "others" ("id" serial primary key) 
   -> 0.0122s
D, [2014-04-23T23:00:41.963809 #6411] DEBUG -- :    (2.7ms)  ALTER TABLE befores ADD CONSTRAINT befores_others_fk FOREIGN KEY (other_id) REFERENCES others (id) NOT VALID
Inserting 100000 rows...done
D, [2014-04-23T23:11:37.105878 #6411] DEBUG -- :    (3.3ms)  ALTER TABLE afters ADD CONSTRAINT afters_others_fk FOREIGN KEY (other_id) REFERENCES others (id) NOT VALID
D, [2014-04-23T23:11:37.196819 #6411] DEBUG -- :    (90.7ms)  ALTER TABLE befores VALIDATE CONSTRAINT befores_others_fk
D, [2014-04-23T23:11:37.287966 #6411] DEBUG -- :    (91.0ms)  ALTER TABLE afters VALIDATE CONSTRAINT afters_others_fk

由于最后两个语句之间没有真正的区别,我的结论是在运行VALIDATE CONSTRAINT时确实会重新检查所有行。用于测试的postgres版本是9.2.6。