PostgreSQL随读笔记-事务下

7. 事务处理与并发控制

7.8 锁管理机制

我们平时说的表锁、页锁、咨询锁等等(行锁除外),实际上都是常规锁根据不同锁定对象划分的子类。 例如,如果要对一个表进行操作,通常会通过heap_open打开这个表,并在打开时指定需要的锁模式。之后会有一系列函数将锁模式传递下去,最终通过LockRelationOid函数将表的Oid和lockmode联系在一起。

# define heap_open(r,l)
Relation table_open(Oid relationId, LOCKMODE lockmode)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
void LockRelationOid(Oid relid, LOCKMODE lockmode)

当然,常规锁不仅可以对表加锁,也可以对各类对象加锁。LOCKTAG确定一个对象。

pg里还有一种对于锁管理的设计:

强锁(5-8)弱锁(1-3)和fastpath
https://blog.csdn.net/Hehuyi_In/article/details/124776984

弱锁只保存在当前会话,从而避免频繁访问共享内存的主锁表,提高数据库的性能。虽然判断是否有强锁也需要访问共享内存中的FastPathStrongRelationLocks,但这种访问粒度比较小。

冲突检测:
LockCheckConflicts:
LockCheckConflicts函数主要是检查当前申请的锁模式,是否与其他事务已持有的锁冲突
如果通过冲突检测发现可以获得锁,则通过GrantLock和GrantLocalLock函数增加锁的引用计数。如果发现不能获得锁,则进入等待状态。

等待状态的判断通过WaitOnLock函数实现,调用关系是WaitOnLock函数 -> ProcSleep函数。ProcSleep函数一方面将当前事务加入等待队列,另一方面还要做死锁检测。

小结一下:
spin锁支持LwLock,Lwlock用来保护共享内存,Regular锁保护的是数据库对象,下层也会作用到一块内存中(即调用LwLock)

下面看一下锁操作:
表锁不继续了:注意的是会话锁是跨事务存在的,应用范围更广。
页粒度锁(暂不了解):
在这里插入图片描述
元组粒度(本质上行锁是由常规锁+xmax结合实现):
有两种会用到行锁:

  • 对行执行update,delete
  • 显示指定行锁 select for update/for share / for no key share / for no key update;

行锁
对应源码lockoptions.h

/*
 * Possible lock modes for a tuple.
 */
typedef enum LockTupleMode
{
	/* SELECT FOR KEY SHARE */
	LockTupleKeyShare,
	/* SELECT FOR SHARE */
	LockTupleShare,
	/* SELECT FOR NO KEY UPDATE, and UPDATEs that don't modify key columns */
	LockTupleNoKeyExclusive,
	/* SELECT FOR UPDATE, UPDATEs that modify key columns, and DELETE */
	LockTupleExclusive
} LockTupleMode;

这里需要建立常规所的映射关系:tupleLockExtraInfo

static const struct
{
	LOCKMODE	hwlock;
	int			lockstatus;
	int			updstatus;
}

			tupleLockExtraInfo[MaxLockTupleMode + 1] =
{
	{							/* LockTupleKeyShare */
		AccessShareLock,
		MultiXactStatusForKeyShare,
		-1						/* KeyShare does not allow updating tuples */
	},
	{							/* LockTupleShare */
		RowShareLock,
		MultiXactStatusForShare,
		-1						/* Share does not allow updating tuples */
	},
	{							/* LockTupleNoKeyExclusive */
		ExclusiveLock,
		MultiXactStatusForNoKeyUpdate,
		MultiXactStatusNoKeyUpdate
	},
	{							/* LockTupleExclusive */
		AccessExclusiveLock,
		MultiXactStatusForUpdate,
		MultiXactStatusUpdate
	}
};

其实这些行锁模式的兼容性是怎么来的,就是根据对应的表锁兼容性来的。例如一个delete操作,它在表上加的是3级锁,但在行上加的是8级锁。因此在表上该操作是兼容的(可以同时对一个表进行delete),但在行上是冲突的(不可以同时delete同一行)。

  • xmax设置:tuple上的事务信息
  1. MultiXactId
    通常如果只有一个事务增加行锁,那么直接将行的xmax设为事务id,并在infomask中设置对应锁类型即可。
    在这里插入图片描述
    但select…for…语句中可以加行级共享锁(普通的select不行),即可以有多个事务对一个元组加共享锁(保证别的不能更改行,只能读),这时就没法通过将行的xmax设为事务id来表示了。为此,pg将多个事务组成一个mXactCacheEnt(multixact.c文件),并为其指定唯一的MultiXactId,此时在xmax处保存的就是MultiXactId。

