在不引入服务中断的情况下截断行并插入新行?

Truncate rows and insert new ones without introducing a service break?

我在 PostgreSQL 9.5 数据库中有大约 1,500,000 条记录 table,我正在获取一个 CSV 文件(通过 http post 请求),其中包含约 1,500,000 行新行,其中一些是不变,有些不同,有些与原来的行相比去掉了。

那我

  1. 截断旧的 table
  2. 遍历 CSV 文件的行
  3. 将每一行插入 table

我需要的是一种无需向我的客户引入服务中断的方法,即该服务应继续使用旧数据,直到完成所有三个步骤。目前,服务中断时间约为 1 小时,这是读取 CSV 并插入所有新行所需的时间。如果需要,我可以休息 5 分钟。

我怎样才能实现这种行为?

这是我的 Python 脚本的简化版本:

cursor = conn.cursor(cursor_factory=DictCursor)
cursor.execute('TRUNCATE TABLE rows CASCADE')
with open(request.files.csv) as csv_file:
    for line in csv_file:
        row = parse_line(line)
        cursor.execute(
            '''INSERT INTO rows (name, bla, blu)
            VALUES (%(name)s, %(bla)s, %(blu)s)''',
            row,
        )
cursor.commit()
  1. 使用 COPY 而不是 with open(request.files.csv),因为 1,500,000 行在几秒钟内从 CSV 复制到 table
  2. 如果那些秒数(假设一分钟)太长,仅使用事务将无济于事,导致 table 上的 truncate requires lock,而不是行

TRUNCATE acquires an ACCESS EXCLUSIVE lock on each table it operates on

因此,如果您可以重建 table 上的所有依赖对象,最好的可能是:

create t_table as select * from "rows" where false;
copy t_table from request.files.csv;
--build all needed dependant objects (indexes, constraints,triggers);
begin;
  alter table "rows" rename to "some_name";
  alter table "t_table " rename to "rows";
end;
--here is a miliseconds glitch to swith for users (if you use memcache or so - need to refresh it)
drop table "some_name";

更新 to copy columns from csv to several table columns list columns:

COPY table_name [ ( column_name [, ...] ) ]