梧桐数据库锁处理过程

遇到一个奇怪的现象,select 和 delete 表时正常执行,但 truncate 和 drop 表时会一直运行,也不报错。

一、查了些资料才发现问题的原因,总结如下:

" drop table " 和 " truncate table " 需要申请排它锁 " ACCESS EXCLUSIVE ", 执行这个命令卡住时,说明此时这张表上还有操作正在进行,比如查询等,那么只有等待这个查询操作完成," drop table " 或" truncate table "或者增加字段的 SQL 才能获取这张表上的 " ACCESS EXCLUSIVE " 锁 ,操作才能进行下去。

补充内容:产生锁冲突的操作及锁类型

在数据库操作中,不同的SQL命令会申请不同类型的锁,以确保数据的一致性和完整性。以下是一些常见的操作及其申请的锁类型:

  1. SELECT :默认情况下,SELECT 操作会申请共享锁(Share Locks),这意味着多个事务可以同时读取数据,但不能修改。共享锁的类型通常是 ACCESS SHARE
  2. UPDATE、DELETE :这些操作会申请排它锁(Exclusive Locks),但不是 ACCESS EXCLUSIVE。它们通常申请 ROW EXCLUSIVESHARE UPDATE EXCLUSIVE 锁,这允许事务修改数据,但不允许其他事务同时修改同一行。
  3. INSERTINSERT 操作也会申请 ROW EXCLUSIVE 锁,以确保新插入的行不会被其他事务同时修改。
  4. ALTER TABLEALTER TABLE 操作,如添加或删除列,会申请 ACCESS EXCLUSIVE 锁,因为这些操作需要修改表结构,可能会影响所有行。
  5. TRUNCATE TABLETRUNCATE TABLE 操作会申请 ACCESS EXCLUSIVE 锁,因为它需要删除表中的所有行,这是一个重量级操作,需要确保没有其他事务正在访问表。
  6. DROP TABLEDROP TABLE 操作同样需要 ACCESS EXCLUSIVE 锁,因为它会完全删除表,这是一个不可逆的操作,需要确保没有其他事务正在使用表。
  7. LOCK TABLE :使用 LOCK TABLE 命令时,可以指定不同的锁模式,如 ACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEACCESS EXCLUSIVE

当一个事务持有共享锁(如 ACCESS SHARE)时,其他事务可以读取数据但不能修改。而当一个事务尝试获取 ACCESS EXCLUSIVE 锁时,它会等待所有现有的共享锁释放。如果存在未完成的查询或其他操作,它们持有的共享锁会阻止 ACCESS EXCLUSIVE 锁的获取,从而导致 TRUNCATE TABLEDROP TABLE 操作卡住。

为了解决这种锁冲突,可以采取以下步骤:

  1. 确定持有锁的事务的进程ID(procpid)。
  2. 使用 pg_cancel_backendpg_terminate_backend 函数终止持有锁的事务。

这些操作应该谨慎执行,因为终止事务可能会导致数据不一致或其他问题。在执行这些操作之前,最好先评估影响,并确保有适当的数据备份。

二、知道原因后解锁步骤如下:

1.检索出死锁进程的ID。

SELECT * FROM pg_stat_activity WHERE datname='死锁的数据库ID ';

检索出来的字段中,【 wating 】字段,数据为t的那条,就是锁等待的进程。找到对应的【 procpid 】列的值。

注:

数据库中有两种基本的锁:排它锁(Exclusive Locks)和共享锁(Share Locks)。 如果数据对象加上排它锁,则其他的事务不能对它读取和修改。 如果加上共享锁,则该数据库对象可以被其他事务读取,但不能修改。 锁定模式:ACCESS SHARE,ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE,SHARE ROW EXCLUSIVE,EXCLUSIVE,ACCESS EXCLUSIVE。 LOCK TABLE department1 IN ACCESS EXCLUSIVE MODE;

查看锁的会话,确定锁的来源:

select * from pg_stat_activity_global where current_query<>''; select * from oushu_lock_status where pid = 38099 ; select * from oushu_lock_status where relation = 955982; select * from pg_stat_activity_global where procpid=630657; select pg_terminate_backend(27189)

2.将进程杀掉。

SELECT pg_cancel_backend('死锁那条数据的procpid值 ');

结果:运行完后,再次更新这个表,sql 顺利执行。

如果 pg_stat_activity_global 没有记录,则查询 pg_locks 是否有这个对象的锁

复制代码
select oid,relname from pg_class where relname='table name';
select locktype,pid,relation,mode,granted,* from pg_locks where relation='上面查询出来的oid';
SELECT pg_cancel_backend('进程ID ');

另外,也可以使用 pg_terminate_backend() 函数也可以杀掉进程。

相关推荐
Raymond运维1 小时前
MariaDB源码编译安装(二)
运维·数据库·mariadb
沢田纲吉1 小时前
🗄️ MySQL 表操作全面指南
数据库·后端·mysql
RestCloud17 小时前
SQL Server到Hive:批处理ETL性能提升30%的实战经验
数据库·api
RestCloud17 小时前
为什么说零代码 ETL 是未来趋势?
数据库·api
ClouGence19 小时前
CloudCanal + Paimon + SelectDB 从 0 到 1 构建实时湖仓
数据库
DemonAvenger1 天前
NoSQL与MySQL混合架构设计:从入门到实战的最佳实践
数据库·mysql·性能优化
AAA修煤气灶刘哥2 天前
后端人速藏!数据库PD建模避坑指南
数据库·后端·mysql
RestCloud2 天前
揭秘 CDC 技术:让数据库同步快人一步
数据库·api
得物技术2 天前
MySQL单表为何别超2000万行?揭秘B+树与16KB页的生死博弈|得物技术
数据库·后端·mysql