执行ALTER TABLE语句时如何避免长时间阻塞并发查询

简介: 执行ALTER TABLE语句时如何避免长时间阻塞并发查询

最近看到这样的案例:

1、应用需要为现有的表添加列

2、应用执行ALTER TABLE ADD COLUMN语句

3、其他每个查询都需要被阻塞几分钟甚至更长时间

为什么出现这种情况?如果避免?

首先看下执行ALTER TABLE ADD COLUMN时发生了什么?

    # ALTER TABLE test ADD COLUMN whatever int4;
    ALTER TABLE
    TIME: 12.662 ms

    可以看到该语句执行的非常快,在看下alter table获取的锁:


    =# BEGIN;
    BEGIN
    =# ALTER TABLE test ADD COLUMN whatever2 int4;
    ALTER TABLE
    =# SELECT * FROM pg_locks WHERE pid = pg_backend_pid();
       locktype    | DATABASE | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction |  pid  |        mode         | GRANTED | fastpath 
    ---------------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-------+---------------------+---------+----------
     relation      |   165725 |    12143 |      |       |            |               |         |       |          | 3/2594             | 32470 | AccessShareLock     | t       | t
     virtualxid    |          |          |      |       | 3/2594     |               |         |       |          | 3/2594             | 32470 | ExclusiveLock       | t       | t
     transactionid |          |          |      |       |            |          1422 |         |       |          | 3/2594             | 32470 | ExclusiveLock       | t       | f
     relation      |   165725 |   166142 |      |       |            |               |         |       |          | 3/2594             | 32470 | AccessExclusiveLock | t       | f
    (4 ROWS)
    =# ROLLBACK;
    ROLLBACK

    需要注意,有一个AccessExclusiveLock,直到事务提交或者回滚才能释放,此间会锁表。

    但是alter table add column只花费12ms,哪来的几分钟?这种事情发生在有其他查询在这个表上,然后在执行alter tablealter table需要等待之前的锁释放:

      (SESSION 1) =# BEGIN;
      BEGIN
      (SESSION 1) =# SELECT COUNT(*) FROM test;
       COUNT 
      -------
           0
      (1 ROW)

      会话1不关闭,同时不用关闭事务。


      (SESSION 2) =# ALTER TABLE test ADD COLUMN whatever2 int4;

      会话2执行alter 语句时由于需要等待会话1释放锁被阻塞,但是他已经获取这个表上的AccessExclusiveLock了,其他select不能执行了。


      (SESSION 3) =# depesz=# SELECT * FROM test LIMIT 1;

      会话3再执行selecthang住。

      那么,是否存在这样的语句,执行添加列时不申请长时间锁表的锁?pg_reorg/pg_repack

      首先设置事务超时时间,然后执行alter table语句:


      =$ time printf "SET statement_timeout = 50;\nALTER TABLE test add column whatever2 INT4;\n" | psql -qX
      ERROR:  canceling statement due to statement timeout
      real    0m0.054s
      user    0m0.000s
      sys     0m0.002s
      =$ echo $?
      0

      超时时间保证alter table语句执行不超过50毫秒,然后通过psql执行:

        =$ time printf "SET statement_timeout = 50;\nALTER TABLE test add column whatever2 INT4;\n" | psql -qX -v ON_ERROR_STOP=1
        ERROR:  canceling statement due to statement timeout
        real    0m0.054s
        user    0m0.002s
        sys     0m0.000s
        =$ echo $?
        3

        语句执行很快失败,但是返回结果是0,标记成功了,我们需要修改下:

          =$ time printf "SET statement_timeout = 50;\nALTER TABLE test add column whatever2 INT4;\n" | psql -qX -v ON_ERROR_STOP=1ERROR:  canceling statement due to statement timeout real    0m0.054suser    0m0.002ssys     0m0.000s =$ echo $?3

          这样就合理了:


          =$ printf "SET statement_timeout = 50;\nALTER TABLE test add column whatever2 INT4;\n" > alter.sql

          然后:

            =$ while true; do date; psql -qX -v ON_ERROR_STOP=1 -f alter.sql && break; sleep 1; done
            Thu 26 Sep 2019 03:43:52 PM CEST
            psql:alter.sql:2: ERROR:  canceling statement due to statement timeout
            Thu 26 Sep 2019 03:43:53 PM CEST
            psql:alter.sql:2: ERROR:  canceling statement due to statement timeout
            Thu 26 Sep 2019 03:43:54 PM CEST
            psql:alter.sql:2: ERROR:  canceling statement due to statement timeout
            Thu 26 Sep 2019 03:43:55 PM CEST
            psql:alter.sql:2: ERROR:  canceling statement due to statement timeout
            Thu 26 Sep 2019 03:43:56 PM CEST
            =$

            While循环:


            while true
            do
                date
                psql -qX -v ON_ERROR_STOP=1 -f alter.sql && break
                sleep 1
            done

            可以看到开始时间和结束时间。

            需要注意,如果想对这个表进行更多操作,需要使用事务:


            BEGIN;
                SET statement_timeout = 50;
                LOCK TABLE ONLY test IN ACCESS EXCLUSIVE MODE;
                SET statement_timeout = 0;
                ALTER TABLE test ....;
                -- do whatever you want, timeout is removed.
            commit;


            但是需要注意,一旦获取了这个锁,其他链接就不能使用这个表了。


            原文:

            https://www.depesz.com/2019/09/26/how-to-run-short-alter-table-without-long-locking-concurrent-queries/

            目录
            相关文章
            |
            6月前
            |
            SQL
            幂等修改表增加一列的sql语句
            这个 SQL 查询用于修改名为 `t1` 的表,通过添加一个名为 `b` 的新列,并指定数据类型。`IF NOT EXISTS` 子句确保只有在表中不存在该列的情况下才会添加。 在你的查询中,`<数据类型>` 应该被替换为你想要的具体数据类型(例如,INT,VARCHAR等)。以下是使用 INT 数据类型的示例: ```sql ALTER TABLE t1 ADD COLUMN IF NOT EXISTS b INT; ``` 请根据你的需求选择合适的数据类型。这个查询的作用是在表 `t1` 中添加一个名为 `b` 的新列,如果该列尚不存在的话。
            122 0
            |
            3月前
            |
            SQL 数据处理 数据库
            DELETE 和 TRUNCATE 语句之间的详细区别
            【8月更文挑战第31天】
            472 0
            |
            SQL 关系型数据库 MySQL
            当并发insert on duplicate key update遇见死锁:更新丢失
            数据库死锁问题,是一个老生常谈且很常见的问题,网上也有非常多对于各类死锁场景的解析和复现,但凡和死锁有关,无外乎不涉及数据库隔离等级、索引、以及innodb锁等相关原因。但是我这个案例我搜遍了全网也没能找到比较相似情况。于是我想尽可能的复现出这种情况,找出死锁的原因,找出可能出现的隐患。问题的背景:我们的数据库中的发生死锁的表是具有”多列组合构建的唯一索引“(不包含
            19271 4
            |
            6月前
            |
            监控 关系型数据库 MySQL
            MySQL 并发insert 唯一键冲突导致的死锁
            一 前言死锁其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。二 背景知识2.1 insert 锁机制在分析死锁案例之前,我们先学习一下背景知识 insert 语句的加锁策略。我们先来看...
            983 0
            |
            6月前
            |
            关系型数据库 MySQL 数据库管理
            MySQL 三个并发insert 语句导致的死锁
            一 前言死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。本文介绍一例三个并发insert 导致的死锁,根本原因还是在于insert 唯一键申请插入意向锁这个特殊的GAP锁。其实...
            995 0
            |
            6月前
            |
            SQL 关系型数据库 MySQL
            MySQL 并发更新唯一键和插入数据导致死锁
            一 前言死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。二 案例分析2.1 业务场景业务开发同学想同步数据,他们的逻辑是通过update 更新操作,如果更新记录返回的affec...
            436 0
            |
            6月前
            |
            SQL 关系型数据库 MySQL
            MySQL insert 遇到delete 唯一键未提交导致死锁
            一 前言死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助二案例分析2.1 业务场景用户录入商品,应用程序会提前检查是否存在相同记录,如果有则先删除再插入;如果没有则直接插入。2....
            294 0
            |
            SQL 关系型数据库 MySQL
            |
            索引 关系型数据库 存储
            InnoDB,select为啥会阻塞insert?
            MySQL的InnoDB的细粒度行锁,是它最吸引人的特性之一。
            720 0
            |
            SQL 关系型数据库 MySQL
            Innodb中常见SQL语句设置的锁类型
            1、select … from 除了serializable隔离级别,这种SQL都是一致性非锁定读,不加锁;在serializable级别,这种SQL加next-key锁。 2、select … from … lock in share mode 这种SQL加S类型的next-key锁。
            1559 0