• 沒有找到結果。

DSC SQL 语法迁移工具操作指导

3.17 管理并发写入操作

3.17.1 事务隔离说明

GaussDB(DWS)基于MVCC(多版本并发控制)并结合两阶段锁的方式进行事务管 理,其特点是读写之间不阻塞。SELECT是纯读操作,UPDATE和DELETE是读写操作。

● 读写操作和纯读操作之间并不会发生冲突,读写操作之间也不会发生冲突。每个 并发事务在事务开始时创建事务快照,并发事务之间不能检测到对方的更改。

– 读已提交隔离级别中,如果事务T1提交后,事务T2就可以看到事务T1更改的 结果。

– 可重复读级别中,如果事务T1提交事务前事务T2开始执行,则事务T1提交 后,事务T2依旧看不到事务T1更改的结果,保证了一个事务开始后,查询的 结果前后一致,不受其他事务的影响。

● 读写操作,支持的是行级锁,不同的事务可以并发更新同一个表,只有更新同一 行时才需等待,后发生的事务会等待先发生的事务提交后,再执行更新操作。

– READ COMMITTED:读已提交隔离级别,事务只能读到已提交的数据而不 会读到未提交的数据,这是缺省值。

– REPEATABLE READ: 事务只能读到事务开始之前已提交的数据,不能读到 未提交的数据以及事务执行期间其它并发事务提交的修改。

3.17.2 写入和读写操作

关于写入和读写操作的命令:

● INSERT,可向表中插入一行或多行数据。

● UPDATE,可修改表中现有数据。

● DELETE,可删除表中现有数据。

● COPY,导入数据。

INSERT和COPY是纯写入的操作。并发写入操作,需要等待,对同一个表的操作,当 事务T1的INSERT或COPY未解除锁定时,事务T2的INSERT或COPY需等待,事务T1解 除锁定时,事务T2正常继续。

UPDATE和DELETE是读写操作(先查询出要操作的行)。UPDATE和DELETE执行前需 要先查询数据,由于并发事务彼此不可见,所以UPDATE和DELETE操作是读取事务发 生前提交的数据的快照。写入操作,是行级锁,当事务T1和事务T2并发更新同一行 时,后发生的事务T2会等待,根据设置的等待时长,若超时事务T1未提交则事务T2执 行失败;当事务T1和事务T2并发更新的行不同时,事务T1和事务2都会执行成功。

3.17.3 并发写入事务的潜在死锁情况

只要事务涉及多个表的或者同一个表相同行的更新时,同时运行的事务就可能在同时 尝试写入时变为死锁状态。事务会在提交或回滚时一次性解除其所有锁定,而不会逐 一放弃锁定。 例如,假设事务T1和T2在大致相同的时间开始:

● 如果T1开始对表A进行写入且T2开始对表B进行写入,则两个事务均可继续而不会 发生冲突;但是,如果T1完成了对表A的写入操作并需要开始对表B进行写入,此 时操作的行数正好与T2一致,它将无法继续,因为T2仍保持对表B对应行的锁 定,此时T2开始更新表A中与T1相同的行数,此时也将无法继续,产生死锁,在 锁等待超时内,前面事务提交释放锁,后面的事务可以继续执行更新,等待时间 超时后,事务抛错,有一个事务退出。

● 如果T1,T2都对表A进行写入,此时T1更新1-5行的数据,T2更新6-10行的数据,

两个事务不会发生冲突,但是,如果T1完成后开始对表A的6-10行数据进行更 新,T2完成后开始更新1-5行的数据,此时两个事务无法继续,在锁等待超时内,

前面事务提交释放锁,后面的事务可以继续执行更新,等待时间超时后,事务抛 错,有一个事务退出。

3.17.4 并发写入示例

本章节以表test为例,分别介绍相同表的INSERT和DELETE并发,相同表的并发 INSERT,相同表的并发UPDATE,以及数据导入和查询的并发的执行详情。

CREATE TABLE test(id int, name char(50), address varchar(255));

