Postgresql锁机制详解(表锁和行锁)

表锁

LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ]

lockmode包括以下几种:

ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE| SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE

LOCK TABLE命令用于获取一个表锁,获取过程将阻塞一直到等待的锁被其他事务释放。如果使用NOWAIT关键字则如果获取不到锁,将不会等待而是直接返回,放弃执行当前指令并抛出一个错误(error)。一旦获取到锁,将一直持有锁直到事务结束。(没有主动释放锁的命令,锁总是会在事务结束的时候被释放)。

当使用自动获取锁的模式的时候,PostgreSQL总是尽可能地使用限制最小的模式。LOCK TABLE命令使我们可以自己定义锁的限制大小。比如一个应用程序使用事务在读提交(Read Committed isolation level)模式下需要保证数据库的数据在事务期间保持稳定,于是可以使用SHARE锁模式在读取前对表进行加锁。这可以防止并发的数据改变并且可以保证后续的事务对这个表的读取不会读到没有提交的数据,因为SHARE锁和由写入事务持有的ROW EXCLUSIVE锁是冲突的,所以对于想要使用SHARE锁对表进行加锁的事务,将会等到它之前所有持有该表的ROW EXCLUSIVE锁的事务commit或者是roll back。因此,一旦获取了表的SHARE锁,将不会有没有提交的数据,同样也不会有其他事务能够对表数据进行改变,直到当前事务释放SHARE锁。

为了在REPEATABLE READ(重复读)模式和SERIALIZABLE(序列化)模式下实现同样的效果,必须在任何查询和修改语句之前加上LOCK TABLE。在执行第一句SELECT语句或者修改数据语句前,重复读和序列化模式中一个事务的的数据视图将会被存储为快照。在这种情况下,事务申明的表锁同样可以避免并发的修改,但是并不能保证该事务能够读取到最新提交的数据。

如果一个事务想要修改表中的数据,应该使用SHARE ROW EXCLUSIVE(共享行排他)锁而不是SHARE锁。共享行排他锁将能够保证在同一时间只有当前事务能够运行。不加这个锁的话可能会造成死锁:两个事务同时想要获取SHARE锁,并且接下来又想要同时获取ROW EXCLUSIVE锁去进行数据更新(注意:同一个事务获取的两种不同的锁不会造成冲突,所以对于同一个事务,它可以在获取SHARE锁之后再次获取ROW EXCLUSIVE,当然是在没有其他事务获取SHARE锁的情况下)。为了避免死锁,应该保证所有的事务获取同一对象的锁的顺序是一致的,同时如果在同一个对象上想要获取多个锁,则总是应该先获取限制最大的锁。

ACCESS SHARE(访问共享锁)

只与ACCESS EXCLUSIVE锁冲突。

SELECT命令会在当前查询的表上获取一个ACCESS SHARE锁。总的来说,任何只读操作都会获取该锁。

ROW SHARE(行共享锁)

和EXCLUSIVE锁和ACCESS EXCLUSIVE锁冲突。

SELECT FOR UPDATE或者SELECT FOR SHARE命令会在目标表上获取该锁,并且所有被引用但是没有FOR UPDATE的表上会加上ACCESS SHARED锁。

ROW EXCLUSIVE(行排他锁)

和SHARE,SHARE ROW EXCLUSIVE和ACCESS EXCLUSIVE锁冲突。

UPDATE,DELETE和INSERT会在目标表上获取该锁,总的来说,任何对数据库数据进行修改的命令会获取到该锁。

SHARE UPDATE EXCLUSIVE(共享更新排他锁)

和SHARE UPDATE EXCLUSIVE,SHARE ROW EXCLUSIVE,EXCLUSIVE和ACCESS EXCLUSIVE冲突,该锁可以保护表防止并发的(schema)改变和VACUUM(释放空间)命令。

VACUUM,ANALYZE,CREATE INDEX CONCURRENTLY和ALTER TABLE VALIDATE以及其他ALTER TABLE类的命令会获取该锁。

SHARE(共享锁)

和ROW EXCLUSIVE,SHARE UPDATE EXCLUSIVE,SHARE ROW EXCLUSIVE,EXCLUSIVE和ACCESS EXCLUSIVE锁冲突。该锁保护一个表防止并发的数据改变。

由CREATE INDEX命令获得。

SHARE ROW EXCLUSIVE(行共享排他锁)

