PostgreSQL 锁机制

锁存在的意义

在了解 PostgreSQL 锁之前,我们需要了解锁存在的意义是啥?

当多个会话同时访问数据库的同一数据时,理想状态是为所有会话提供高效的访问,同时还要维护严格的数据一致性。那数据一致性通过什么来维护呢,就是通过 MVCC(多版本并发控制)

MVCC(多版本并发控制):每个SQL语句看到的都只是当前事务开始的数据快照,而不管底层数据的当前状态。

这样可以保护语句不会看到在相同数据上由其他连接执行更新的并发事务造成的不一致数据,为每一个数据库会话提供事务隔离。MVCC 避免了传统的数据库系统的锁定方法,将通过锁争夺最小化的方法来达到多会话并发访问时的性能最大化目的。

PostgreSQL 锁机制浅析

锁机制在 PostgreSQL 里非常重要 (对于其他现代的 RDBMS 也是如此)。对于数据库应用程序开发者(特别是那些涉及到高并发代码的程序员),需要对锁非常熟悉。对于某些问题,锁需要被重点关注与检查。大部分情况,这些问题跟死锁或者数据不一致有关系,基本上都是由于对 Postgres 的锁机制不太了解导致的。虽然锁机制在 Postgres 内部很重要,但是文档缺非常缺乏,有时甚至还是错误的,与文档所指出的结果不一致。我会告诉你精通 Postgres 的锁机制需要知道的一切,要知道对锁了解的越多,解决与锁相关的问题就会越快。

PostgreSQL 提供了多种锁模式用于控制对表中数据的并发访问,其中最主要的是表级锁行级锁,除此之外还有页级锁咨询锁等等,接下来主要介绍表级锁与行级锁。

表级锁

表级锁通常会在执行各种命令执行时自动获取,或者通过在事务中使用LOCK语句显示获取。

每种锁都有自己的冲突集合。

表级锁:两个事务在同一时刻不能在同一个表上持有互相冲突的锁,但是可以同时持有不冲突的锁。

表级锁共有八种模式,其存在于PG的共享内存中,可以通过 pg_locks 系统视图查阅。

ACCESS SHARE 访问共享

SELECT 命令在被引用的表上会获得一个这种模式的锁。通常,任何只读取表而不修改它的查询都将获取这种表模式。

ROW SHARE 行共享

SELECT FOR UPDATESELECT FOR SHARE 命令在目标表上会获得一个这种模式的锁。(加上在被引用但没有选择 FOR UPDATE / FOR SHARE 的任何其他表上的 ACCESS SHARE 锁。)

ROW EXCLUSIVE 行独占

UPDATEDELETEINSERT 命令在目标表上会获得一个这种模式的锁。(加上在任何其他被引用表上的 ACCESS SHARE锁。)通常,这种锁模式将被任何修改表中数据的命令取得。

SHARE UPDATE EXCLUSIVE 共享更新独占

VACUUM(不带FULL)ANALYZECREATE INDEX CONCURRENTLYREINDEX CONCURRENTLYCREATE STATISTICS 命令以及某些 ALTER INDEXALTER TABLE 命令的变体会获得。这种模式保护一个表不受并发模式改变和 VACUUM 运行的影响。

SHARE 共享

CREATE INDEX(不带CONCURRENTLY) 命令会获得。

这种模式保护一个表不受并发数据改变的影响。

SHARE ROW EXCLUSIVE 共享行独占

CREATE TRIGGER 命令和某些形式的 ALTER TABLE 命令会获得。

这种模式保护一个表不受并发数据修改所影响,并且是自排他的,这样在同一个时刻只能有一个会话持有它。

EXCLUSIVE 排他

REFRESH METERIALIZED VIEW CONCURRENTLY 命令会获得。

这种模式只允许并发的ACCESS SHARE锁,即只有来自于表的读操作可以与一个持有该锁模式的事务并行处理。

ACCESS EXCLUSIVE 访问独占

ALTER TABLEDROP TABLETRUNCATEREINDEXCLUSTERVACUUM FULLREFRESH MATERIALIZED VIEW(不带CONCURRENTLY)命令会获得。很多形式的 ALTER INDEXALTER TABLE 也在这个层面上获得锁。这也是未显式指定模式的 LOCK TABLE 命令的默认锁模式。

