python - POSTGRES - 具有多个连接的高效 SELECT 或 INSERT

标签 python sql database postgresql concurrency

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/

相关文章:

python - 根据多行将 Dataframe 拆分为多个部分

python - 从 MultiIndex 中选择特定级别

sql - 加入SQLite获取所需的记录

php - BadMethodCallException 方法 orderBy 不存在

php - 如何从 Symfony 中的数据库/实体中删除表?

python - 尝试用Python中的正则表达式替换\t为\s,但结果出现 "Unhashable type:list"错误

python - 使用 keras 神经网络逼近具有多维输出的函数

mysql - mysql中的计算和多重连接

MySQL 按字符串聚合

database - CodeIgniter 中的 Doctrine ORM - 优点和缺点?