我最近在设计一个系统时需要有一个用户下拉选择的功能,设计成输入首字母自动联想的autocomplete模式。现在在设计后端数据结构上遇到了些麻烦。目前是我直接用mysql的LIKE来做的这一功能,但如果随着用户和访问量的增长,这样肯定很慢。
我使用的是redis缓存,利用redis的交集功能来做搜索。比如一个用户名叫Hello,用php代码来展示如何处理这个用户名,为了展示方便,我只处理英文字母
在上面的代码中,我把每个字母用md5哈希一下然后和他的位置值$pos共同组成一个redis索引。这样如果在所有用户名都用这种办法做索引后,我们就可以使用redis的交集功能来做搜索了
但这个办法有几个缺陷
1.我觉得还是偏重,因为我只想实现一个用户名自动联想的功能而不是搜索。这样的系统还要维护一套庞大的索引,每次更新用户名还得一个一个删掉然后再塞入新的。
2.无法做LIMIT和OFFSET啊,翻页怎么半,万一一个交集非常大,岂不是要把系统撑死
3.模糊查询的功能偏弱,只能处理以关键词开头的情况。不过这个不是重点,能把上面两点解决就很好了。
不知道各位有什么好的方案?
你这个做法复杂度太高了。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
这样排在前面的就是用户经常选择的了。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。