MongoDB匹配部分手机号数组查询文档的性能问题咨询
关于MongoDB手机号后9位匹配查询的性能与生产可用性分析
你最初设想的$in同时传入数值数组和统一正则的写法无法正常生效,MongoDB不支持这种混合匹配的逻辑,只能像你测试的那样把每个后9位转成后缀正则放到$in中。
现有实现的性能问题
- 无法命中普通索引:结尾匹配的正则
/xxx$/没有固定前缀字符,普通单字段索引对这类查询完全失效,数据量较大时会触发全表扫描,查询延迟会随用户量级增长线性上升 - 多正则匹配开销高:$in中传入N个正则表达式时,每一条扫描到的文档都需要逐一匹配N次正则,当上传的联系人数组长度较大(比如超过50)时,CPU开销会显著上升,高并发场景下很容易出现请求超时
生产环境使用建议
如果你的业务满足以下所有条件,可以临时使用该方案上线:
- 用户总规模低于10万
- 单次请求上传的联系人数组长度不超过30
- 该接口的QPS低于10
如果不符合以上条件,不建议直接在生产环境使用该方案,推荐使用更优化的实现方式。
推荐优化方案
方案1:冗余存储后9位字段(最优)
写入用户数据时,额外冗余存储一个phoneLast9字段,值为手机号后9位,为该字段建立普通索引。查询时直接使用以下语句即可走索引查询,性能提升10倍以上:
usersContacts = [123456789, 987654321, 123443212,...] db.users.find({ phoneLast9: { $in: usersContacts } })
该方案改造成本极低,查询性能最优,是生产环境的首选方案。
方案2:创建计算字段索引(无需修改存量写入逻辑)
如果不方便修改存量数据的写入逻辑,可以在MongoDB 4.4及以上版本中创建计算字段索引,直接对手机号后9位的计算结果建索引:
// 创建计算索引 db.users.createIndex({ "$expr": { $substrCP: [ "$phoneNumStr", { $subtract: [ { $strLenCP: "$phoneNumStr" }, 9 ] }, 9 ] } }, {name: "phone_last9_idx"}) // 查询时使用$expr匹配 db.users.find({ $expr: { $in: [ { $substrCP: [ "$phoneNumStr", { $subtract: [ { $strLenCP: "$phoneNumStr" }, 9 ] }, 9 ] }, usersContacts ] } })
该方案无需修改现有写入逻辑,查询也可以命中索引,性能接近冗余字段方案。
内容的提问来源于stack exchange,提问作者cjd
相关产品推荐
相关产品推荐

