开发者社区> 问答> 正文

如何设计一个轻量的用户autocomplete系统

我最近在设计一个系统时需要有一个用户下拉选择的功能,设计成输入首字母自动联想的autocomplete模式。现在在设计后端数据结构上遇到了些麻烦。目前是我直接用mysql的LIKE来做的这一功能,但如果随着用户和访问量的增长,这样肯定很慢。
我使用的是redis缓存,利用redis的交集功能来做搜索。比如一个用户名叫Hello,用php代码来展示如何处理这个用户名,为了展示方便,我只处理英文字母
1
在上面的代码中,我把每个字母用md5哈希一下然后和他的位置值$pos共同组成一个redis索引。这样如果在所有用户名都用这种办法做索引后,我们就可以使用redis的交集功能来做搜索了
2
但这个办法有几个缺陷
1.我觉得还是偏重,因为我只想实现一个用户名自动联想的功能而不是搜索。这样的系统还要维护一套庞大的索引,每次更新用户名还得一个一个删掉然后再塞入新的。
2.无法做LIMIT和OFFSET啊,翻页怎么半,万一一个交集非常大,岂不是要把系统撑死
3.模糊查询的功能偏弱,只能处理以关键词开头的情况。不过这个不是重点,能把上面两点解决就很好了。
不知道各位有什么好的方案?

展开
收起
落地花开啦 2016-02-27 14:05:26 3199 0
1 条回答
写回答
取消 提交回答
  • 喜欢技术,喜欢努力的人

    你这个做法复杂度太高了。sInter的复杂度是O(N*M),N表示最小的集合长度,M表示集合数。当用户输入比较短时,比如He,你M=2,N而N应该会随着数据量越来越大。当用户输入长一些的时候,M和N都会变得非常大。所以我个人觉得不可取。
    我的建议是使用类似trie树的结构,使用Redis的话做法也很简单。将所有usename切成多个前缀。比如“foo”切成
    `f
    fo
    foo`
    几个前缀,每一个前缀维护一个set,后面跟着uname:uid的形式,这样你反查uid的需求也能满足。
    这样如果有两个用户:
    `100001:foo
    100002:fou`
    你的存储就是
    `f => foo:100001,fou:100002
    fo => foo:100001,fou:100002
    foo => foo:100001
    fou => fou:100001`
    这样查询的时候一次查询就可以了。修改的时候在所有前缀里把对应的uname:uid删除掉即可。比如foo这个用户名修改为bar了,就把foo这个删除,再对新名字的前缀对应的集合进行维护即可。
    `srem f foo:100001
    srem fo foo:100001
    srem foo foo:100001`
    另外,如果你把set换成zset,score可以用到存热度,比如上面的fou和foo,如果有用户输入f选择了foo,那么就对foo在f对应的集合里的score加1。
    zincrby f 1 foo
    这样排在前面的就是用户经常选择的了。

    2019-07-17 18:48:47
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
无需从0开发 1天上手只能语音离在线方案 立即下载
无需从0开发-1天上手智能语音离在线方案 立即下载
QQ移动页面框架优化实践 立即下载