执行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/

            目录
            打赏
            0
            0
            0
            0
            353
            分享
            相关文章
            |
            10月前
            |
            SQL
            幂等修改表增加一列的sql语句
            这个 SQL 查询用于修改名为 `t1` 的表,通过添加一个名为 `b` 的新列,并指定数据类型。`IF NOT EXISTS` 子句确保只有在表中不存在该列的情况下才会添加。 在你的查询中,`<数据类型>` 应该被替换为你想要的具体数据类型(例如,INT,VARCHAR等)。以下是使用 INT 数据类型的示例: ```sql ALTER TABLE t1 ADD COLUMN IF NOT EXISTS b INT; ``` 请根据你的需求选择合适的数据类型。这个查询的作用是在表 `t1` 中添加一个名为 `b` 的新列,如果该列尚不存在的话。
            158 0
            TRUNCATE语句到底因何而慢?
            综上所述,尽管 `TRUNCATE` 通常被视为快速的数据删除方法,但在处理特定的数据库配置、大型数据集、复杂的外键关系等方面,可能会意外地缓慢。因此,优化数据库性能和理解 `TRUNCATE` 在特定情况下的行为,对数据库管理员和开发人员来说是重要的。在进行此类操作之前,建议先测试并理解它们在您的特定环境中的表现,以便制定最有效的数据管理策略。
            400 1
            |
            10月前
            |
            SQL 语句不要过多的 join
            SQL 语句不要过多的 join
            39 1
            SQL语句命中索引,但还是执行很慢
            MySQL的慢查询日志是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值(默认值10s)的SQL,则会被记录到慢查询日志中。
            367 0
            MySQL 并发delete不存在记录申请gap锁导致死锁
            一 前言死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。本文源于我们的生产案例:并发申请gap锁导致的死锁案例,与之前的 死锁案例一不同,本案例是因为RR模式下两个事务中的sql可以获取同一个...
            260 0
            MySQL 并发insert 唯一键冲突导致的死锁
            一 前言死锁其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。二 背景知识2.1 insert 锁机制在分析死锁案例之前,我们先学习一下背景知识 insert 语句的加锁策略。我们先来看...
            1252 0
            为什么 SQL 语句不要过多的 join?
            送分题 面试官:有操作过Linux吗? 我:有的呀 面试官:我想查看内存的使用情况该用什么命令 我:free 或者 top 面试官:那你说一下用free命令都可以看到啥信息 我:那,如下图所示 可以看到内存以及缓存的使用情况 total 总内存 used 已用内存 free 空闲内存 buff/cache 已使用的缓存 avaiable 可用内存
            187 0
            为什么 SQL 语句不要过多的 join?
            SQL优化之一则MySQL中的DELETE、UPDATE 子查询的锁机制失效案例
            MySQL 下的 InnoDB 行锁,是通过以位图方式对 index page 加锁机制来实现的。
            15044 0