梧桐数据库锁处理过程

遇到一个奇怪的现象,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() 函数也可以杀掉进程。

相关推荐
工业甲酰苯胺5 分钟前
分布式系统架构:服务容错
数据库·架构
独行soc1 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘
White_Mountain1 小时前
在Ubuntu中配置mysql,并允许外部访问数据库
数据库·mysql·ubuntu
Code apprenticeship1 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站1 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶1 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
清平乐的技术专栏2 小时前
Hive SQL 查询所有函数
hive·hadoop·sql
梦想平凡3 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
TianyaOAO3 小时前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
Ewen Seong4 小时前
mysql系列5—Innodb的缓存
数据库·mysql·缓存