3.17.4.1 相同表的 INSERT 和 DELETE 并发

事务T1:

START TRANSACTION;

INSERT INTO test VALUES(1,'test1','test123');

COMMIT;

事务T2:

START TRANSACTION;

DELETE test WHERE NAME='test1';

COMMIT;

场景1:

开启事务T1,不提交的同时开启事务T2,事务T1执行INSERT完成后,执行事务T2的 DELETE,此时显示DELETE 0,由于事务T1未提交,事务2看不到事务插入的数据;

场景2:

● READ COMMITTED级别

开启事务T1,不提交的同时开启事务T2,事务T1执行INSERT完成后,提交事务 T1,事务T2再执行DELETE语句时,此时显示DELETE 1,事务T1提交完成后,事 务T2可以看到此条数据,可以删除成功。

● REPEATABLE READ级别

开启事务T1,不提交的同时开启事务T2,事务T1执行INSERT完成后,提交事务 T1,事务T2再执行DELETE语句时,此时显示DELETE 0,事务T1提交完成后,事 务T2依旧看不到事务T1的数据,一个事务中前后查询到的数据是一致的。

3.17.4.2 相同表的并发 INSERT

事务T1:

START TRANSACTION;

INSERT INTO test VALUES(2,'test2','test123');

COMMIT;

事务T2:

START TRANSACTION;

INSERT INTO test VALUES(3,'test3','test123');

COMMIT;

场景1:

开启事务T1,不提交的同时开启事务T2,事务T1执行INSERT完成后,执行事务T2的 INSERT语句,可以执行成功,读已提交和可重复读隔离级别下,此时在事务T1中执行 SELECT语句,看不到事务T2中插入的数据,事务T2中执行查询语句看不到事务T1中插 入的数据。

场景2:

● READ COMMITTED级别

开启事务T1,不提交的同时开启事务T2,事务T1执行INSERT完成后直接提交,事 务T2中执行INSERT语句后执行查询语句,可以看到事务T1中插入的数据。

● REPEATABLE READ级别

开启事务T1,不提交的同时开启事务T2,事务T1执行INSERT完成后直接提交,事 务T2中执行INSERT语句后执行查询语句,看不到事务T1中插入的数据。

3.17.4.3 相同表的并发 UPDATE

事务T1:

START TRANSACTION;

UPDATE test SET address='test1234' WHERE name='test1';

COMMIT;

事务T2:

START TRANSACTION;

UPDATE test SET address='test1234' WHERE name='test2';

COMMIT;

事务T3:

START TRANSACTION;

UPDATE test SET address='test1234' WHERE name='test1';

COMMIT;

场景1:

开启事务T1,不提交的同时开启事务T2,事务T1开始执行UPDATE,事务T2开始执行 UPDATE,事务T1和事务T2都执行成功。更新不同行时,更新操作拿的是行级锁,不 会发生冲突,两个事务都可以执行成功。

场景2:

开启事务T1,不提交的同时开启事务T3,事务T1开始执行UPDATE,事务T3开始执行 UPDATE,事务T1执行成功,事务T3等待超时后会出错。更新相同行时,事务T1未提 交时,未释放锁,导致事务T3执行不成功。

3.17.4.4 数据导入和查询的并发

事务T1:

START TRANSACTION;

COPY test FROM '...';

COMMIT;

事务T2:

START TRANSACTION;

SELECT * FROM test;

COMMIT;

场景1:

开启事务T1,不提交的同时开启事务T2,事务T1开始执行COPY,事务T2开始执行 SELECT,事务T1和事务T2都执行成功。事务T2中查询看不到事务T1新COPY进来的数 据。

场景2:

● READ COMMITTED级别

开启事务T1,不提交的同时开启事务T2,事务T1开始执行COPY,然后提交,事 务T2查询,可以看到事务T1中COPY的数据。

● REPEATABLE READ级别

开启事务T1,不提交的同时开启事务T2,事务T1开始执行COPY,然后提交,事 务T2 查询,看不到事务T1中COPY的数据。