惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
B
Blog
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
G
GRAHAM CLULEY
Microsoft Azure Blog
Microsoft Azure Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
Security Latest
Security Latest
F
Full Disclosure
云风的 BLOG
云风的 BLOG
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 热门话题
V
Visual Studio Blog
有赞技术团队
有赞技术团队
腾讯CDC
V
V2EX
Vercel News
Vercel News
C
Cisco Blogs
V2EX - 技术
V2EX - 技术
Scott Helme
Scott Helme
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
AWS News Blog
AWS News Blog
S
Schneier on Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
小众软件
小众软件
G
Google Developers Blog
C
Check Point Blog
C
CERT Recently Published Vulnerability Notes
博客园 - 叶小钗
S
SegmentFault 最新的问题
T
Tor Project blog
J
Java Code Geeks
L
Lohrmann on Cybersecurity
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
The Exploit Database - CXSecurity.com
Apple Machine Learning Research
Apple Machine Learning Research
T
Tailwind CSS Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 司徒正美
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Secure Thoughts
量子位
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
TaoSecurity Blog
TaoSecurity Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Engineering at Meta
Engineering at Meta

博客园 - today4king

DOTNET 运行AES-GCM程序 ON MACOS(错误ALGORITHM ‘AESGCM’ IS NOT SUPPORTED ON THIS PLATFORM) MACBOOK M1 PRO 下运行.NET CORE(MAC下如何与X86_64兼容) Mac上使用SQL Server作为开发用数据库 EntityFramework Core CLI Tools 几点坑 VisualStudio Code Remote 调试方法(错误Containers Docker version 17.12.0 or later required.) AWS Kubernetes/k8s kubeamd 初始化后kube-controller-manager pod CrashLoopBackOff 错误启动不了 阿里云 NAS OSS 云盘价格对比 GB/小时 kubernetes/k8s pod下多容器的设计模式(ambassador 大使代理模式,adapter 适配模式,sidecar 边车模式, init containers初始化容器) ❤️ 从125ms到11ms,记一次关键字检测过滤服务的优化 -python and Pythonnet FastAPI 中的Async (并发和async/await) 阿里云vs华为云 的容器镜像服务swr使用体验 理工男对衣架的选择/衣柜收纳之衣架篇 Python中使用自定义类class作为海量数据结构时内存占用巨大的问题 关闭/开启 ubuntu 自动更新提示 blueImp/jQuery file upload 的正确用法(限制上传大小和文件类型) python3 crypto winrandom import error Flask-Babel 中文支持(zh-CN和zh-Hans-CN) Aapache status / apache2ctl status 总是403 为什么你还在用嵌入式的方式来使用mod_wsgi?
高效的的关键字查找和检测(哈希表和Trie前缀树和FastCheck)在实际使用中的性能
today4king · 2021-05-14 · via 博客园 - today4king

前言:看到dudu发的博文中似乎最近的db压力来源于关键字检测,以前只关注了倒排索引,于是好奇经典的关键字查找在实际生产中性能到底是一个什么数量级?

为什么不用倒排索引

在一个文本中找到给定的关键字最快的做法是倒排索引,比如平常使用的各种搜索框如google,还有咱们在生产中分析日志时用的ES也是。它的优点是海量的文本和海量的关键字,缺点是搜索的文本加入现有索引很慢。所以它不适合:评论、即时通讯、微博、脏字过滤等对发布时间有要求的场景,而这些场景正好跟倒排索引是反过来的。

如果不用Trie树

那么如何实现一个高效的及时关键字检测算法,这里为了简单期间只关注FindFirst,而FindAll 同理。首先第一个否定的算法就是IndexOf(keyword)方法,这里假定输入文本长度n,检测的关键词数量m,关键字平均长度l,复杂度就是O(n*m*l),复杂度很高。在不知道Trie树之前很容易想到的实现是Hashtable,我想大多数程序员也是这样想的,复杂度直接降为O(n+m*l),这里要提一下好多写java和csharp的同学在实现这个算法的时候会调用substring,然后导致内存飙升,其实这个临时空间完全不需要的。下面是我用python写的一个实现,这里为简单期间回避了hashtable中碰撞的情况,一般用单向链表还有树来解决,因为知道trie了没必要去做的很完善,只是提供一个对比的数量级,而哈希表的完整实现只会比这个更慢。

