修改表結構DDL語句與鎖
阿新 • • 發佈:2019-01-05
--當pg開啟一個事務,執行dml操作時,如果另一個事務要修改表結構,其不得不等待
--session1 中執行dml
postgres=# begin;
BEGIN
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
4144
(1 row)
postgres=# insert into t values(2,'rudy2');
INSERT 0 1
postgres=#
--在session2 中執行ddl語句,發現其要等待
postgres=# begin;
BEGIN
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
3128
(1 row)
postgres=# alter table t add password1 varchar(50);
--在session3中可以發現session2發等待session 1
postgres=# SELECT locktype,
postgres-# pg_locks.pid,
postgres-# virtualtransaction,
postgres-# transactionid,
postgres-# nspname,
postgres-# relname,
postgres-# mode,
postgres-# granted,
postgres-# CASE
postgres-# WHEN granted='f' THEN
postgres-# 'get_lock'
postgres-# WHEN granted='t' THEN
postgres-# 'wait_lock'
postgres-# END lock_satus,
postgres-# CASE
postgres-# WHEN waiting='f' THEN
postgres-# 'waiting'
postgres-# WHEN waiting='t' THEN
postgres-# 'executing'
postgres-# END lock_satus, cast(date_trunc('second',query_start) AS timestamp) AS query_start, substr(query,1,25) AS query
postgres-# FROM pg_locks LEFT OUTER
postgres-# JOIN pg_class
postgres-# ON (pg_locks.relation = pg_class.oid) LEFT OUTER
postgres-# JOIN pg_namespace
postgres-# ON (pg_namespace.oid = pg_class.relnamespace), pg_stat_activity
postgres-# WHERE NOT pg_locks.pid=pg_backend_pid()
postgres-# AND pg_locks.pid=pg_stat_activity.pid
postgres-# AND transactionid is NOT null
postgres-# ORDER BY query_start;
locktype | pid | virtualtransaction | transactionid | nspname | relname | mode | granted | lock_satus | lock_satus | query_start | query
---------------+------+--------------------+---------------+---------+---------+---------------+---------+------------+------------+---------------------+---------------------------
transactionid | 4144 | 8/3137 | 4638456 | | | ExclusiveLock | t | wait_lock | waiting | 2015-10-21 20:29:58 | insert into t values(2,'r'
transactionid | 3128 | 2/14315 | 4638461 | | | ExclusiveLock | t | wait_lock | executing | 2015-10-21 20:30:32 | alter table t add passwor
--在修改表結構時,如果把ddl放在一個事務中,如果事務沒有提交,則其它的事務必須等待,包括查詢
--在session2事務中執行一個ddl
postgres=# begin;
BEGIN
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
3128
(1 row)
postgres=# alter table t add password2 varchar(50);
ALTER TABLE
--在session1事務中執行dml發生等待
postgres=# begin;
BEGIN
postgres=# insert into t values(2,'rudy2');
--在session3中可以發現session1在等待session2
locktype | pid | virtualtransaction | transactionid | nspname | relname | mode | granted | lock_satus | lock_satus | query_start | query
---------------+------+--------------------+---------------+---------+---------+---------------+---------+------------+------------+---------------------+---------------------------
transactionid | 3128 | 2/14316 | 4638466 | | | ExclusiveLock | t | wait_lock | waiting | 2015-10-21 20:37:38 | alter table t add passwor
--在session4中的查詢也發生了等待
postgres=# select * from t limit 1;
--故最好不要把ddl語句放在一個事務中,如果該事務沒有提交,否則其會造成後續的查詢與修改都等待
--如果一個session中有一個大的查詢在執行,此時最好不要在另一個session中修改表結構,因為查詢會阻塞ddl語句
--在session1中執行一個大的查詢
postgres=# select count(*) from t,t t1;
--在session2中的ddl語句不得不等待
postgres=# alter table t add password3 varchar(50);
--在session3中可以檢視到等待的鎖
locktype | pid | virtualtransaction | transactionid | nspname | relname | mode | granted | lock_satus | lock_satus | query_start | query
---------------+------+--------------------+---------------+---------+---------+---------------+---------+------------+------------+---------------------+---------------------------
transactionid | 3128 | 2/14317 | 4638470 | | | ExclusiveLock | t | wait_lock | executing | 2015-10-21 20:46:09 | alter table t add passwor
--此時如果有其它session的dml操作,又不得不等待session2的ddl語句執行結束,也不得不等待,進而造成排對等待
--由此展開,在主從複製過程中,如果從庫再進行類似報表的查詢,主庫有一個修改表結構的動作,wal日誌複製到從庫,但其卻不得不等待從庫查詢語句執行完成,才能應用日誌,進而造成主從之間的延遲
--故最好在修改表結構時觀察下是否有大的查詢在發生
--session1 中執行dml
postgres=# begin;
BEGIN
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
4144
(1 row)
postgres=# insert into t values(2,'rudy2');
INSERT 0 1
postgres=#
--在session2 中執行ddl語句,發現其要等待
postgres=# begin;
BEGIN
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
3128
(1 row)
postgres=# alter table t add password1 varchar(50);
--在session3中可以發現session2發等待session 1
postgres=# SELECT locktype,
postgres-# pg_locks.pid,
postgres-# virtualtransaction,
postgres-# transactionid,
postgres-# nspname,
postgres-# relname,
postgres-# mode,
postgres-# granted,
postgres-# CASE
postgres-# WHEN granted='f' THEN
postgres-# 'get_lock'
postgres-# WHEN granted='t' THEN
postgres-# 'wait_lock'
postgres-# END lock_satus,
postgres-# CASE
postgres-# WHEN waiting='f' THEN
postgres-# 'waiting'
postgres-# WHEN waiting='t' THEN
postgres-# 'executing'
postgres-# END lock_satus, cast(date_trunc('second',query_start) AS timestamp) AS query_start, substr(query,1,25) AS query
postgres-# FROM pg_locks LEFT OUTER
postgres-# JOIN pg_class
postgres-# ON (pg_locks.relation = pg_class.oid) LEFT OUTER
postgres-# JOIN pg_namespace
postgres-# ON (pg_namespace.oid = pg_class.relnamespace), pg_stat_activity
postgres-# WHERE NOT pg_locks.pid=pg_backend_pid()
postgres-# AND pg_locks.pid=pg_stat_activity.pid
postgres-# AND transactionid is NOT null
postgres-# ORDER BY query_start;
locktype | pid | virtualtransaction | transactionid | nspname | relname | mode | granted | lock_satus | lock_satus | query_start | query
---------------+------+--------------------+---------------+---------+---------+---------------+---------+------------+------------+---------------------+---------------------------
transactionid | 4144 | 8/3137 | 4638456 | | | ExclusiveLock | t | wait_lock | waiting | 2015-10-21 20:29:58 | insert into t values(2,'r'
transactionid | 3128 | 2/14315 | 4638461 | | | ExclusiveLock | t | wait_lock | executing | 2015-10-21 20:30:32 | alter table t add passwor
--在修改表結構時,如果把ddl放在一個事務中,如果事務沒有提交,則其它的事務必須等待,包括查詢
--在session2事務中執行一個ddl
postgres=# begin;
BEGIN
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
3128
(1 row)
postgres=# alter table t add password2 varchar(50);
ALTER TABLE
--在session1事務中執行dml發生等待
postgres=# begin;
BEGIN
postgres=# insert into t values(2,'rudy2');
--在session3中可以發現session1在等待session2
locktype | pid | virtualtransaction | transactionid | nspname | relname | mode | granted | lock_satus | lock_satus | query_start | query
---------------+------+--------------------+---------------+---------+---------+---------------+---------+------------+------------+---------------------+---------------------------
transactionid | 3128 | 2/14316 | 4638466 | | | ExclusiveLock | t | wait_lock | waiting | 2015-10-21 20:37:38 | alter table t add passwor
--在session4中的查詢也發生了等待
postgres=# select * from t limit 1;
--故最好不要把ddl語句放在一個事務中,如果該事務沒有提交,否則其會造成後續的查詢與修改都等待
--如果一個session中有一個大的查詢在執行,此時最好不要在另一個session中修改表結構,因為查詢會阻塞ddl語句
--在session1中執行一個大的查詢
postgres=# select count(*) from t,t t1;
--在session2中的ddl語句不得不等待
postgres=# alter table t add password3 varchar(50);
--在session3中可以檢視到等待的鎖
locktype | pid | virtualtransaction | transactionid | nspname | relname | mode | granted | lock_satus | lock_satus | query_start | query
---------------+------+--------------------+---------------+---------+---------+---------------+---------+------------+------------+---------------------+---------------------------
transactionid | 3128 | 2/14317 | 4638470 | | | ExclusiveLock | t | wait_lock | executing | 2015-10-21 20:46:09 | alter table t add passwor
--此時如果有其它session的dml操作,又不得不等待session2的ddl語句執行結束,也不得不等待,進而造成排對等待
--由此展開,在主從複製過程中,如果從庫再進行類似報表的查詢,主庫有一個修改表結構的動作,wal日誌複製到從庫,但其卻不得不等待從庫查詢語句執行完成,才能應用日誌,進而造成主從之間的延遲
--故最好在修改表結構時觀察下是否有大的查詢在發生