为了区分xmax设置的是事务id还是MultiXactId,在使用MultiXactId时会在元组上增加HEAP_XMAX_IS_MULTI标记。

ps: 可以查看tuple的xmin和xmax:

gaussdb=# create table t1(a int primary key, b int);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
CREATE TABLE
gaussdb=# insert into t1 values(1, 2);
INSERT 0 1
gaussdb=# insert into t1 values(3, 4);
INSERT 0 1
gaussdb=# select xmin, xmax, * from t1;
  xmin  | xmax | a | b 
--------+------+---+---
 219833 |    0 | 1 | 2
 220116 |    0 | 3 | 4
(2 rows)
  1. 行锁标记位
  • 如果元组的xmax是事务id,需要通过infomask标记位区分元组的加锁情况。
    在htup_details.h中
#define HEAP_XMAX_KEYSHR_LOCK	0x0010	/* for key share子句对应的锁 */
#define HEAP_XMAX_EXCL_LOCK		0x0040	/* xmax is exclusive locker,排他锁标记位 */
/* xmax is a shared locker */
#define HEAP_XMAX_SHR_LOCK	(HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMAX_LOCK_ONLY		0x0080	/* xmax, if valid, is only a locker,显式加行锁 */
#define HEAP_XMAX_IS_MULTI		0x1000	/* t_xmax is a MultiXactId */
#define HEAP_KEYS_UPDATED		0x2000	/* tuple was updated and key cols */
...
  • 如果元组的xmax是MultiXactId,则每种子句都对应一种锁模式(它们的对应关系通过tupleLockExtraInfo也可以看出来)
/*
 * Possible multixact lock modes ("status").  The first four modes are for
 * tuple locks (FOR KEY SHARE, FOR SHARE, FOR NO KEY UPDATE, FOR UPDATE); the
 * next two are used for update and delete modes.
 */
typedef enum
{
	MultiXactStatusForKeyShare = 0x00,
	MultiXactStatusForShare = 0x01,
	MultiXactStatusForNoKeyUpdate = 0x02,
	MultiXactStatusForUpdate = 0x03,
	/* an update that doesn't touch "key" columns */
	MultiXactStatusNoKeyUpdate = 0x04,
	/* other updates, and delete */
	MultiXactStatusUpdate = 0x05
} MultiXactStatus;

7.9 死锁处理机制

暂不深入

7.10 多版本并发控制

基本概念

以update为例:

  1. 元组状态类型判断:

首先看返回码:

/*
 * Result codes for table_{update,delete,lock_tuple}, and for visibility
 * routines inside table AMs.
 */
typedef enum TM_Result
{
	TM_Ok, // 成功:update/delete performed, lock was acquired
	TM_Invisible, // 元组对当前快照根本不可见,自然无法处理
	TM_SelfModified, // 元组被当前事务更新过 通常需要自增事务id解决
	TM_Updated, // 元组被已提交事务更新过, 这就说明这个tuple可能被移动到了其他地方(ctid指向)
	TM_Deleted, // 元组被已提交事务删除过
	TM_BeingModified, // 	元组正在被其他(session的)事务更新
	TM_WouldBlock // lock couldn't be acquired, action skipped. Only used by lock_tuple 
} TM_Result;
  1. 元组更新函数
    元组的操作都在heapam.c中
 * INTERFACE ROUTINES
 *		heap_beginscan	- begin relation scan
 *		heap_rescan		- restart a relation scan
 *		heap_endscan	- end relation scan
 *		heap_getnext	- retrieve next tuple in scan
 *		heap_fetch		- retrieve tuple with given tid
 *		heap_insert		- insert tuple into a relation
 *		heap_multi_insert - insert multiple tuples into a relation
 *		heap_delete		- delete a tuple from a relation
 *		heap_update		- replace a tuple in a relation with another tuple

下面分析heap_upadated:
占个坑。太多了。

解决读写冲突
一般MVCC有2种实现方法:

  • 写新数据时,把旧数据快照存入其他位置(如oracle的回滚段、sqlserver的tempdb)。当读数据时,读的是快照的旧数据。
  • 写新数据时,旧数据不删除,直接插入新数据。PostgreSQL就是使用的这种实现方法。

