前言

速记今天遇到的一个由表情(emoji)引起的血案

Infobright

公司BI数据库用的是infobright,一个曾经风光,但是被收购后年久失修,连文档都难找的列式OLAP数据库。由于是社区版,不是收费版,所以录入数据只能load data infile...的方法,不能CRUD,也不能ALTER TABLE,如果入库数据出了问题,只能删表重导. 当然infobright本质上是基于列式存储,所以在查询和分析上在十年前还是不错的,但放到今天,就显得不够用了 infobright不支持utf8mb4,只能utf8,utf8mb4支持表情,所以infobright不支持表情存储,故load data infile的时候就load不进去,CPU直接上100%,而且还不报错…血案上演

诊断

最开始的症状是日志数据load不进infobright数据库,而且CPU负载过高,但是能想到影响CPU负载的因素就有当时只有三个:数据本身问题,入库脚本问题,infobright数据库进程。 在确诊是需要入库的数据中含有表情的问题之前,我们把入库脚本和数据库进程都分别重启了,但是重新录入数据后,还是一样的问题。谷歌看到有人遇到同样的症状,说是数据中含有表情的问题,才去查看原数据本身,才发现数据中有的用户的用户名是表情组成,而且正好是数据录不进数据库的时间,因此才确定问题的来源。 其实为什么我们最后才查看原数据,因为原数据我们已经作了规范,也说过不能含有表情,对于表情的处理应该是上游完成,而不应该是我们入库的时候清洗。

P.s.很多时候,诊断的时间其实花的比修复的时间更多。

处理表情

由于不支持utf8mb4,没办法直接改表的编码来完成表情的入库。 所以,我们后面的采用的办法是用python在文件入库前,先处理掉文件中表情,因为线上跑着的都是python2写的脚本,所以用的是python2写的(唉,别提用重新用py2写程序的各种编码地狱了)。

import re
emoji_pattern = re.compile(
            u"(\ud83d[\ude00-\ude4f])|"  # emoticons
            u"(\ud83c[\udf00-\uffff])|"  # symbols & pictographs (1 of 2)
            u"(\ud83d[\u0000-\uddff])|"  # symbols & pictographs (2 of 2)
            u"(\ud83d[\ude80-\udeff])|"  # transport & map symbols
            u"(\ud83c[\udde0-\uddff])"  # flags (iOS)
            "+", flags=re.UNICODE)
new_content = emoji_pattern.sub(u'emoji', content_unicode)

我们先把文件decode(‘utf8’),再用regex匹配表情的unicode编码,换成空格(或者其他东西,比如’emoji’),再encode(‘utf8’),重新保存为文件,最后load入库。 中间有个小插曲,以往的经验来说,python2的小版本之间,比如2.6到2.7之间的内建模块是兼容的,但是我在本地用python2.7写的程序ok,到线上python2.6跑,用内建的re写的正则匹配竟然不起作用了(因为是多进程,所以我直接在转换前后打印了日志,看日志才发现的),后面线上编译装了个了一个2.7版本的来跑,就正常了…珍爱生命,尽早转py3