这种模式与所有模式的锁冲突。这种模式保持者是访问该表的唯一事务。

表级锁模式冲突表

(注:X表示冲突。)

示例一

当一个会话运行了 update 语句,此时会话表上的锁模式为 ROW EXCLUSIVE,从上图我们可以看出 ROW EXCLUSIVESHARESHARE ROWROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 锁模式冲突。

也就是说在这个会话未提交事务释放锁之前,我们不能做申请 SHARESHARE ROWROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 锁模式相关的操作,例如 CREATE INDEX(不带CONCURRENTLY)ALTER TABLEDROP TABLETRUNCATEREINDEXCLUSTERVACUUM FULLREFRESH MATERIALIZED VIEW(不带CONCURRENTLY)等。

我们先创建一个测试数据库:

# 创建测试用户
create user root password 'root';
# 创建测试数据库
create database mydb owner root encoding UTF8;
# 创建和测试用户同名Schema
create schema AUTHORIZATION CURRENT_USER;

复制

我们创建一张测试表 t_user,并插入一条测试数据:

create table "t_user" ("id" bigserial not null,"username" varchar (64) not null,constraint t_user_pk primary key (id)
);
insert into t_user values(1, 'ACGkaka');

复制

会话一: 执行 update 语句。

begin;
update t_user set username='ACGkaka1' where id=1;

复制

会话二: 执行 alter table 语句,这时会处于等待状态。

alter table t_user add dept_no int;

复制