7.10.1 heaptuple 结构

txid是事务管理器为事务分配的一个txid,唯一标志符。
使用可以查询:

select txid_current();

三个特殊的txid

  • 0:InvalidTransactionId,表示无效的事务ID
  • 1:BootstrapTransactionId,表示系统表初始化时的事务ID,比任何普通的事务ID都旧。
  • 2:FrozenTransactionId,冻结的事务ID,比任何普通的事务ID都旧。
    大于2的事务ID都是普通的事务ID。

txid间可以相互比较大小,任何事务只可见txid<其自身txid的事务修改结果。

看一下tuple:
在这里插入图片描述

  • t_xmin:保存插入该元组的事务txid(该元组由哪个事务插入)
  • t_xmax:保存更新或删除该元组的事务txid。若该元组尚未被删除或更新,则t_xmax=0,即invalid
  • t_cid:保存命令标识(command id,cid),指在该事务中,执行当前命令之前还执行过几条sql命令(从0开始计算)
  • t_ctid:一个指针,保存指向自身或新元组的元组的标识符(tid)。

当更新该元组时,t_ctid会指向新版本元组。若元组被更新多次,则该元组会存在多个版本,各版本通过t_ctid串联,形成一个版本链。通过这个版本链,可以找到最新的版本。t_ctid是一个二元组(页号,页内偏移量),其中页号从0开始,页内偏移量从1开始。

对应结构体:

  1. DatumTupleFields
/* We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac in three
 * physical fields.  Xmin and Xmax are always really stored, but Cmin, Cmax
 * and Xvac share a field.
*/
 
typedef struct HeapTupleFields
{
	TransactionId t_xmin;		/* inserting xact ID,插入该元组的事务id */
	TransactionId t_xmax;		/* deleting or locking xact ID,删除或锁定该元组的事务id */
 
	union
	{
		CommandId	t_cid;		/* inserting or deleting command ID, or both,插入或删除(或者两种兼有)该元组的命令id */
		TransactionId t_xvac;	/* old-style VACUUM FULL xact ID,对该元组执行vacuum full操作的事务id */
	}			t_field3;
} HeapTupleFields;
  1. DatumTupleFields
typedef struct DatumTupleFields
{
	int32		datum_len_;		/* varlena header (do not touch directly!),变长header */
	int32		datum_typmod;	/* -1, or identifier of a record type,-1或者表示记录类型 */
	Oid			datum_typeid;	/* composite type OID, or RECORDOID,复合类型oid或者记录oid */
} DatumTupleFields;
  1. HeapTupleHeaderData
struct HeapTupleHeaderData
{
	union
	{
		HeapTupleFields t_heap; // 主要用于元组可见性检查
		DatumTupleFields t_datum;  
	}			t_choice;
ItemPointerData t_ctid;		
 
	/* Fields below here must match MinimalTupleData! */
 
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK2 2
	uint16		t_infomask2;	/* number of attributes + various flags */
 
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK 3
	uint16		t_infomask;		/* various flag bits, see below */
 
#define FIELDNO_HEAPTUPLEHEADERDATA_HOFF 4
	uint8		t_hoff;			/* sizeof header incl. bitmap, padding */
 
#define FIELDNO_HEAPTUPLEHEADERDATA_BITS 5
	bits8		t_bits[FLEXIBLE_ARRAY_MEMBER];	/* bitmap of NULLs,23 bytes */
 
	/* MORE DATA FOLLOWS AT END OF STRUCT */
};

字段说明:

① t_choice:具有两个成员的联合类型:

t_heap:用于记录对元组执行插入/删除操作的事务ID和命令ID,这些信息主要用于并发控制时检查元组对事务的可见性。
t_datum:当一个新元组在内存中形成的时候,我们并不关心其事务可见性,因此在t_choice中只需用DatumTupleFields结构来记录元组的长度等信息。但在把该元组插入到表文件时,需要在元组头信息中记录插入该元组的事务和命令ID,故此时会把t_choice所占用的内存转换为HeapTupleFields结构并填充相应数据后再进行元组的插入。

