日常答疑系列第二期
前言
这几天收集了一下各个交流群里的问题,潜水过程中也学到了很多内容。
tcp_keepalive
首先是关于TCP keepalive的,在PostgreSQL中,也有相关的几个参数用于释放无法继续通讯的空闲连接,默认配置取决于操作系统
postgres=# select name,setting from pg_settings where name like 'tcp%';
name | setting
-------------------------+---------
tcp_keepalives_count | 0
tcp_keepalives_idle | 0
tcp_keepalives_interval | 0
tcp_user_timeout | 0
(4 rows)
如果通信一方突然崩溃,无法通知对方,此时另一方能做的就只有傻傻等待了,所以引入了keepalive机制,快速感知失败。但是如果服务端正在忙着处理一个长查询,并不会立马注意到死连接直至查询结束,并且尝试给客户端发送执行结果,因此在14中引入了client_connection_check_interval参数来解决这个问题,每隔一段时间检查客户端是否已离线,如果离线则快速结束未完成的查询,防止客户端已离线而数据库继续运行未完成的查询。
让我们回到问题
可以看到,配置了相关参数,但是数据库里显式为0。为什么会这样?其实官网已经有所说明:
“In sessions connected via a Unix-domain socket, this parameter is ignored and always reads as zero.
如果是通过unix domain socket登录的话,那么始终显式为0。unix domain socket不需要使用传统的IP地址和端口,而是使用文件系统来进行程序之间的数据交互,如果不同主机上的两个进程进行通信,使用传统的TCP/IP协议当然没什么问题。但是,如果只需要在一台机器上的两个不同进程间通信,就有点杀鸡用牛刀的味道了,这便是unix domain socket的缘由。上面图片也很清晰,可以通过psql -h /path/to/socket/directory/ -p port切换其他目录下的socket文件,所以显式为0就不足为奇了。
当然功能类似的还有jdbc中的socket timeout,可以理解成全局的查询时长限制,是通过TCP连接发送数据(SQL)后,等待响应的超时时间,也可以及时发现网络问题,虽然有了keepalive机制,但大多数操作系统的心跳包的间隔时间要么很长,要不默认没有设置,比如Linux下的keepalive的设置是2小时,所以这个参数也可以帮助我们。
“The timeout value used for socket read operations. If reading from the server takes longer than this value, the connection is closed. This can be used as both a brute force global query timeout and a method of detecting network problems. The timeout is specified in seconds and a value of zero means that it is disabled.
空闲连接
另外一个是关于空闲连接的问题,长链接的危害不言而喻,在14以前是没有参数用于控制空闲连接何时主动超时断开的(不要与tcp keepalive机制搞混),当然你可以通过pg_timeout插件或者自己写脚本来更加细粒度地控制,在14就方便了,idle_session_timeout解千愁。
checkpoint触发机制
这个问题是③群一位朋友问的
当把log_checkpoint参数打开便会有类似日志条目,这种问题其实在代码里根据关键字一搜就知道了
else
ereport(LOG,
/* translator: the placeholders show checkpoint options */
(errmsg("checkpoint starting:%s%s%s%s%s%s%s%s",
(flags & CHECKPOINT_IS_SHUTDOWN) ? " shutdown" : "",
(flags & CHECKPOINT_END_OF_RECOVERY) ? " end-of-recovery" : "",
(flags & CHECKPOINT_IMMEDIATE) ? " immediate" : "",
(flags & CHECKPOINT_FORCE) ? " force" : "",
(flags & CHECKPOINT_WAIT) ? " wait" : "",
(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
(flags & CHECKPOINT_CAUSE_TIME) ? " time" : "",
(flags & CHECKPOINT_FLUSH_ALL) ? " flush-all" : "")));
}
可以看到触发条件是CHECKPOINT_CAUSE_XLOG,顾名思义,达到了max_wal_size触发的checkpoint
/*
* OR-able request flag bits for checkpoints. The "cause" bits are used only
* for logging purposes. Note: the flags must be defined so that it's
* sensible to OR together request flags arising from different requestors.
*//* These directly affect the behavior of CreateCheckPoint and subsidiaries */
#define CHECKPOINT_IS_SHUTDOWN 0x0001 /* Checkpoint is for shutdown */
#define CHECKPOINT_END_OF_RECOVERY 0x0002 /* Like shutdown checkpoint, but
* issued at end of WAL recovery */
#define CHECKPOINT_IMMEDIATE 0x0004 /* Do it without delays */
#define CHECKPOINT_FORCE 0x0008 /* Force even if no activity */
#define CHECKPOINT_FLUSH_ALL 0x0010 /* Flush all pages, including those
* belonging to unlogged tables */
/* These are important to RequestCheckpoint */
#define CHECKPOINT_WAIT 0x0020 /* Wait for completion */
#define CHECKPOINT_REQUESTED 0x0040 /* Checkpoint request has been made */
/* These indicate the cause of a checkpoint request */
#define CHECKPOINT_CAUSE_XLOG 0x0080 /* XLOG consumption */
#define CHECKPOINT_CAUSE_TIME 0x0100 /* Elapsed time */
其他场景比如create database、达到了timeout、手动发起、recovery结束时等等都会触发checkpoint。
逻辑复制
“这个我经常在开发环境遇到过,他们老是改结果,我搞了个ddl触发器 去消费ddl的变更,老是有问题,原因是在源端执行了ddl过程中有数据产生,就形成了ddl+dml+ddl ,订阅段是直接ddl+ddl ,中间的一个dml就会结构匹配不上而报错
具体内容可以参照 https://blog.csdn.net/dazuiba008/article/details/117670295,这个问题之前我确实没有遇到过,从源头进行了扼制,不允许轻易DDL,在逻辑复制场景下更甚,分多步严格执行。
小结
以上几个问题都比较典型,容易让人混淆。潜水各个交流群的好处体现出来了,不说话也能学到东西。
最近粉丝数涨得挺快,快要5000了。学徒2群也快满了,3群目前160人还可以扫码进,后来的读者感兴趣的可以扫码进来唠唠嗑,日常答疑、吹牛唠嗑都是可以的。若是满了就添加我微信手动拉吧。
参考
https://hackmd.io/@TMSNgCo0TY6IGequcdJLwA/HkpPKG6-v