和ROW EXCLUSIVE,SHARE UPDATE EXCLUSIVE,SHARE,SHARE ROW EXCLUSIVE,EXCLUSIVE以及ACCESS EXCLUSIVE锁冲突,该锁用于保护一个表防止并发的数据改变,同时是自排他的,所以在同一时间只有同一个session可以持有该锁。

该锁不会被PGSQL的任何命令自动获取。

EXCLUSIVE(排它锁)

和ROW SHARE,ROW EXCLUSIVE,SHARE UPDATE EXCLUSIVE,SHARE,SHARE ROW EXCLUSIVE,EXCLUSIVE和ACCESS EXCLUSIVE锁冲突。该锁只允许并发的ACCESS SHARE锁,只有只读操作能在一个事务持有排他锁的时候进行并发操作。

ACCESS EXCLUSIVE(访问排他锁)

和所有的锁都冲突,该锁保证只有持有锁的事务能够访问当前表。

被DROP TABLE,TRUNCATE,REINDEX,CLUSTER,VACUUM FULL和REFRESH MATERIALIZED VIEW命令自动获取。有很多种形式的ALTER TABLE命令可以获取该锁,它同样也是LOCK TABLE命令默认的锁级别。

只有ACCESS EXCLUSIVE锁可以防止一个SELECT语句。

注意

一段获取锁,只有当事务结束的时候才会释放,但是如果一个锁是在一个savepoint(保存点)之后被获取,则当这个保存点回滚的时候这个锁会被马上释放。

行锁

除了表锁,PgSQL还提供了行锁。一个事务可以获取相互冲突的两种行锁,包括在子事务中,但是两个事务不能同时在同一行获取相互冲突的两种锁。

FOR UPDATE

FOR UPDATE锁使得SELECT语句可以获取行锁用于更新数据。这使得该行可以防止被其他的事务获取锁或者进行更改删除操作,也就是说其他事务的操作会被阻塞直到当前事务结束;同样的,SELECT FOR UPDATE命令会等待直到前一个事务结束。在REPEATABLE模式或者SERIALIZABLE模式下,如果一个将要被上锁的行在事务开始之前被删除了,则会返回一个error。

FOR UPDATE锁同样可以被DELETE命令获取,以及UPDATE命令当使用在确定的行用来修改数据的时候也会获取到该锁。目前当使用确定的唯一索引时使用UPDATE命令可以获取到该锁(部分索引和联合索引暂时不支持),但是未来可能会改变这种设计。

FOR NO KEY UPDATE

和FOR UPDATE命令类似,但是对于获取锁的要求更加宽松一些,在同一行中不会阻塞SELECT FOR KEY SHARE命令。同样在UPDATE命令的时候如果没有获取到FOR UPDATE锁的情况下会获取到该锁。

FOR SHARE

和FOR NO KEY UPDATE命令类似,不同点在于这个锁是一个共享锁而不是之前的锁一样是排他锁,所以这个锁会阻塞UPDATE,DELETE,SELECT FOR UPDATE或者SELECT FOR NO KEY UPDATE,但是不会阻塞SELECT FOR SHARE或者SELECT FOR KEY SHARE。

FOR KEY SHARE

和FOR SHARE表现类似,但是对加锁的要求更加宽松,SELECT FOR UPDATE会被阻塞但是SELECT FOR NO KEY UPDATE不会被阻塞。KEY SHARE模式下的锁会阻塞其他事务的DELETE或者是改变KEY值的UPDATE语句,但是对于其他的UPDATE或者是SELECT FOR NO KEY UPDATE,SELECT FOR SHARE以及SELECT FOR KEY SHARE则不会阻塞。

补充:Postgresql死锁的处理

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

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

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

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

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

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

2.将进程杀掉。

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

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