② t_ctid:一个指针,保存指向自身或新元组的元组的标识符(tid)。当更新该元组时,t_ctid会指向新版本元组。若元组被更新多次,则该元组会存在多个版本,各版本通过t_cid串联,形成一个版本链。

③ t_infomask2:其低11位表示当前元组的属性个数,其他位则用于包括用于HOT技术及元组可见性的标志位。

④ t_infomask:用于标识元组当前的状态,比如元组是否具有OID、是否有空属性等,t_infomask的每一位对应不同的状态,共16种状态。

⑤ t_hoff:该元组头的大小。

⑥ t_bits[]数组:标识该元组哪些字段为空。

  1. ItemIdData
typedef struct ItemIdData
{
	unsigned	lp_off:15,		/* offset to tuple (from start of page),元组在页面的偏移量 */
				lp_flags:2,		/* state of line pointer, see below,元组的line pointer状态,参考下面 */
				lp_len:15;		/* byte length of tuple,元组长度 */
} ItemIdData;
 
typedef ItemIdData *ItemId;

7.10.2 快照和隔离级别

快照是记录数据库当前瞬时状态的一个数据结构。pg的快照主要保存:当前所有活跃事务的最小、最大事务ID、当前活跃事务列表、commandID等。

快照可以分为多种类型,每种快照类型都对应了一种判断元组可见性的方法。

  1. 快照结构体
typedef struct SnapshotData *Snapshot;
 
typedef struct SnapshotData
{
	SnapshotType snapshot_type; /* type of snapshot,快照类型 */
 
	/* 用于MVCC控制	 */
	TransactionId xmin;			/* all XID < xmin are visible to me,若事务ID小于xmin,则对当前事务可见 */
	TransactionId xmax;			/* all XID >= xmax are invisible to me,若事务ID大于xmax,则对当前事务不可见*/
 
	/* 快照生成时对应的活跃事务列表	 */
	TransactionId *xip;
	uint32		xcnt;			/* # of xact ids in xip[],xip数组大小 */
 
	/*  活跃的子事务列表	 */
	TransactionId *subxip;
	int32		subxcnt;		/* # of xact ids in subxip[],sub xip数组大小*/
	bool		suboverflowed;	/* has the subxip array overflowed? 当子事务过多时,数组有可能overflow */
 
	bool		takenDuringRecovery;	/* recovery-shaped snapshot? */
	bool		copied;			/* false if it's a static snapshot,如果是静态快照则为false */
 
	CommandId	curcid;			/* in my xact, CID < curcid are visible */
 
	/*
	 * An extra return value for HeapTupleSatisfiesDirty, not used in MVCC
	 * snapshots.,HeapTupleSatisfiesDirty使用的一个变量,在判断可见性的同时,会借助这个变量将元组上的speculativeToken返回给上层
	 */
	uint32		speculativeToken;
 
	/*
	 * For SNAPSHOT_NON_VACUUMABLE (and hopefully more in the future) this is
	 * used to determine whether row could be vacuumed.元组是否可vacuum(用于SNAPSHOT_NON_VACUUMABLE状态)
	 */
	struct GlobalVisState *vistest;
 
	/*
	 * Book-keeping information, used by the snapshot manager
	 */
	uint32		active_count;	/* refcount on ActiveSnapshot stack */
	uint32		regd_count;		/* refcount on RegisteredSnapshots */
	pairingheap_node ph_node;	/* link in the RegisteredSnapshots heap */
 
	TimestampTz whenTaken;		/* timestamp when snapshot was taken,快照生成时间 */
	XLogRecPtr	lsn;			/* position in the WAL stream when taken,快照生成时事务REDO日志的LSN*/
 
	/*
	 * The transaction completion count at the time GetSnapshotData() built
	 * this snapshot. Allows to avoid re-computing static snapshots when no
	 * transactions completed since the last GetSnapshotData().
	 */
	uint64		snapXactCompletionCount;
} SnapshotData;
 
#endif							/* SNAPSHOT_H */
  1. 主要字段含义:

快照中最重要的xmin, xmax,xip,用来表示当前系统中所有事务所处状态。

testdb=# SELECT txid_current_snapshot();
 
txid_current_snapshot
-----------------------
 100:104:100,102
(1 row)

这3个值就是 xmin:xmax:xip[]数组,注意不要与元组的t_xmin和t_xmax弄混

