tl;dr 我正在尝试找出最有效的方法来选择记录或插入它(如果它不存在),这将适用于多个并发连接。
情况: 我正在构建一个 Postgres 数据库(9.3.5,x64),其中包含与客户相关的一大堆信息。该数据库具有一个“客户”表,其中包含一个“id”列(SERIAL PRIMARY KEY)和一个“system_id”列(VARCHAR(64))。 id 列用作其他表中的外键以链接到客户。如果“system_id”列不为空,则它必须是唯一的。
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
system_id VARCHAR(64),
name VARCHAR(256));
引用客户表中 id 的表示例:
CREATE TABLE tsrs (
id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id),
filename VARCHAR(256) NOT NULL,
name VARCHAR(256),
timestamp TIMESTAMP WITHOUT TIME ZONE);
我编写了一个 python 脚本,它使用多处理模块通过多个连接(来自不同进程)将数据推送到数据库中。
将数据插入数据库时,每个进程需要做的第一件事是检查具有特定 system_id 的客户是否在客户表中。如果是,则缓存关联的 customer.id。如果它不在表中,则添加一个新行,并缓存生成的 customer.id。我已经编写了一个 SQL 函数来为我执行此操作:
CREATE OR REPLACE FUNCTION get_or_insert_customer(p_system_id customers.system_id%TYPE, p_name customers.name%TYPE) RETURNS customers.id%TYPE AS $$
DECLARE
v_id customers.id%TYPE;
BEGIN
LOCK TABLE customers IN EXCLUSIVE MODE;
SELECT id INTO v_id FROM customers WHERE system_id=p_system_id;
IF v_id is NULL THEN
INSERT INTO customers(system_id, name)
VALUES(p_system_id,p_name)
RETURNING id INTO v_id;
END IF;
RETURN v_id;
END;
$$ LANGUAGE plpgsql;
问题: 表锁定是我能够防止并发进程将重复的 system_id 添加到表中的唯一方法。这并不是很理想,因为它有效地序列化了此时的所有处理,并且基本上使将给定数量的数据推送到数据库中所需的时间加倍。
我想问一下是否有更有效/更优雅的方式来实现“SELECT 或 INSERT”机制,而不会导致那么多的减速?我怀疑没有,但认为值得一问,以防万一。
非常感谢您阅读到这里。非常感谢任何建议!
最佳答案
我设法将函数重写为纯 SQL,更改了顺序(避免了 IF
和潜在的 竞争条件)
CREATE OR REPLACE FUNCTION get_or_insert_customer
( p_system_id customers.system_id%TYPE
, p_name customers.name%TYPE
) RETURNS customers.id%TYPE AS $func$
LOCK TABLE customers IN EXCLUSIVE MODE;
INSERT INTO customers(system_id, name)
SELECT p_system_id,p_name
WHERE NOT EXISTS (SELECT 1 FROM customers WHERE system_id = p_system_id)
;
SELECT id
FROM customers WHERE system_id = p_system_id
;
$func$ LANGUAGE sql;
关于python - POSTGRES - 具有多个连接的高效 SELECT 或 INSERT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27523493/