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

            目录
            相关文章
            |
            前端开发
            File和MultipartFile互相转化工具类
            File和MultipartFile互相转化工具类
            2140 0
            |
            存储 缓存 算法
            数据库必知词汇:布隆过滤器(Bloom Filter)
            布隆过滤器(Bloom Filter)是由Burton Bloom 在1970年提出的,其后在P2P上得到了广泛的应用。一个空的布隆过滤器是一个m位的位数组,所有位的值都为0。定义了k个不同的符合均匀随机分布的哈希函数,每个函数把集合元素映射到位数组的m位中的某一位。Bloom filter算法可用来查询某一数据是否在某一数据集合中。其优点是查询效率高、可节省空间。但其缺点是会存在一定的错误。因此Bloom filter 算法仅仅能应用于那些同意有一定错误的场合。可使用Bloom filter 算法的场合包含字典软件、分布式缓存、P2P网络和资源路由等等。
            1500 0
            |
            12月前
            |
            负载均衡 Java Nacos
            Nacos服务注册与发现
            【10月更文挑战第11天】Nacos 是一个开源平台,用于服务发现和配置管理,提供服务注册、发现及动态配置等功能,适用于微服务架构。其核心功能包括服务注册、服务发现和动态配置管理,支持多种语言如 Java、Go、Python 等,具备高可用性和易用性。Nacos 可用于微服务治理、动态扩展和跨语言服务调用等场景,简化了服务间的交互和管理。
            467 10
            |
            Java 编译器 Spring
            面试突击78:@Autowired 和 @Resource 有什么区别?
            面试突击78:@Autowired 和 @Resource 有什么区别?
            15134 6
            |
            编解码 监控 Android开发
            视频码率、分辨率、帧率的关系
            视频码率、分辨率、帧率的关系
            3099 0
            |
            Rust 安全 JavaScript
            Rust 和 WebAssembly 搞大事啦!代码在浏览器中运行,这波操作简直逆天!
            【8月更文挑战第31天】《Rust 与 WebAssembly:将 Rust 代码运行在浏览器中》介绍了 Rust 和 WebAssembly 的强大结合。Rust 是一门安全高效的编程语言,而 WebAssembly 则是新兴的网页技术标准,两者结合使得 Rust 代码能在浏览器中运行,带来更高的性能和安全性。文章通过示例代码展示了如何将 Rust 函数编译为 WebAssembly 格式并在网页中调用,从而实现复杂高效的应用程序,同时确保了内存安全性和跨平台兼容性,为开发者提供了全新的可能性。
            484 0
            |
            设计模式 供应链 Go
            Go语言实现设计模式之责任链模式
            责任链模式是一种常用的设计模式,它将请求的发送者和接收者解耦,并允许多个对象都有机会处理请求。本文将详细介绍责任链模式的概念和原理,并使用Go语言实现一个示例,以帮助读者更好地理解该设计模式的应用。
            410 0
            |
            存储 算法 安全
            基于Guava布隆过滤器的海量字符串高效去重实践
            基于Guava布隆过滤器的海量字符串高效去重实践
            |
            Android开发
            Android Uri转File方法(适配android 10以上版本及android 10以下版本)
            Android Uri转File方法(适配android 10以上版本及android 10以下版本)
            1040 0
            |
            Linux 网络安全
            百度搜索:蓝易云【CentOS7查看开放端口命令、查看端口占用情况和开启端口命令、杀掉进程等命令教程。】
            请注意,在执行涉及系统配置和进程管理的命令时,确保你有足够的权限。同时,谨慎操作以避免对系统产生不可预见的影响。
            746 0