Snapshot->xmin:当前所有活跃事务中最小的事务txid(所以只有一个事务的时候就是自己本身),所有比它更早的事务(xid<xmin),要么已提交要么已回滚,因此均可见。
Snapshot->xmax:Snapshot->xmax=latestCompletedXid+1,latestCompletedXid记录的是最大的已提交事务id,因此Snapshot->xmax就比所有已提交事务的事务id都要大。如果某个事务xid>= Snapshot->xmax,说明在快照创建时这个事务还是活跃事务(或尚未启动)因此其结果对当前事务不可见。
xip[]数组:快照创建时,所有活跃事务的事务id列表,该列表仅包含[xmin,xmax)范围内的活跃事务xid。

例子:

在这里插入图片描述
100💯 含义如下

xmin=100,所以txid<100的事务均不活跃
xmax=100,所以txid>=100的事务均活跃或未启动
xip_list为空,表示[xmin,xmax)范围内无活跃事务
100:104:100,102含义如下

xmin=100,所以txid<100的事务均不活跃
xmax=104,所以txid>=104的事务均活跃或未启动
xip_list为100,102,表示[xmin,xmax)范围内100,102为活跃事务

  1. 快照和隔离级别

pg会根据不同隔离级别设置,获取不同时刻的快照:

  • 已提交读(只会读到commited的数据):在该事务的每条SQL执行之前都会重新获取一次快照
  • 可重复读和可串行化(保证一个事务中两次读到的东西一样):该事务只在第一条SQL执行之前获取一次快照

在这里插入图片描述
注意这里Transaction C这里是可重复读,所以所有sql的快照都是一样的。

查看数据库的隔离级别:

SELECT name, setting FROM pg_settings WHERE name ='default_transaction_isolation';
-- 或
SELECT current_setting('default_transaction_isolation');

在这里插入图片描述
4, 快照类型

就是有的快照不是用于常规的MVCC判断的,有一些特殊的snapshot有其他的可见性规则。

typedef enum SnapshotType
{
	SNAPSHOT_MVCC = 0, 
	SNAPSHOT_SELF,	
    SNAPSHOT_ANY,
	SNAPSHOT_TOAST,
	SNAPSHOT_DIRTY,
    SNAPSHOT_HISTORIC_MVCC,
	SNAPSHOT_NON_VACUUMABLE
} SnapshotType;

各类型快照可见性判断条件:
在这里插入图片描述

7.10.3 快照管理和获取

地址:snapmgr.c

快照管理器管理快照按照两种方式:“registered” by resowner.c和active snapshot栈。
它们中的所有快照都保存在持久内存中。当快照不再在这些列表中时(由每个快照上的单独引用计数跟踪),可以释放其内存。

两个静态的快照:

static SnapshotData CurrentSnapshotData = {SNAPSHOT_MVCC};
static SnapshotData SecondarySnapshotData = {SNAPSHOT_MVCC};
SnapshotData CatalogSnapshotData = {SNAPSHOT_MVCC};
SnapshotData SnapshotSelfData = {SNAPSHOT_SELF};
SnapshotData SnapshotAnyData = {SNAPSHOT_ANY};

/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
static Snapshot SecondarySnapshot = NULL;
static Snapshot CatalogSnapshot = NULL;
static Snapshot HistoricSnapshot = NULL;

CurrentSnapshot指向在事务快照模式(SNAPSHOT_MVCC类型)下的唯一快照,是reed-commited的最新快照。SecondarySnapshot是一个截至当前时刻始终最新的快照,即使在事务快照方式下也是如此。它只能用于特殊目的代码(例如RI检查)。CatalogSnapshot指向用于系统表扫描的MVCC快照;每当发生系统表更改时,我们都必须使其无效。

  • Snapshot GetTransactionSnapshot函数:为事务中的新查询返回合适的快照。

啥是合适的?

取决于隔离级别,读已提交肯定是最新的,可重复读那肯定是事务开始的时候获取的。这里还有一个变量:

  • FirstSnapshotSet初始值是false(尚无快照或者不是事务的第一个快照):这是为了加速。
    GetTransactionSnapshot函数中,通过FirstSnapshotSet标志来判断当前要获得的是不是事务的第一个快照。如果是,则通过GetSnapshotData获得快照并将快照缓存。在已提交读隔离级别下,直接返回获得的快照;在可重复读及串行化隔离级别下,返回缓存的快照。

