GPDB-疑难杂症-使用资源组入库OOM
1、问题
GPDB6资源组可以使业务在事务级别控制资源的使用,业务侧启用资源组后,入库时查看数据库日志发现大量OOM报错:
ERROR...Out of memory...Resource group memory limit reached...INSERT INTO ... SELECT...
业务连接用户具备superuser权限,使用admin_group资源组。对memory_limit等资源组属性配置进行调整,仍持续报错。
gp_resgroup_memory_policy为eager_free,gp_ressource_group_memory_limit为0.7,statement_mem为200MB,gp_resource_group_bypass为off。
将memory_limit调整后,查看gp_toolkit.gp_resgroup_status_per_segment视图,发现全局可用内存memory_available还剩余很多,高达46GB,slot配额剩余36GB,资源组共享区没有使用。
2、分析
视图中可以看到资源组可用内存还剩余很多,为什么还会报错OOM呢?前文我们分析过,bypass模式下,QE上限制仅10MB,若INSERT数据量特别大并且复杂时,倒是有可能超过10MB。但是,通过对资源组机制进行分析,仅SET、RESET、SHOW语句才会bypass模式,通过日志可以看到时INSERT语句OOM。所以又是什么原因呢?
我们找到OOM报错日志的位置:
gp_failed_to_alloc函数中:
也就是错误码是MemoryFailure_SystemMemoryExhausted。根据该错误码向上回溯:该错误码由gp_malloc_internal函数调用VmemTracker_ReserveVmem时的返回值。进一步看代码,可知是VmemTracker_ReserveVmemChunks的返回值。VmemTracker_ReserveVmemChunks函数中返回该错误码的分支为:
也就是ResGroupReserveMemory函数返回false时才会报该错误。
前文已分析,返回false的场景也就两种,一种是bypass模式下的10MB限制,另一种是资源组定义的内存。
我们分别在这两个分支处添加打印日志,看下到底是哪个场景导致OOM的。
经测试复现,确实是INSERT语句,走bypass模式分支报的错。
Bypass模式只能是SET、RESET、SHOW语句才会用,前文我们也分析了,同一个事务内的SQL语句都使用同一个资源组,业务会不会将INSERT语句和SET/RESET/SHOW放在一起了?
返回来,查看业务的SQL,发现执行INSERT前确实有个SET statement_mem语句。询问业务侧,是否将这两个语句放到一个事务里了,但人家回应两个在单独事务里。有时候不能太相信业务端,还真得深究他们的代码:他们通过JDBC连接GPDB,默认情况下为自动提交,也就是说这两个是独立的两个事务。
始终怀疑,这两个语句在同一个事务中,好了,继续验证:修改代码,让jdbc连接服务后执行SET语句前sleep 30秒,这段时间足够我们ps看到服务端的进程ID,gdb跟踪该PID,在QD端StartTransaction和CommitTransaction处都打上断点,以及QE端资源组分配函数SwitchResGroupOnSegment上打断点。
业务JDBC执行后,StartTransaction仅执行了一次,QE端先执行SET语句,确实走的bypass模式,然后再执行INSERT,它确实在SET事务内,同样走bypass模式。
到此,十分清楚了,SET和INSERT在同一个事务内,而SET语句在前,它的事务分配资源组bypass模式,后续的INSERT命令继续使用该资源组,同样继续走bypass模式,所以限制仍旧是10MB。
那出问题的就是JDBC了,使用默认自动提交情况下,将SET命令和INSERT语句一起发送时,成为了一个事务。有可能是JDBC的bug。
怎么解决?将SET命令单独放在显式事务中:BEGIN;SET...;COMMIT;然后再执行INSERT,这样将其分开,INSERT独立一个事务,让其走资源组属性的限制。Ok,问题解决!