如果pg_stat_activity 没有记录,则查询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()函数也可以杀掉进程。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • postgresql插入后返回id的操作

    如下所示: 补充:PostgreSQL中执行insert同时返回插入的那行数据 通过使用语句: INSERT INTO tab1 ... RETURNING *; 以上这篇postgresql插入后返回id的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • postgreSQL数据库 实现向表中快速插入1000000条数据

    不用创建函数,直接向表中快速插入1000000条数据 create table tbl_test (id int, info text, c_time timestamp); insert into tbl_test select generate_series(1,100000),md5(random()::text),clock_timestamp(); select count(id) from tbl_test; --查看个数据条数 补充:postgreSQL 批量插入10000条数据

  • 查看postgresql数据库用户系统权限、对象权限的方法

    PostgreSQL简介 PostgreSQL是一种特性非常齐全的自由软件的对象-关系型数据库管理系统(ORDBMS),是以加州大学计算机系开发的POSTGRES,4.2版本为基础的对象关系型数据库管理系统.POSTGRES的许多领先概念只是在比较迟的时候才出现在商业网站数据库中.PostgreSQL支持大部分的SQL标准并且提供了很多其他现代特性,如复杂查询.外键.触发器.视图.事务完整性.多版本并发控制等.同样,PostgreSQL也可以用许多方法扩展,例如通过增加新的数据类型.函数.操作符

  • postgresql 实现数据的导入导出

    最近想把服务器上的测试数据库数据导到我本地的电脑上,本地电脑数据库是安装在windows系统下 之前没使用过pgsql,网上找了点资料,记入如下: 一,首先把服务器上的数据进行备份 pg_dump -U 用户名 数据库名 (-t 表名)> 数据存放路径 二,把.sql 文件下载到本地之后,首先切换到pgsql路径下的bin目录 然后执行这条命令: -d:数据库名 -h:地址 -p:端口 -u:用户名 -f:sql文件路径 之后输入口令: 这样就可以了! 补充:Sqoop从PostgreSQL导入

  • Python操作PostgreSql数据库的方法(基本的增删改查)

    Python操作PostgreSql数据库(基本的增删改查) 操作数据库最快的方式当然是直接用使用SQL语言直接对数据库进行操作,但是偶尔我们也会碰到在代码中操作数据库的情况,我们可能用ORM类的库对数控库进行操作,但是当需要操作大量的数据时,ORM的数据显的太慢了.在python中,遇到这样的情况,我推荐使用psycopg2操作postgresql数据库 psycopg2 官方文档传送门: http://initd.org/psycopg/docs/index.html 简单的增删改查 连接

  • PostgreSQL function返回多行的操作

    1. 建表 postgres=# create table tb1(id integer,name character varying); CREATE TABLE postgres=# postgres=# insert into tb1 select generate_series(1,5),'aa'; INSERT 0 5 2. 返回单字段的多行(returns setof datatype) 不指定out参数,使用return next xx: create or replace fun

  • 基于postgresql行级锁for update测试

    创建表: CREATE TABLE db_user ( id character varying(50) NOT NULL, age integer, name character varying(100), roleid character varying, CONSTRAINT db_user_pkey PRIMARY KEY (id) ) 随便插入几条数据即可. 一.不加锁演示 1.打开一个postgreSQL的SQL Shell或pgAdmin的SQL编辑器窗口,执行: begin; s

  • postgreSql分组统计数据的实现代码

    1. 背景 比如气象台的气温监控,每半小时上报一条数据,有很多个地方的气温监控,这样数据表里就会有很多地方的不同时间的气温数据 2. 需求: 每次查询只查最新的气温数据按照不同的温度区间来分组查出,比如:高温有多少地方,正常有多少地方,低温有多少地方 3. 构建数据 3.1 创建表结构: -- DROP TABLE public.t_temperature CREATE TABLE public.t_temperature ( id int4 NOT NULL GENERATED ALWAYS

  • Postgresql锁机制详解(表锁和行锁)

    表锁 LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ] lockmode包括以下几种: ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE| SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE LOCK TABLE命令用于获取一个表锁,获取过程将阻塞一直

  • Java并发编程之显式锁机制详解

    我们之前介绍过synchronized关键字实现程序的原子性操作,它的内部也是一种加锁和解锁机制,是一种声明式的编程方式,我们只需要对方法或者代码块进行声明,Java内部帮我们在调用方法之前和结束时加锁和解锁.而我们本篇将要介绍的显式锁是一种手动式的实现方式,程序员控制锁的具体实现,虽然现在越来越趋向于使用synchronized直接实现原子操作,但是了解了Lock接口的具体实现机制将有助于我们对synchronized的使用.本文主要涉及以下一些内容: 接口Lock的基本组成成员 可重入锁Re

  • MySQL锁(表锁,行锁,共享锁,排它锁,间隙锁)使用详解

    锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一种供许多用户共享访问的资源.如何保证数据并发访问的一致性.有效性,是所有数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素.从这一角度来说,锁对于数据库而言就显得尤为重要. MySQL锁 相对于其他的数据库而言,MySQL的锁机制比较简单,最显著的特点就是不同的存储引擎支持不

  • 详解Java中的悲观锁与乐观锁

    一.悲观锁 悲观锁顾名思义是从悲观的角度去思考问题,解决问题.它总是会假设当前情况是最坏的情况,在每次去拿数据的时候,都会认为数据会被别人改变,因此在每次进行拿数据操作的时候都会加锁,如此一来,如果此时有别人也来拿这个数据的时候就会阻塞知道它拿到锁.在Java中,Synchronized和ReentrantLock等独占锁的实现机制就是基于悲观锁思想.在数据库中也经常用到这种锁机制,如行锁,表锁,读写锁等,都是在操作之前先上锁,保证共享资源只能给一个操作(一个线程)使用. 由于悲观锁的频繁加锁,

  • Java编程实现排他锁代码详解

    一 .前言 某年某月某天,同事说需要一个文件排他锁功能,需求如下: (1)写操作是排他属性 (2)适用于同一进程的多线程/也适用于多进程的排他操作 (3)容错性:获得锁的进程若Crash,不影响到后续进程的正常获取锁 二 .解决方案 1. 最初的构想 在Java领域,同进程的多线程排他实现还是较简易的.比如使用线程同步变量标示是否已锁状态便可.但不同进程的排他实现就比较繁琐.使用已有API,自然想到 java.nio.channels.FileLock:如下 /** * @param file

  • Java多线程之显示锁和内置锁总结详解

    总结多线程之显示锁和内置锁 Java中具有通过Synchronized实现的内置锁,和ReentrantLock实现的显示锁,这两种锁各有各的好处,算是互有补充,这篇文章就是做一个总结. *Synchronized* 内置锁获得锁和释放锁是隐式的,进入synchronized修饰的代码就获得锁,走出相应的代码就释放锁. synchronized(list){ //获得锁 list.append(); list.count(); }//释放锁 通信 与Synchronized配套使用的通信方法通常

  • Hibernate悲观锁和乐观锁实例详解

    本文研究的主要是Hibernate悲观锁和乐观锁的全部内容,具体介绍如下. 悲观锁 悲观锁通常是由数据库机制实现的,在整个过程中把数据锁住(查询时),只要事物不释放(提交/回滚),那么任何用户都不能查看或修改. 下面我们通过一个案例来说明. 案例:假设货物库存为1000,当核算员1取出了数据准备修改,但临时有事,就走了.期间核算员2取出了数据把数量减去200,然后核算员1回来了把刚才取出的数量减去200,这就出现了一个问题,核算员1并没有在800的基础上做修改.这就是所谓的更新丢失,采用悲观锁可

  • 详解Java中的ReentrantLock锁

    ReentrantLock锁 ReentrantLock是Java中常用的锁,属于乐观锁类型,多线程并发情况下.能保证共享数据安全性,线程间有序性 ReentrantLock通过原子操作和阻塞实现锁原理,一般使用lock获取锁,unlock释放锁, 下面说一下锁的基本使用和底层基本实现原理,lock和unlock底层 lock的时候可能被其他线程获得所,那么此线程会阻塞自己,关键原理底层用到Unsafe类的API: CAS和park 使用 java.util.concurrent.locks.R

  • 程序猿必须要掌握的多线程安全问题之锁策略详解

    一.常见的锁策略 1.1 乐观锁 乐观锁:乐观锁假设认为数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正 式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如 何去做.乐观锁的性能比较高. 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会 上锁,这样别人想拿这个数据就会阻塞直到它拿到锁. 悲观锁的问题:总是需要竞争锁,进而导致发生线程切换,挂起其他线程:所以性能不高. 乐观锁的问题:并不总是能处理

  • Java 同步锁(synchronized)详解及实例

    Java 同步锁(synchronized)详解及实例 Java中cpu分给每个线程的时间片是随机的并且在Java中好多都是多个线程共用一个资源,比如火车卖票,火车票是一定的,但卖火车票的窗口到处都有,每个窗口就相当于一个线程,这么多的线程共用所有的火车票这个资源.如果在一个时间点上,两个线程同时使用这个资源,那他们取出的火车票是一样的(座位号一样),这样就会给乘客造成麻烦.比如下面程序: package com.pakage.ThreadAndRunnable; public class Ru

随机推荐