这些变量对应gaussdb都保存在u_sess->utils_cxt中(会话级的)。
u_sess->utils_cxt保存了自己获取的snapshot的信息。

在GetTransactionSnapshot中,如果获得当前最新快照,需要从smgr,也就是快照管理器中获取:GetSnapshotData(对应gaussdb的proc_array_get_snapshot_data,pg内部是一个堆在维护所有事务产生的快照,这很合理,因为事务访问只会访问currentsnapshot(top))

过程先略

7.10.4 heap 可见性判断 (这里其实很重要)

  • 可见情况:

在创建快照时所有已提交的事务
本事务之前执行的命令

  • 不可见情况:

在创建快照时尚活跃的事务
在创建快照后启动的事务
当前命令造成的变化(changes made by the current command)

  • HeapTupleSatisfiesMVCC
    主要根据xmin,xmax以及tuple的t_infomask标志位查看元组的提交/未提交,删除/未删除,事务的第几条命令提交的等。

gs里在 heapam_visibility.cpp

 * Summary of visibility functions:
 *
 *    visibility_heap_tuple_satisfies_mvcc()
 *        visible to supplied snapshot, excludes current command
 *    visibility_heap_tuple_satisfies_now()
 *        visible to instant snapshot, excludes current command
 *    visibility_heap_tuple_satisfies_update()
 *        like visibility_heap_tuple_satisfies_now(), but with user-supplied command
 *        counter and more complex result
 *    visibility_heap_tuple_satisfies_self()
 *        visible to instant snapshot and current command
 *    visibility_heap_tuple_satisfies_dirty()
 *        like visibility_heap_tuple_satisfies_self(), but includes open transactions
 *    visibility_heap_tuple_satisfies_vacuum()
 *        visible to any running transaction, used by VACUUM
 *    visibility_heap_tuple_satisfies_toast()
 *        visible unless part of interrupted vacuum, used for TOAST
 *    visibility_heap_tuple_satisfies_any()
 *        all tuples are visible

其他还有一些函数待补充:

register push等

7.11 日志管理

事务状态

pg定义了四种事务状态——IN_PROGRESS, COMMITTED, ABORTED和SUB_COMMITTED,其中SUB_COMMITTED状态用于子事务。

#define TRANSACTION_STATUS_IN_PROGRESS		0x00
#define TRANSACTION_STATUS_COMMITTED		0x01
#define TRANSACTION_STATUS_ABORTED		0x02
#define TRANSACTION_STATUS_SUB_COMMITTED	0x03

四种事务状态仅需两个bit即可记录。以一个块8KB为例,可以存储8KB*8/2 = 32K个事务的状态。内存中缓存CLOG的buffer 大小为Min(128,Max(4,NBuffers/512))。

这块略复杂,先放一下,占坑。

事务实践

进程:

1.查看数据库的进程。

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

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

例如:

SELECT  procpid  FROM pg_stat_activity WHERE datname='数据库ID' and waiting ='t';

查看本身进程:

select pg_backend_pid();

查看当前事务:

select txid_current();

查看事务当前快照:

SELECT txid_current_snapshot();

2.杀掉进程。

kill有两种方式,第一种是:

SELECT pg_cancel_backend(PID);

这种方式只能kill select查询,对update、delete 及DML不生效)

第二种是:

SELECT pg_terminate_backend(PID);

这种可以kill掉各种操作(select、update、delete、drop等)操作

例子:
三个会话:

  • 会话1:
gaussdb=# begin;
BEGIN
gaussdb=# select pg_backend_pid();
 pg_backend_pid  
-----------------
 139735972443904
(1 row)

gaussdb=# select txid_current();
 txid_current 
--------------
        17526
(1 row)

gaussdb=# update t1 set a = 2 where a = 1;
UPDATE 1

此时没有commit;

  • 会话2:
gaussdb=# begin;
BEGIN
gaussdb=# select pg_backend_pid();
 pg_backend_pid  
-----------------
 139735735465728
(1 row)

gaussdb=# 
gaussdb=# select txid_current();
 txid_current 
--------------
        17529
(1 row)

gaussdb=# update t1 set a = 2 where a = 1;
  • 会话3:
select locktype as lc,relation::regclass as relname,page||','||tuple as ctid,virtualxid ,transactionid as txid,virtualtransaction,pid,mode,granted from pg_locks where pid = 139735972443904;
      lc       | relname  | ctid | virtualxid | txid  | virtualtransaction |       pid       |       mode       | granted 
