AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 223598
Accepted
GollyJer
GollyJer
Asked: 2018-11-28 16:18:46 +0800 CST2018-11-28 16:18:46 +0800 CST 2018-11-28 16:18:46 +0800 CST

如何防止多对多插入/更新触发功能中的死锁?

  • 772

我在多对多插入时遇到了死锁问题,并且在这一点上远远超出了我的范围。

我有一个tweet每秒接收数千条记录的表。其中一列是 PostgreSQLarray[]::text[]类型,在数组中具有零对多的 url。它看起来像{www.blah.com, www.blah2.com}。

我试图从表上的触发器中完成的是在tweet表中创建一个条目,urls_starting然后在tweet_x_url_starting.

旁注:该url_starting表链接到url_ending完全解析的 url 路径所在的表。

我面临的问题是死锁,我不知道还能尝试什么。
我继续进行Erwin Brandstetter学习狂欢。(如果你在外面,伙计......谢谢!?)

  1. 如何在 PostgreSQL 中实现多对多关系?
  2. 尽管 ON CONFLICT DO NOTHING 导致多行 INSERT 死锁
  3. Postgres 更新……限制 1(skip locked help)

我尝试为确定性、稳定的订单和 FOR UPDATE SKIP LOCKED 添加 ORDER BY,但我不确定我是否正确地执行了任何操作。

这是结构。使用 PostgreSQL 10.5。

CREATE TABLE tweet(
    id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    twitter_id text NOT NULL,
    created_at timestamp NOT NULL,
    content text NOT NULL,
    urls text[],
    CONSTRAINT tweet_pk PRIMARY KEY (id)
);

CREATE TABLE url_starting(
    id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    url text NOT NULL,
    CONSTRAINT url_starting_pk PRIMARY KEY (id),
    CONSTRAINT url_starting_ak_1 UNIQUE (url)
);

CREATE TABLE tweet_x_url_starting(
    id_tweet integer NOT NULL,
    id_url_starting integer NOT NULL,
    CONSTRAINT tweet_x_url_starting_pk PRIMARY KEY (id_tweet,id_url_starting)

ALTER TABLE tweet_x_url_starting ADD CONSTRAINT tweet_fk FOREIGN KEY (id_tweet)
REFERENCES tweet (id) MATCH FULL
ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE tweet_x_url_starting ADD CONSTRAINT url_starting_fk FOREIGN KEY (id_url_starting)
REFERENCES url_starting (id) MATCH FULL
ON DELETE CASCADE ON UPDATE CASCADE;

这是tweet表触发器。

CREATE TRIGGER create_tweet_relationships
    AFTER INSERT OR UPDATE
    ON tweet
    FOR EACH ROW
    EXECUTE PROCEDURE create_tweet_relationships();

最后,功能。

CREATE FUNCTION create_tweet_relationships ()
    RETURNS trigger
    LANGUAGE plpgsql
    VOLATILE 
    CALLED ON NULL INPUT
    SECURITY INVOKER
    COST 1
    AS $$
BEGIN
    IF (NEW.urls IS NOT NULL) AND cardinality(NEW.urls::TEXT[]) > 0 THEN
        WITH tmp_url AS (
          INSERT INTO url_starting (url)
          SELECT UNNEST(NEW.urls)
            ORDER BY 1
          ON CONFLICT (url) DO UPDATE
            SET url = EXCLUDED.url
          RETURNING id
        )
        INSERT INTO tweet_x_url_starting (id_tweet, id_url_starting)
        SELECT NEW.id, id
            FROM tmp_url
            ORDER BY 1, 2
            FOR UPDATE SKIP LOCKED
        ON CONFLICT DO NOTHING;
    END IF;

    RETURN NULL;
END
$$;

我盲目地将我读到的东西扔到函数中,但没有成功。

错误看起来像这样。

deadlock detected
DETAIL:  Process 11281 waits for ShareLock on transaction 1317; blocked by process 11278.
Process 11278 waits for ShareLock on transaction 1316; blocked by process 11281.
HINT:  See server log for query details.
CONTEXT:  while inserting index tuple (494,33) in relation "url_starting"
SQL statement "WITH tmp_url AS (
          INSERT INTO url_starting (url)
          SELECT UNNEST(NEW.urls)
      ORDER BY 1
          ON CONFLICT (url) DO UPDATE
              SET url = EXCLUDED.url
          RETURNING id
        )
        INSERT INTO tweet_x_url_starting (id_tweet, id_url_starting)
        SELECT NEW.id, id
            FROM tmp_url
    ORDER BY 1, 2
        FOR UPDATE SKIP LOCKED
        ON CONFLICT DO NOTHING"