def test3():

    with  open('./sample_post', encoding='utf-8') as f:
        test_post = f.read()
    spam_words={}
    with open('././SpamWordsCN.min.txt', encoding='utf-8') as f:
        words=[line.strip() for line in f]
    for w in words:
        if len(w)>0:
            spam_words[w[0]]=w
    import time

    start = time.time()
    times = 500
    while times > 0:
        for i,c in enumerate(test_post):
            if c in spam_words:
                is_found=True
                for j,sc in enumerate(spam_words):
                    if sc!=test_post[i+j]:
                        

测试条件和运行环境

  • 输入文本长度:5388 from word count
  • 关键字数量:575
  • 平均长度:3 目测
  • 测试次数:500
  • 运行环境:Ryzen 5 3600 6-core 3.6GHz

 

多次运行后会降为650ms左右

HashTable 总结

哈希的原理就是通过对关键字的预处理,把首字母的关键字检索降为O(1),整个过滤只需要O(n)即可5k的字符过滤650ms时间对大多数小项目小网站足够了,如果是即时通信、站内消息也是勉强足够了。但是作为一个服务的话,100并发下50ms都太大了,而且这里还只是非重复的关键字,重复的关键字是脏词最常见的形式,那么可不可以更快一点。

Trie 出场

在计算机科学中,trie,又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。— from 维基百科

关于Trie的原理可以查看Tool.Good的文章:https://www.cnblogs.com/toolgood/p/6284718.html

Trie的实现

高达2.3k星的实现:ToolGood.Words,每秒3亿的过滤效率,有多个语言版本的实现,其中以ccharp为主,python的 “咳咳”有点尴尬,从文件命名上看就不友好,而且似乎作者用来检测的字词有点过于简短,四五个关键词,不超过20个字符的文本输入完全测试不出压力。接下来试试Trie的威力,同样先从python试起,下面是测试代码:

from StringSearch import StringSearch
with  open('./sample_post',encoding='utf-8') as f:
    test_post = f.read()
with open('././SpamWordsCN.min.txt',encoding='utf-8') as f:
    spam_words = [line.rstrip() for line in f]
import time
search = StringSearch()
search.SetKeywords(spam_words)
start = time.time()
times = 500
while times > 0:

    f = search.FindFirst(test_post)
    times -= 1
end = time.time()
print('程序运行时间:%s毫秒' % ((end - start) * 1000))

Trie的实现只有之前哈希表的五分之一

测试环境下过滤效率大概是 2千万/每秒 ,看来csharp 3亿确实是可能的。 它之所以快是因为哈西表只解决了输入字符中完全不需要匹配的n,而Trie把关键字的复杂度也降低为O(max(l))。白话一点就是哈西表把indexof中遍历m遍降低为一遍,而Trie把哈希表中关键词的检测也降低为max(l),这里的l指最长的关键字长度,也可以理解为关键字的每一个字匹配都像哈希中的实现一样只需要一次匹配。O(n+max(l))的复杂度已经很低,效率极高。对于要求不高的项目直接可以耦合进现在的处理中。那么可不可以更快一点?

Fastcheck 和 TTMP

这里还有两个号称更快的算法FastCheck和TTMP,咳咳,只搜索到了博客园的帖子,不信你google。因为是改进算法,其中fastcheck在tool.goods中有实现,期待将来能再做一个评测比较。他们比Trie快在,适当的增加了空间换了可观的时间,比如fastcheck利用了类似bitmap的原理考虑了字符出现的位置,所以它在短关键字和过滤替换场景上的表现会很好。

还是前面的问题可以不可以更快一点

接下篇《从125ms到17ms,一次关键字检测过滤服务的优化》