---------------+----------+------+------------+-------+--------------------+-----------------+------------------+---------
 relation      | t1_pkey  | ,    |            |       | 14/1423            | 139735972443904 | RowExclusiveLock | t
 relation      | t1       | ,    |            |       | 14/1423            | 139735972443904 | RowExclusiveLock | t
 virtualxid    |          | ,    | 14/1423    |       | 14/1423            | 139735972443904 | ExclusiveLock    | t
 transactionid |          | ,    |            | 17526 | 14/1423            | 139735972443904 | ExclusiveLock    | t
(4 rows)
gaussdb=# select locktype as lc,relation::regclass as relname,page||','||tuple as ctid,virtualxid ,transactionid as txid,virtualtransaction,pid,mode,granted from pg_locks where pid = 139735735465728;
      lc       | relname | ctid | virtualxid | txid  | virtualtransaction |       pid       |        mode         | granted 
---------------+---------+------+------------+-------+--------------------+-----------------+---------------------+---------
 relation      | t1_pkey | ,    |            |       | 15/36              | 139735735465728 | AccessShareLock     | t
 relation      | t1_pkey | ,    |            |       | 15/36              | 139735735465728 | RowExclusiveLock    | t
 relation      | t1      | ,    |            |       | 15/36              | 139735735465728 | AccessShareLock     | t
 relation      | t1      | ,    |            |       | 15/36              | 139735735465728 | RowExclusiveLock    | t
 virtualxid    |         | ,    | 15/36      |       | 15/36              | 139735735465728 | ExclusiveLock       | t
 tuple         | t1      | 0,1  |            |       | 15/36              | 139735735465728 | AccessExclusiveLock | t
 transactionid |         | ,    |            | 17526 | 15/36              | 139735735465728 | ShareLock           | f
 transactionid |         | ,    |            | 17529 | 15/36              | 139735735465728 | ExclusiveLock       | t
(8 rows)

几点:

  1. 每个事务是获得自己virtualxid和transactionid的ExclusiveLock锁的;
  2. 当某个进程发现需要等待另一个事务结束时,它还会尝试在另一个事务ID上加上share锁(可能是虚拟事务ID,也可能是永久事务ID)。仅当另一个事务终止并释放锁时,操作才会成功。即第二条sql的第7条数据,这里的grant是f,即还没获取到,因为会话1没有commit。
好的,我可以和你分享一些关于 PostgreSQL 的学习笔记。 1. PostgreSQL 是什么? PostgreSQL 是一种开源的关系型数据库管理系统,它支持 SQL 语言和 ACID 事务处理,并且具有丰富的特性和扩展性,可用于大型企业应用、Web 应用和嵌入式应用。 2. PostgreSQL 的特点 PostgreSQL 具有以下特点: - 开源免费 - 支持 ACID 事务处理 - 支持 SQL 语言 - 支持复杂查询和数据类型 - 支持存储过程和触发器 - 可以扩展和定制 - 可以跨平台运行 3. PostgreSQL 的安装 在 Windows 平台下,可以从官网下载 Windows 安装程序进行安装。在 Linux 平台下,可以使用包管理器安装,例如 Ubuntu 可以使用以下命令进行安装: ``` sudo apt-get install postgresql ``` 4. PostgreSQL 的基本操作 创建数据库: ``` CREATE DATABASE databasename; ``` 创建表: ``` CREATE TABLE tablename ( column1 datatype, column2 datatype, column3 datatype, .... ); ``` 插入数据: ``` INSERT INTO tablename (column1, column2, column3, ...) VALUES (value1, value2, value3, ...); ``` 查询数据: ``` SELECT column1, column2, ... FROM tablename WHERE condition; ``` 更新数据: ``` UPDATE tablename SET column1 = value1, column2 = value2, ... WHERE condition; ``` 删除数据: ``` DELETE FROM tablename WHERE condition; ``` 5. PostgreSQL 的扩展 PostgreSQL 可以通过扩展来增强其功能,例如: - PostGIS:空间数据处理扩展 - PL/Python:Python 语言扩展 - pgAdmin:可视化管理工具 - pgpool-II:连接池和负载均衡工具 以上就是一些关于 PostgreSQL 的基本学习笔记,希望对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值