PL/pgSQL function create_tweet_relationships() line 12 at SQL statement

Error causing transaction rollback (deadlocks, serialization failures, etc).

我怎样才能停止死锁?谢谢!?

database-design postgresql
  • 2 2 个回答
  • 4394 Views

2 个回答

  • Voted
  1. Best Answer
    GollyJer
    2018-12-05T13:57:44+08:002018-12-05T13:57:44+08:00

    这最终归结为两件事。

    1. 如果插入的数据在插入前没有排序,并发写入最终会死锁。在我的触发器函数中,所有插入都已排序,但无法对同时添加的所有 url 进行排序。解决此问题的唯一方法是备份一个级别并使用整批推文进行插入/排序,从而一次访问所有 URL。

      更多在这里。?如何在 PostgreSQL 中使用 RETURNING 和 ON CONFLICT?

      这样做有很大的不同,但并没有完全解决问题。?

    2. 该ON CONFLICT子句可以防止重复键错误,但不能防止并发事务尝试输入相同的键。

      更多在这里。?尽管 ON CONFLICT DO NOTHING 导致多行 INSERT 死锁

      如我的问题中的错误消息所示,系统元组索引ctid在执行ON CONFLICT (column) DO UPDATE. 幸运的是,我不需要更新任何数据,因此不需要DO UPDATE我的部分查询。

      修复这个100% 停止了死锁!?


    这里 ? 是使用 python 发送的最终查询,带有 ) 中的execute_values()函数psycopg2。

    WITH cte_data (twitter_id, created_at, contents, search_hits, urls) AS (
        VALUES
        (NULL::text, NULL::timestamp, NULL::text, NULL::text[], NULL::text[]),
        %s
        OFFSET 1
    )
    , inserted_tweets AS (
        INSERT INTO tweet (twitter_id, created_at, contents, search_hits)
            SELECT twitter_id, created_at, contents, search_hits
            FROM cte_data
            ORDER BY 1
        ON CONFLICT DO NOTHING
        RETURNING id, twitter_id
        )
    , inserted_tweets_with_urls AS (
        SELECT id, urls
        FROM inserted_tweets
        JOIN cte_data USING (twitter_id)
    )
    , unique_urls AS (
        SELECT DISTINCT UNNEST(urls) url
        FROM cte_data
    )
    , new_urls AS (
        SELECT url
        FROM url_starting
        RIGHT JOIN unique_urls USING (url)
        WHERE id IS NULL
    )
    , inserted_urls AS (
        INSERT INTO url_starting (url)
            TABLE new_urls
            ORDER BY 1
        ON CONFLICT DO NOTHING
        RETURNING id, url
    )
    INSERT INTO tweet_x_url_starting (id_tweet, id_url_starting)
        SELECT it.id, iu.id
        FROM inserted_tweets_with_urls it
        JOIN inserted_urls iu
            ON (iu.url = ANY (it.urls))
        ORDER BY 1, 2
    ON CONFLICT DO NOTHING;
    
    • 3
  2. Erwin Brandstetter
    2018-11-28T20:28:09+08:002018-11-28T20:28:09+08:00

    这可能需要仔细检查,了解完整情况以及我现在无法花费的更多时间。或者,也许我遗漏了一些明显的东西。无论这里可能出现什么问题,有些事情很突出:

    • 完全删除FOR UPDATE SKIP LOCKED。在哪里使用它是没有意义的。当从 CTE 中选择时,它已经拥有排他锁的行,这是没有意义的。在查询的这个阶段跳过任何行也是没有意义的。

    • COST 1具有误导性。默认是COST 100,您的触发功能更多地在 COST 5000 的范围内。保留默认值或将其设置得更高。可能与死锁无关。

    • 与使用AFTER单个查询(具有多个数据修改 CTE)重写整个工作流相比,触发器可能更容易出现死锁。

    • 我在黑暗中开枪:死锁是由 FK 约束试图采取 ShareLock 造成的,url_starting而并发事务尝试在自己采取类似的 ShareLock 后修改同一行,反之亦然。tweet_x_url_starting如果您负担得起,一个快速而肮脏的解决方案可能是放弃 FK 约束。您至少可以尝试验证它是问题的一部分。

    如果你想继续你的学习狂潮 - 这里还有一个似乎相关的:

    • 如何在 PostgreSQL 中使用 RETURNING 和 ON CONFLICT?
    • 2

相关问题

  • 运行时间偏移延迟复制的最佳实践

  • 存储过程可以防止 SQL 注入吗?

  • 在数据仓库中实现多对多关系有哪些方法?

  • PostgreSQL 中 UniProt 的生物序列

  • PostgreSQL 9.0 Replication 和 Slony-I 有什么区别?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve