异常断电导致索引数据未持久化成功,引发索引无法命中记录,顺序扫描可命中的问题排查分享

不知道应该起个啥标题

最近经手现场一个因意外断电停机导致索引数据未写入,但数据已写入,特定数据使用索引无法检索到,且程序中未判空导致NPE的问题。感觉有必要跟大家提个醒,给大家分享一下。

要不标题叫排查过程也行

1、有个定时任务定时扫描需要OCR的文书记录,并交给执行器去OCR(现在判断,这一步应该是走的全表扫描,或者没有用到主键索引)

2、每个执行器拿一条记录去取完整的文书表记录,调用OCR识别引擎(这里经常出现NPE错误)
有怀疑过是否有应用经常删除记录,但想了想概率不大,删除操作还是比较谨慎的
想给整个流程增加一些特殊标记日志,分发任务之前把获取到的文书ID打印出来,报错的时候也打印出来,看时间差及数据库状态,通过类似Oracle的误删除恢复闪回特性确认是否记录真的被物理删除了(没验证postgresql是否有table as timestamp to_timestamp(xxx)这种特性,有可能没有)。

3、后来小伙伴人(rou)定位到两条脚本执行的结果不一致,问题稳定复现
步骤1中是用JZ表和WS表JOIN,并根据特定条件取出WS表的C_BH等几个基本字段,然后步骤2中使用C_BH精确定位WS表记录,取需要的字段进行OCR操作。
但小伙伴发现步骤1中查询出来的好多WS表C_BH直接用C_BH=XX查询是不存在的,但反过来再次查询联表查询依然存在,这不科学。

4、问题定位(索引数据异常,且优化器选择索引扫描导致)
我按照小伙伴的脚本验证了下,确实问题稳定复现,啥原因呢?
首先怀疑是CHAR定长,且存在空格导致。用like左右加百分号果然可以查询出来了,但是用length查询字段总长度及查看表结构定义发现没问题,字段为CHAR(32),编号值也是32位,还人工数了两遍确认没啥问题。下一个思路是啥?
既然确定数据是存在的,只不过特定条件下不能命中,那就看看不同结果的脚本执行计划吧,能否看出来啥。然后发现…只要用index scan的都无法命中,只要是seq scan的就能命中,这明显是索引有问题了。但是为啥索引会出问题呢?保留案发现场,叫来了DBA小伙伴一起研究了一下,abase的日志里面没啥报错,这个问题还真的不好定位,DBA小伙伴说之前出现过断电意外停止数据库会出现这种情况,然后现场反馈还真的就是近期意外断电了!!!

5、问题解决(重建主键索引解决)
既然原因都定位了,那就解决吧。我们直接重建了主键约束,重新执行脚本,都可以命中了,问题解决。

这一段要不就叫结语吧

1、数据库的数据文件和索引文件是分开存储的,极端情况下可能会出现物理表数据与对应索引数据不对应的情况,例如这次的意外断电。
2、现场非常有必要定期执行VACUUM,而且不能仅仅是VACUUM ANALYZE,要用VACUUM FULL。
3、大家写程序的时候要考虑异常情况,不要因为一个异常把整个程序搞死了。像本次的事儿如果最后剩下的都是有问题的数据,那就好定位一些了。
4、程序是死的,你代码怎么写他怎么执行,不可能存在灵异事件,所谓的灵异事件只是不知道根因而已。

已跟分公司系统总工沟通,要求现场安排定期执行VACUUM FULL。

猜你喜欢

转载自blog.csdn.net/leandzgc/article/details/103164168