庖丁优化,ac算法

来源:互联网 发布:mac虚拟机win7镜像 编辑:程序博客网 时间:2024/05/19 17:57

    目前关键词敏感词改造后是部署在一起的,暂时有三台服务器,敏感词大约有900条数据,关键词大概有35W数据,
    敏感词部分有自动更新机制,每天更一次,从数据库中查询,这个如果后面数量变大,可以考虑不进行每天更新,目前词汇数目小没有什么影响。敏感词部分目前是去掉空格匹配敏感词,用的是AC算法。
AC算法思想:用多模式串建立一个确定性的树形有限状态机,以主串作为该有限状态机的输入,使状态机进行状态的转换,当到达某些特定的状态时,说明发生模式匹配。具体可以参考一些论文里面提到的算法原理。
     关键词部分采取的是庖丁分词来解决,不过在我们的版本进过了一些曲折,刚刚开始的时候我们在本地采用的策略是对外不感知,也就是原来的接口是通过庖丁分词,然后从能存中比较,庖丁词库用的是默认的词库,同时如果没有匹配到关键词的时候将标题两个字为标准切割,我们修改的次略,是将标题和类目都传到微服务上,并且修改庖丁源码,使得庖丁可以接受需要解析的词,及类目,在庖丁中通过不同的类目来创建不同的庖丁分词knife,并且读取不同的庖丁字典,从而解析出来的词自然就是关键词。同时将孤立词不返回处理。这就比较精准了。在本地测试也没有什么问题,为了解决要拿到购买量和点击量,所有将查询出来的关键词查询数据库,在请求较少的时候没有什么问题。 
   当上线的时候,发现关键词所在数据库立马会把资源占完。当时以为是数据库问题,就将庖丁分出的词不进行数据库匹配,这样所有的购买数量和点击数就会变成0,但是这样远远没有解决问题,发现跑大约2-3分钟后服务还是会自动停止,原因是内存太小了,我们的内存最大只有1G,而我们抛开敏感词,留给关键词使用的内存不会超过400M,如果继续使用原来的分类目解析,这么小的内存对于35W数据来说是远远不够的,本地测试,35W数据如果只保存keyword buynum clicknum三个字段作为一个map的话大概需要内存200-300M这样留给庖丁的也就只有100-200M,而庖丁分词器也是要占很大内存的,少量一个至少要占用10-20M,这时候如果在现网大量请求来的时候里面就不够了。
   当时想到两个方案,第一个方案是扩容,将机器内存变大,这样实现还是继续那样实现,总共有2400个左右的二级类目,每一个二级类目都对应有一个分词器,这样算下来内存也是不够,假设有一半的类目是常用类目,也得有10G才行,如果通过类目将请求分片,不同请求到不同机器上,每个机器上的类目就不大这样可行,但是这样要的资源就多了,也不可行。第二个方案是修改代码:不再采用二级分类作为标准来建立庖丁解析器,我将所有的关键字字典做成一个庖丁解析器。这时候就不用这么多庖丁解析器,我只要缓存一个庖丁解析器就可以了,但是这样分词就不准确了,无法匹配到具体的类目,此时我们就用上二级类目的内存值了,拿着庖丁全词结果,加上对应的类目作为key去匹配内存中的值就可以匹配到对应类目的精准值,这个方案的另一个缺点是如果要将所有的35w数据读取到内存很慢,而且全词的字典项生成也很慢,要解决这些我问题我们要么是用空间换时间,要么是用时间换空间,我们选择后者,我们可以先在一台机器上慢慢生成全词字典项,我们可以将全词字典项作为代码提交,我们只需要在代码中读取文件就可以了,从测试结果来看,如果从后台读取数据,一个二级类目差不多4S一共有2400个二级类目,如果直接是线上查询,可能是无法承受的,所以只能先生成在文件里面,然后读取文件并结构化成map对象,大概用时间40-200MS,所以显而易见,我们用第二种,所以我们要提前保存所有关键字的字典项,和所有字典项的点击量和购买量,这两个可以放在一台空闲服务器上生成,生成后替换到运行环境上即可。这样就可以解决所有问题,测试后词方法可行。考虑到词库的变更也不是很频繁,暂时先用词策略。
    采用新方法后内存问题解决,但是微服务的内存配置是1G 1U,cpu还是有点小,一接入大概cpu会达到97%左右。这样运行一段时间后大概是18小时左右就会挂掉,内存占用大概是800M,所以下一步是优化cpu利用率,主要讲将没有必要的if判断还有循环删除,最后成功将cpu降低到7%以下,效果时分明显。
   总结如果用户使用较多的情况下任何一个if都不能小看,里面可能就占用10%的cpu,特别是由大对象操作的时候更加要注意。内存,gc ,cpu都是大的冲击,编码优化对节约资源很有帮助,采用的策略也很重要,后期可以实践探讨。
参考:https://github.com/zouheliang2011/aho-corasick 
原创粉丝点击