PostgreSQL's documentation表明,对于随后更新或插入的行,仍会检查添加为NOT VALID
的约束。
我有一张很高的桌子" churn"。也就是说,我知道在两周内,大多数当前存在的未经检查的行都将被删除。并且会存在许多新行(已经过检查)。
所以我的问题是:如果我延迟运行VALIDATE CONSTRAINT
直到2周后,PostgreSQL是否仍然需要检查从现在到现在之间插入的所有行?或者它是否足够智能以了解哪些行已经被检查过,因此在运行VALIDATE CONSTRAINT
时减少了排他锁所需的时间?
答案 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。