PostgreSQL 为什么也不建议 RR隔离级别,MySQL别笑

数据库技术
44
0
0
2024-09-10
标签   PostgreSQL

为了说这件事的公平性,公正,透明的特性,调整isolation 从源头进行改变,所以语句执行的时候,并不会在begin 上标明是什么隔离级别。这点请各位看官注意。

# only default tablespace
#check_function_bodies = on
default_transaction_isolation = 'serializable' #'read committed' 'repeatable read'
#default_transaction_read_only = off
#default_transaction_deferrable = off
#session_replication_role = 'origin'

本篇需要说明几个问题

1 PG 的Repeatable Read 在事务的隔离性方面,的确是非常的严谨,相关的严谨性方面我们可以对比 serializable 。

2 如果我们使用 RC 的情况下,会避免一些使用RR 方面的什么问题

本着这些问题,我们来开始我们的讨论

问题1:RR 和 Serializable 在PG 中,对于一些事务的处理的情况

postgres=# create table test (id int primary key,
                   name varchar(20));
                                   
insert into test (id,name) values (1,'simon');
insert into test (id,name) values (2,'kacy');
insert into test (id,name) values (3,'Tim');
insert into test (id,name) values (4,'Austin');
CREATE TABLE
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
postgres=# 
postgres=# show transaction_isolation;
 transaction_isolation 
-----------------------
 repeatable read
(1 row)

postgres=# select * from test;
 id |  name  
----+--------
  1 | simon
  2 | kacy
  3 | Tim
  4 | Austin
(4 rows)

上图中我们初始化了一个数据库的表,并且插入了4条,数据记录。

然后我们打开4个客户进程,来模拟多客户访问下的一些情况。首先我们要在 serializable 隔离级别下进行,相关的工作。我们以截图和文字的方式来进行解析。

1 serializable

session 1

postgres=# begin;
BEGIN
postgres=*# show transaction_isolation;
 transaction_isolation 
-----------------------
 serializable
(1 row)

postgres=*# update test set name = '4444' where id = 4;
UPDATE 1
postgres=*# update test set name = '333' where id = 3;
ERROR:  could not serialize access due to concurrent update
postgres=!# 

session2

postgres=# show transaction_isolation;
 transaction_isolation 
-----------------------
 serializable
(1 row)

postgres=# insert into test (id,name) values (5,'5555');
INSERT 0 1
postgres=# 

session 3

postgres=# show transaction_isolation;
 transaction_isolation 
-----------------------
 serializable
(1 row)

postgres=#  
postgres=# update test set name = '333' where id = 3;
UPDATE 1

上面是相关PostgreSQL 在SERIALIZABLE 级别上执行事务产生的结果,

ERROR: could not serialize access due to concurrent update

此时我们将,Isolation 调整为 , repeatable read 我们在尝试一次,看结果如何

1 repeatable read

session 1

postgres=# begin;
BEGIN
postgres=*# update test set name = '4444' where id = 4;
UPDATE 1

session 2

postgres=# insert into test (id,name) values (5,'5555');
INSERT 0 1

session 3

postgres=# update test set name = '333' where id = 3;
UPDATE 1

session 1

postgres=*# update test set name = '333' where id = 3;
ERROR:  could not serialize access due to concurrent update
postgres=!# 

从上图的执行过程我们得到了session 1 执行事务错误,并且报告为 could not serialize access due to concurrent update 的错误。

通过上面的实验,我们得到一个答案,在这样的事务处理中,PG 在事务操作中的反馈-- 序列化和RR 反馈是一样的。

可能一个事例不够,那么我们在做一个实例,这个实例比较简单,开四个进程,表中一条记录,分别对这条记录进行修改,最后最先进行修改的进程对这个数据进行commit, 然后观察

CREATE TABLE accounts (

id SERIAL PRIMARY KEY,

name VARCHAR(100),

balance INTEGER

);

INSERT INTO accounts (name, balance) VALUES ('Alice', 1000);

下面四个图,是repeatable read 和 serializable 两个隔离级别,在操作同样的事务操作后的结果,可以看到这两个之间没有区别。

那么我们将隔离级别降级为RC,看看做同样的事情会有什么变化

我们把事例1的操作在重新来一边

在这次操作里面,我们可以发现操作中没有失败和退回了,整体操作都是正常进行的。

那么PG的隔离级别到底有什么问题,PG数据库主要的在隔离级别方面有以下特点

1 PG的隔离级别中是不存在 Read uncommitted 的,这点和某些世界级别数据库一样,某些数据库也不存在read uncommitted.

2 PG 数据库在 RR 隔离级别方面,是非常严格实现相关隔离级别的要求,没有偷工减料,但这样得结果是,在RR 隔离级别中,将会对于高并发和热表的事务操作变得不友好,会产生更多的死锁,BLOCKED 事务失败的概率会提高不少,应用程序必须有重试的机制,或者这将劝退不少的PG初级的使用者(开发人员)

3 PG 的 RC 隔离级别,严格遵守RC的隔离级别的要求,同时在金融类的场景下,RC 完全满足使用的需求,并不需要进行RR的隔离级别强制需求,如果有一些特殊的需求,也可以通过应用程序来自行解决,这点也参见世界级别的数据库产品Oracle,他在金融系统中也是使用RC,也并未使用RR 作为默认的金融方面的隔离级别进行使用

另需要注明,如果必须使用PG 中的RR 隔离级别,则应用系统的开发者,需要注意应用中一些设计的事务的顺序,不要不同的事务操作表的顺序是逆向的(非业务要求,进行逆向设计)。这样也会避免RR 给你带来的一些事务经常失败的问题产生。

最后,MYSQL 在事务隔离级别中,为什么也不建议使用RR,结果与PG 一样都是不建议,但MYSQL 在RR 隔离级别上的问题,主要是性能问题,这里主要指的的是 间隙锁解决幻读的问题导致的在MYSQL范围查询中导致的性能问题。同时下面的图也能说明一些问题,在RR ISOLATION 的层面。

所以如果作为金融类的数据库使用,必须要严谨的情况下,并且必须使用RR 作为默认隔离级别的情况下,建议还是首先考虑POSTGRESQL 而不是......