基于某云上功能需求,最近实现了类似于MySQL global read only的功能。PG的read only功能,也不再需要通过重起PG实现来实现。直接可以online更改PG实例级别
global read only 和 global read write功能, 以达到快速实现主备切换的功能。大大缩短了主备切换时间,提高了PG的高可性。弥补了PG在这一功能上的不足。
此次通过源码定制更改实现的PG版本global read only有许多明显的优势:
1.在设置global read only时,新的查询不会被堵
新进来session,read only直接生效,不需要重起PG实例。并用在设置global read only时,不会堵住新会话。因此避免了连接拥堵现象。
2.正在跑的事务,分级别对待
a.如果是事务块,也就是用户发起的”BEGIN”语句,那么已经执行完毕的语句不受影响。对于此事务中后面执行的语句,会受read only约束,不能执行DML操作。本事务会被终止。
b.如果是正在跑的语句,比如说有一个大的insert或者update。那么此操作不会受影响,设置 global read only需要等待此操作完成后,才返回。
global read only操作会等待所有running 的DML操作完成,并且做完”Immediate Checkpoint”后,再返回响应。这样做的理由是为了确保数据库状态的一致性。
尤其是在主备切换的情况下,更为关键和重要。
3.中断处理
如果在设置 global read only时失败,那么会被回滚,会被重新置为read write状态。
下面展示下源码patch实现结果:
session 1:查看当前数据库read only状态,显示当前为”Read Write”状态,即PG实例级别可以读写。
session 2:起事务,事务中为两个insert语句。我们先执行一个insert语句,但是不提交事务。
表创建语句:
create table grl_test (id int);
begin;
insert into grl_test values(1);
session 1:尝试将PG实例设置为global read only。此时可以看到,不能设置为”Read Only“状态,设置”Read Only”操作没有返回。
原因为 session 2并没有提交。这个符合我们的设计初衷,就是read only设置成功返回时,数据库为一致状态,此后没有user 级别写事务。
session 2:尝试发起第二条insert语句。可以看到insert失败了。原因为session 1尝试设置为 global read only时,虽然操作没有返回,
但是新的任何DML操作以及新的事务已经被约束为read only状态。不允许新的写事务容易理解,但是为什么不允许之前已经发起的事务中,不能DML操作呢?
这样做的原因是:
我们不想让先于设置read only之前的事务块,无限制的跑下去。这会让设置global read only的操作一直进行下去。如果read only设置不成功,
直接影响到主备的快速切换。细心的同学可能会发现,这只限于事务块。的确如此,事务块与一般事务的区别,请见我另外一篇文章”PostgreSQL 事务模型介绍“。
因为一般事务,只在command级别,跑完就结束了。我们可以等待,一般用户也容易理解,如果我们强行终止此类操作,会对应用影响比较大。一般command
级别的事务总是非常快的结束,尤其在OLTP系统中,基本上都是简单的command级别事务。事务块一般逻辑比较复杂,有这一限制,也是为了数据一致性考虑,
失败了,顶多重新跑就行了。总体上来讲,这也是基于目前市面上OLTP类应用系统的现实需求而定制的。
insert into grl_test values(2);
session 1:此时我们再来看session 1时,设置global read only已经成功了。session 2因为第二个命令违背了read only,导致事务被终止了。因此,也就再没有
running transaction了。global read only设置完成后,checkpoint向前推。我们就可以以此checkpoint为准,进行主备一致性切换。
上面完全实现了在线更改PG实例read only状态。我们再将实例在线改回到read write。是不是非常方便呢?
------------------------------------华丽丽的分割线------------------------------------
CentOS 6.3环境下yum安装PostgreSQL 9.3