执行SQL,查看锁等待情况:(SQL参考附录一

注:Lock_Granted: true即为堵塞源。

直到“会话一”结束,“会话二”语句才执行成功。

示例二

当一个会话运行了 truncate 语句,此时会话表上的锁模式为 ACCESS EXCLUSIVE,从图上我们可以看到这种模式和所有的锁模式都冲突。这意味着在当前会话未结束之前,这个表上的其他操作都做不了。

会话一: 执行 truncate 语句。

会话二: 执行 select 语句时处于等待状态。

执行SQL,查看锁等待情况:(SQL参考附录一

注:Lock_Granted: true即为堵塞源。

直到“会话一”结束,“会话二”语句才执行成功。

通过上面2个示例,应该都比较了解各种锁模式冲突的情况了,接下来我们介绍行级锁。

行级锁

行级锁:同一个事务可能会在相同的行上保持冲突的锁,甚至是在不同的子事务中。但是除此之外,两个事务永远不可能在相同的行上持有冲突的锁。

行级锁不影响数据查询,它们只阻塞对同一行的写入者和加锁者。行级锁在事务结束时或保存点回滚的时候释放,就像表级锁一样。下面是常用的行级锁模式:

FOR UPDATE 更新

FOR UPDATE 会导致由 SELECT 语句检索到的行被锁定,就好像它们要被更新。这可以阻止它们被其他事务锁定、修改或者删除,直到当前事务结束。

也就是说其他尝试 UPDATEDELETESELECT FOR UPDATESELECT FOR NO KEY UPDATESELECT FOR SHARE 或者 SELECT FOR KEY SHARE 这些行的事务将被阻塞,直到当前事务结束。

反过来,SELECT FOR UPDATE 将等待已经在相同行上运行以上这些命令的并发事务,并且接着锁定并且返回被更新的行(或者没有行,因为行可能已被删除)。

FOR NO KEY UPDATE 无键更新

行为与 FOR UPDATE 类似,不过获得的锁较弱,这种锁将不会阻塞尝试在相同行上获得锁的 SELECT FOR KEY SHARE 命令。任何不获取 FOR UPDATE 锁的 UPDATE 也会获得这种锁模式。

FOR SHARE 共享

行为与 FOR NO KEY UPDATE 类似,不过它在每个检索到的杭上获得一个共享锁而不是排他锁

一个共享锁会阻塞其他食物在这些行上执行 UPDATEDELETESELECT FOR UPDATE 或者 SELECT FOR NO KEY UPDATE,但是它不会阻止它们执行 SELECT FOR SHARE 或者 SELECT FRO KEY SHARE

FOR KEY SHARE 键共享

行为与 FOR SHARE 类似,不过锁较弱,SELECT FOR UPDATE 会被阻塞,但是 SELECT FOR NO KEY UPDATE 不会被阻塞,一个键共享锁会阻塞其他事务执行修改键值的 DELETE 或者 UPDATE,但不会阻塞其他 UPDATE,也不会阻止 SELECT FOR NO KEY UPDATESELECT FOR SHARE 或者 SELECT FOR KEY SHARE

表级锁模式冲突表

4 个执行语句 timeout 参数

lock_timeout

lock_timeout:获取一个表,索引,行上的锁超过这个时间,直接报错,不等待,0为禁用。

statement_timeout

statement_timeout:当SQL语句的执行时间超过这个设置时间,终止执行SQL,0为禁用。

idle_in_transaction_session_timeout

idle_in_transaction_session_timeout:在一个空闲的事务中,空闲时间超过这个值,将视为超时,0为禁用。

deadlock_timeout

dealdlock_timeout:死锁时间超过这个值将直接报错,不会等待,默认设置为1s。

页级锁

除了表级别和行级别的锁以外,页面级别的共享/排他锁被用来控制对共享缓冲池中表页面的读/写。 这些锁在行被抓取或者更新后马上被释放。应用开发者通常不需要关心页级锁,我们在这里提到它们只是为了完整。

劝告锁

Postgres提供创建具有应用定义的锁的方法,这些被称为劝告锁(advisory locks),因为系统并不支持其使用,其取决于应用对锁的正确使用。

Postgres 中有两种途径可以获得一个劝告锁:会话层级或事务层级。一旦在会话层级获得劝告锁,会一直保持到被显式释放或会话结束。不同于标准的锁请求,会话层级的劝告锁请求并不遵守事务语义:事务被回滚后锁也会随着回滚保持着,同样地即使调用锁的事务之后失败了,解锁请求仍然是有效的。一个锁可以被拥有它的进程多次获取;对于每个完成的锁请求,在锁被真正释放前一定要有一个对应的解锁请求。

另一方面,事务层级的锁请求表现得更像普通的锁请求:它们在事务结束时会自动释放,并且没有显式的解锁操作。对于短暂地使用劝告锁,这种特性通常比会话层级更方便。可以想见,会话层级与事务层级请求同一个劝告锁标识符会互相阻塞。如果一个会话已经有了一个劝告锁,它再请求时总会成功的,即使其他会话在等待此锁;不论保持现有的锁和新的请求是会话层级还是事务层级,都是这样。文档中可以找到操作劝告锁的完整函数列表。

这里有几个获取事务层级劝告锁的例子(pg_locks是系统视图,文章之后会说明。它存有事务保持的表级锁和劝告锁的信息):

启动第一个psql会话,开始一个事务并获取一个劝告锁:

-- Transaction 1
BEGIN; SELECT pg_advisory_xact_lock(1); 
-- Some work here

复制

现在启动第二个psql会话并在同一个劝告锁上执行一个新的事务:

-- Transaction 2
BEGIN; SELECT pg_advisory_xact_lock(1);
-- This transaction is now blocked

复制

在第三个psql会话里我们可以看下这个锁现在的情况:

SELECT * FROM pg_locks;-- Only relevant parts of outputlocktype    | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction |  pid  |        mode         | granted |fastpath---------------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-------+---------------------+---------+----------advisory   |    16393 |          |      |       |            |               |       0 |     1 |        1 | 4/36               |  1360 | ExclusiveLock       | f       | fadvisory   |    16393 |          |      |       |            |               |       0 |     1 |        1 | 3/186              | 14340 | ExclusiveLock       | t       | f
-- Transaction 1
COMMIT;
-- This transaction now released lock, so Transaction 2 can continue

复制

我们同样可以调用获取锁的非阻塞方法,这些方法会尝试去获取锁,并返回true(如果成功了)或者false(如果无法获取锁)。

-- Transaction 1
BEGIN;
SELECT pg_advisory_xact_lock(1);
-- Some work here
-- Transaction 2
BEGIN;
SELECT pg_try_advisory_xact_lock(1) INTO vLockAcquired;
IF vLockAcquired THEN
-- Some work
ELSE
-- Lock not acquired
END IF;
-- Transaction 1
COMMIT;

复制

监控锁

所有活动事务持有的监控锁的基本配置即为系统视图 pg_locks。这个视图为每个可加锁的对象、已请求的锁模式和相关事务包含一行记录。非常重要的一点是,pg_locks 持有内存中被跟踪的锁的信息,所以它不显示行级锁!(译注:据查以前的文档,有关行级锁的信息是存在磁盘上,而非内存)这个视图显示表级锁和劝告锁。如果一个事务在等待一个行级锁,它通常在视图中显示为在等待该行级锁的当前所有者的固定事务 ID。这使得调试行级锁更为困难。事实上,在任何地方你都看不到行级锁,直到有人阻塞了持有此锁的事务(然后你在 pg_locks 表里可以看到一个被上锁的元组)。pg_locks 是可读性欠佳的视图(不是很人性化),所以我们来让显示锁定信息的视图更好接受些:

-- View with readable locks info and filtered out locks on system tables
CREATE VIEW active_locks AS
SELECT clock_timestamp(), pg_class.relname, pg_locks.locktype, pg_locks.database,pg_locks.relation, pg_locks.page, pg_locks.tuple, pg_locks.virtualtransaction,pg_locks.pid, pg_locks.mode, pg_locks.granted
FROM pg_locks JOIN pg_class ON pg_locks.relation = pg_class.oid
WHERE relname !~ '^pg_' and relname <> 'active_locks';
-- Now when we want to see locks just type
SELECT * FROM active_locks;

复制

--查看会话session
select pg_backend_pid();--查看会话持有的锁
select * from pg_locks where pid=3797;--1,查看数据库
select  pg_database.datname, pg_database_size(pg_database.datname) AS size from pg_database; //查询所有数据库,及其所占空间大小--2. 查询存在锁的数据表
select a.locktype,a.database,a.pid,a.mode,a.relation,b.relname -- ,sa.*
from pg_locks a
join pg_class b on a.relation = b.oid 
inner join  pg_stat_activity sa on a.pid=sa.procpid--3.查询某个表内,状态为lock的锁及关联的查询语句
select a.locktype,a.database,a.pid,a.mode,a.relation,b.relname -- ,sa.*
from pg_locks a
join pg_class b on a.relation = b.oid 
inner join  pg_stat_activity sa on a.pid=sa.procpid
where a.database=382790774  and sa.waiting_reason='lock'
order by sa.query_start--4.查看数据库表大小
select pg_database_size('playboy');

复制

--查看会话被谁阻塞
select pg_blocking_pids(3386);

复制

死锁

显式锁定的使用可能会增加死锁的可能性,死锁是指两个(或多个)事务相互持有对方想要的锁。

例如,如果事务 1 在表 A 上获得一个排他锁,同时试图获取一个在表 B 上的排他锁, 而事务 2 已经持有表 B 的排他锁,同时却正在请求表 A 上的一个排他锁,那么两个事务就都不能进行下去。PostgreSQL能够自动检测到死锁情况 并且会通过中断其中一个事务从而允许其它事务完成来解决这个问题(具体哪个事务会被中 断是很难预测的,而且也不应该依靠这样的预测)。

要注意死锁也可能会作为行级锁的结果而发生(并且因此,它们即使在没有使用显式锁定的情况下也会发生)。考虑如下情况,两个并发事务在修改一个表。第一个事务执行:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111; 

复制

这样就在指定帐号的行上获得了一个行级锁。然后,第二个事务执行:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222; 
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;

复制

第一个UPDATE语句成功地在指定行上获得了一个行级锁,因此它成功更新了该行。 但是第 二个UPDATE语句发现它试图更新的行已经被锁住了,因此它等待持有该锁的事务结束。事 务二现在就在等待事务一结束,然后再继续执行。现在,事务一执行:

UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;

复制

事务一试图在指定行上获得一个行级锁,但是它得不到:事务二已经持有了这样的锁。所以 它要等待事务二完成。因此,事务一被事务二阻塞,而事务二也被事务一阻塞:一个死锁。 PostgreSQL将检测这样的情况并中断其中一个事务。

防止死锁的最好方法通常是保证所有使用一个数据库的应用都以一致的顺序在多个对象上获得锁。在上面的例子里,如果两个事务以同样的顺序更新那些行,那么就不会发生死锁。 我们也应该保证一个事务中在一个对象上获得的第一个锁是该对象需要的最严格的锁模式。如果我们无法提前验证这些,那么可以通过重试因死锁而中断的事务来即时处理死锁。

只要没有检测到死锁情况,寻求一个表级或行级锁的事务将无限等待冲突锁被释放。这意味着一个应用长时间保持事务开启不是什么好事(例如等待用户输入)。

附录

附录一:查看锁等待情况SQL
witht_wait as(select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.granted,a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,a.fastpath,b.state,b.query,b.xact_start,b.query_start,b.usename,b.datname,b.client_addr,b.client_port,b.application_namefrom pg_locks a,pg_stat_activity b where a.pid=b.pid and not a.granted),t_run as(select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.granted,a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,a.fastpath,b.state,b.query,b.xact_start,b.query_start,b.usename,b.datname,b.client_addr,b.client_port,b.application_namefrom pg_locks a,pg_stat_activity b where a.pid=b.pid and a.granted),t_overlap as(select r.* from t_wait w join t_run r on(r.locktype is not distinct from w.locktype andr.database is not distinct from w.database andr.relation is not distinct from w.relation andr.page is not distinct from w.page andr.tuple is not distinct from w.tuple andr.virtualxid is not distinct from w.virtualxid andr.transactionid is not distinct from w.transactionid andr.classid is not distinct from w.classid andr.objid is not distinct from w.objid andr.objsubid is not distinct from w.objsubid andr.pid <> w.pid)),t_unionall as(select r.* from t_overlap runion allselect w.* from t_wait w)select locktype,datname,relation::regclass,page,tuple,virtualxid,transactionid::text,classid::regclass,objid,objsubid,string_agg('Pid: '||case when pid is null then 'NULL' else pid::text end||chr(10)||'Lock_Granted: '||case when granted is null then 'NULL' else granted::text end||' , Mode: '||case when mode is null then 'NULL' else mode::text end||' , FastPath: '||case when fastpath is null then 'NULL' else fastpath::text end||' , VirtualTransaction: '||case when virtualtransaction is null then 'NULL' else virtualtransaction::text end||' , Session_State: '||case when state is null then 'NULL' else state::text end||chr(10)||'Username: '||case when usename is null then 'NULL' else usename::text end||' , Database: '||case when datname is null then 'NULL' else datname::text end||' , Client_Addr: '||case when client_addr is null then 'NULL' else client_addr::text end||' , Client_Port: '||case when client_port is null then 'NULL' else client_port::text end||' , Application_Name: '||case when application_name is null then 'NULL' else application_name::text end||chr(10)||'Xact_Start: '||case when xact_start is null then 'NULL' else xact_start::text end||' , Query_Start: '||case when query_start is null then 'NULL' else query_start::text end||' , Xact_Elapse: '||case when (now()-xact_start) is null then 'NULL' else (now()-xact_start)::text end||' , Query_Elapse: '||case when (now()-query_start) is null then 'NULL' else (now()-query_start)::text end||chr(10)||'SQL (Current SQL in Transaction): '||chr(10)||case when query is null then 'NULL' else query::text end,chr(10)||'--------'||chr(10)order by( case modewhen 'INVALID' then 0when 'AccessShareLock' then 1when 'RowShareLock' then 2when 'RowExclusiveLock' then 3when 'ShareUpdateExclusiveLock' then 4when 'ShareLock' then 5when 'ShareRowExclusiveLock' then 6when 'ExclusiveLock' then 7when 'AccessExclusiveLock' then 8else 0end  ) desc,(case when granted then 0 else 1 end)) as lock_conflictfrom t_unionallgroup bylocktype,datname,relation,page,tuple,virtualxid,transactionid::text,classid,objid,objsubid ;

复制

输出结果格式如下:

附录二:查看阻塞会话,并生成kill sql
SELECT pg_cancel_backend(pid); – session还在,事物回退;
SELECT pg_terminate_backend(pid); --session消失,事物回退

复制

with recursive 
tmp_lock as (select distinct--w.mode w_mode,w.page w_page,--w.tuple w_tuple,w.xact_start w_xact_start,w.query_start w_query_start,--now()-w.query_start w_locktime,w.query w_queryw.pid as id,--w_pid,r.pid as parentid--r_pid,--r.locktype,r.mode r_mode,r.usename r_user,r.datname r_db,--r.relation::regclass,--r.page r_page,r.tuple r_tuple,r.xact_start r_xact_start,--r.query_start r_query_start,--now()-r.query_start r_locktime,r.query r_query,from (select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,b.query as query,b.xact_start,b.query_start,b.usename,b.datnamefrom pg_locks a,pg_stat_activity bwhere a.pid=b.pidand not a.granted) w,(select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,b.query as query,b.xact_start,b.query_start,b.usename,b.datnamefrom pg_locks a,pg_stat_activity b -- select pg_typeof(pid) from pg_stat_activitywhere a.pid=b.pidand a.granted) rwhere 1=1and r.locktype is not distinct from w.locktypeand r.database is not distinct from w.databaseand r.relation is not distinct from w.relationand r.page is not distinct from w.pageand r.tuple is not distinct from w.tupleand r.classid is not distinct from w.classidand r.objid is not distinct from w.objidand r.objsubid is not distinct from w.objsubidand r.transactionid is not distinct from w.transactionidand r.pid <> w.pid),
tmp0 as (select *from tmp_lock tlunion allselect t1.parentid,0::int4from tmp_lock t1where 1=1and t1.parentid not in (select id from tmp_lock)),
tmp3 (pathid,depth,id,parentid) as (SELECT array[id]::text[] as pathid,1 as depth,id,parentidFROM tmp0where 1=1 and parentid=0unionSELECT t0.pathid||array[t1.id]::text[] as pathid,t0.depth+1 as depth,t1.id,t1.parentidFROM tmp0 t1, tmp3 t0where 1=1 and t1.parentid=t0.id
)
select distinct'/'||array_to_string(a0.pathid,'/') as pathid,a0.depth,a0.id,a0.parentid,lpad(a0.id::text, 2*a0.depth-1+length(a0.id::text),' ') as tree_id,--'select pg_cancel_backend('||a0.id|| ');' as cancel_pid,--'select pg_terminate_backend('||a0.id|| ');' as term_pid,case when a0.depth =1 then 'select pg_terminate_backend('|| a0.id || ');' else null end  as term_pid,case when a0.depth =1 then 'select cancel_backend('|| a0.id || ');' else null end  as cancel_pid,a2.datname,a2.usename,a2.application_name,a2.client_addr,a2.wait_event_type,a2.wait_event,a2.state--,a2.backend_start,a2.xact_start,a2.query_start
from tmp3 a0
left outer join (select distinct '/'||id||'/' as prefix_id,idfrom tmp0where 1=1 ) a1
on position( a1.prefix_id in '/'||array_to_string(a0.pathid,'/')||'/' ) >0
left outer join pg_stat_activity a2 -- select * from pg_stat_activity
on a0.id = a2.pid
order by '/'||array_to_string(a0.pathid,'/'),a0.depth;

复制

输出结果格式如下:

附录三:查询当前执行时间超过60s的sql
selectpg_stat_activity.datname,pg_stat_activity.pid,pg_stat_activity.query,pg_stat_activity.client_addr,clock_timestamp() - pg_stat_activity.query_start
frompg_stat_activity pg_stat_activity
where(pg_stat_activity.state = any (array['active'::text,'idle in transaction'::text]))and (clock_timestamp() - pg_stat_activity.query_start) > '00:00:60'::interval
order by(clock_timestamp() - pg_stat_activity.query_start) desc;

复制

输出结果格式如下:

附录四:查询所有已经获取锁的SQL
SELECT pid, state, usename, query, query_start 
from pg_stat_activity 
where pid in (select pid from pg_locks l  join pg_class t on l.relation = t.oid and t.relkind = 'r' 
);

复制

输出结果格式如下:

MySQL与PostgreSQL之间的对比

postgresql数据库锁的分类详细,他不会出现锁升级的情况,但也带来用法的繁琐。

mysql数据库当行锁不是所在主键上时会升级成表锁。

mysql和postgresql总体不同基本对比如下:
PostgreSQL的优势
  • PGSQL没有CPU核心数限制,MySQL能用128核CPU。
  • PG主表采用堆表存放,MySQL采用索引组织表,能够支持比MySQL更大的数据量。
  • PG的主备复制属于物理复制(完胜逻辑复制,维护简单),相对于MySQL基于binlog的逻辑复制(sql_log_binbinlog_format等参数设置不正确都会导致主从不一致),数据的一致性更加可靠,复制性能更高,对主机性能的影响也更小。
  • MySQL的存储引擎插件化机制,存在锁机制复杂影响并发的问题,而PG不存在这个机制。
  • PGSQL支持 JIT 执行计划即时编译,使用LLVM编译器,MySQL不支持执行计划即时编译。
  • PGSQL一共有255个参数,用到的大概是80个,参数比较稳定,用上个大版本配置文件也可以启动当前大版本数据库(版本兼容性好),而MySQL一共有707个参数,用到的大概是180个,参数不断增加,就算小版本也会增加参数,大版本之间会有部分参数不兼容情况。参数多引起维护成本加大。比如:PGSQL系统自动设置从库默认只读,不需要人工添加配置,维护简单,MySQL从库需要手动设置参数super_read_only=on,让从库设置为只读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/754441.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出Go的`encoding/xml`库:实战开发指南

深入浅出Go的encoding/xml库&#xff1a;实战开发指南 引言基本概念XML简介Go语言中的XML处理结构体标签&#xff08;Struct Tags&#xff09; 解析XML数据使用xml.Unmarshal解析XML结构体标签详解处理常见解析问题 生成XML数据使用xml.Marshal生成XML使用xml.MarshalIndent优化…

加速量子计算机商业化!富士通日立NEC等联合成立新量子计算公司

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 编辑丨王珩 编译/排版丨沛贤 深度好文&#xff1a;700字丨5分钟阅读 日本工业界和学术界将在 2024 年联合成立新一家公司&#xff0c;研发量子计算机并将其商业化。包括富士通、日立和NEC在内…

arcgis 点连接到面(以地级市图层为例)

地级市图层进行“点到面”的连接&#xff0c;并输出 在点击地级市图层&#xff0c;右击——连接和关联——连接 选择基于空间位置的另一图层数据&#xff0c;文件选择上面输出并添加的图层文件&#xff0c;进行“点到面”的连接&#xff0c;可依据新需求选择平均值&#xff0c…

Halcon 3D算子总结整理

halcon 3D包含以下几个模块&#xff1a; 3D Matching&#xff08;3D匹配&#xff09;3D Object Model&#xff08;3D模型&#xff09;3D Reconstruction&#xff08;3D重构&#xff09;3D Transformations&#xff08;3D转换&#xff09; 1. 3D Matching 1.1 3D Box3D盒查找器…

解锁数据可视化新境界:山海鲸可视化免费编辑与组件探索

作为一名长期使用山海鲸可视化的资深用户&#xff0c;我在数据可视化看板的制作过程中&#xff0c;深刻感受到了这款软件带来的便捷与高效。今天&#xff0c;我想与大家分享一些我在使用山海鲸可视化制作数据可视化看板时的经验&#xff0c;给对这款产品同样感兴趣的朋友同行一…

TT-100K数据集,YOLO格式

TT-100K数据集YOLO格式&#xff0c;分为train、val和test&#xff0c;其中train中共有6793张图片&#xff0c;val中共有1949张图片&#xff0c;test中共有996张图片。数据集只保留包含图片数超过100的类别。共计46类。

C#,图论与图算法,无向图(Graph)回环(Cycle)的不相交集(disjoint)或并集查找(union find)判别算法与源代码

1 回环(Cycle)的不相交集(disjoint)或并集 不相交集数据结构是一种数据结构,它跟踪划分为多个不相交(非重叠)子集的一组元素。联合查找算法是对此类数据结构执行两个有用操作的算法: 查找:确定特定元素所在的子集。这可用于确定两个元素是否在同一子集中。 并集:将…

Windows server 2008 R2共享文件配置和web网站的发布 试题一(Windows部分)

Windows server 2008 R2共享文件配置和web网站的发布 试题一&#xff08;Windows部分&#xff09; 设置虚拟机与本机互通设置虚拟机IP关闭虚拟机防火墙设置本机IP测试本机与虚拟机是否可以互通 开启文件共享function discovery resource publication服务的开启SSDP Discovery服…

web渗透测试漏洞复现:Elasticsearch未授权漏洞复现

web渗透测试漏洞复现 Elasticsearch未授权漏洞复现Elasticsearch简介Elasticsearch复现Elasticsearch漏洞修复和加固措施 Elasticsearch未授权漏洞复现 Elasticsearch简介 Elasticsearch 是一款 Java 编写的企业级搜索服务&#xff0c;它以分布式多用户能力和全文搜索引擎为特…

利用textarea和white-space实现最简单的文章编辑器 支持缩进和换行

当你遇到一个非常基础的文章发布和展示的需求&#xff0c;只需要保留换行和空格缩进&#xff0c;你是否会犹豫要使用富文本编辑器&#xff1f;实际上这个用原生的标签两步就能搞定&#xff01; 1.直接用textarea当编辑器 textarea本身就可以保存空格和换行符&#xff0c;示例如…

【目标检测】2. RCNN

接上篇 【目标检测】1. 目标检测概述_目标检测包括预测目标的位置吗?-CSDN博客 一、前言 CVPR201 4经典paper:《 Rich feature hierarchies for accurate object detection and semantic segmentation》&#xff0c;https://arxiv.org/abs/1311.2524, 这篇论文的算法思想被称…

C#集合:从字典到队列——探索数据结构核心

文章目录 C# 中的集合类型C# Dictionary 字典C# Hashtable&#xff1a;哈希表Hashtable 类中的属性Hashtable 类中的方法 C# SortedList&#xff1a;排序列表SortedList 类的中的属性SortedList 类的中的方法 C# Stack&#xff1a;堆栈Stack 类中的属性Stack 类中的方法 C# Que…

基于深度学习YOLOv8+Pyqt5的工地安全帽头盔佩戴检测识别系统(源码+跑通说明文件)

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;318安全帽 获取完整源码源文件7000张已标注的数据集训练好的模型配置说明文件 可有偿59yuan一对一远程操作配置环境跑通程序 效果展示&#xff08;图片检测批量检测视频检测摄像头检测&#xff09; 基于深度学习YOLOv8Pyqt…

JVM的双亲委派模型和垃圾回收机制

jvm的作用是解释执行java字节码.java的跨平台就是靠jvm实现的.下面看看一个java程序的执行流程. 1. jvm中的内存区域划分 jvm也是一个进程,进程在运行过程中,要行操作系统申请一些资源.这些内存空间就支撑了后续java程序的执行. jvm从系统申请了一大块内存,这块内存在java程序使…

影响MySql 服务性能最重要的两个参数。

不同的需求&#xff0c;不同服务器硬件配置&#xff0c;要想MySql 服务处于最优状态是需要调试一些参数的&#xff0c;可调的参数非常多&#xff0c;在看完官方的mysql的文档&#xff0c;结合以前的配置情况在这里选择影响性能最大的参数作介绍&#xff1a; 先查一下参数情况&…

吴恩达深度学习环境本地化构建wsl+docker+tensorflow+cuda

Tensorflow2 on wsl using cuda 动机环境选择安装步骤1. WSL安装2. docker安装2.1 配置Docker Desktop2.2 WSL上的docker使用2.3 Docker Destop的登陆2.4 测试一下 3. 在WSL上安装CUDA3.1 Software list needed3.2 [CUDA Support for WSL 2](https://docs.nvidia.com/cuda/wsl-…

Hive SQL必刷练习题:连续问题 间断连续(*****)

问题描述&#xff1a; 1&#xff09; 连续问题&#xff1a;找出连续三天&#xff08;或者连续几天的啥啥啥&#xff09;。 2&#xff09; 间断连续&#xff1a;统计各用户连续登录最长天数&#xff0c;间断一天也算连续&#xff0c;比如1、3、4、6也算登陆了6天 问题分析&am…

html-docx-js-typescript——将html生成docx文档

html-docx-js-typescript源码&#xff1a;GitHub - caiyexiang/html-docx-js-typescript: Convert HTML documents to docx format. html-docx-js地址&#xff1a;html-docx-js - npm *简单使用&#xff1a; 获取需要转为word文档的html节点&#xff0c;借助file-saver提供的…

判断闰年(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int year 2000;//执行循环判断&#xff1b;while (year < 2010){//执行流程&#xff1b;//判断能否整除4&#xff1…

Chrome历史版本下载地址:Google Chrome Older Versions Download (Windows, Linux Mac)

最近升级到最新版本Chrome后发现页面居然显示错乱,是在无语, 打算退回原来的版本, 又发现官方只提供最新的版本下载, 为了解决这个问题所有收集了Chrome历史版本的下载地址分享给大家. Google Chrome Windows version 32-bit VersionSizeDate104.0.5112.10279.68 MB2022-05-30…