多城市礼拜时间存储场景MongoDB Schema设计合理性咨询
现有Schema设计的问题
你当前的设计不符合MongoDB最佳实践,核心问题如下:
- 单文档体积极易超过MongoDB 16MB的上限:按34个省份、平均每个省15个地级市、10年共3652天计算,单个省份文档的嵌套数据量会轻松突破16MB限制,最终会导致写入失败。
- 查询效率极低:如果仅需要获取单个城市某一天的礼拜时间,你需要加载整个省份的全量数据,再遍历多层嵌套结构才能拿到目标数据,没有办法做高效的查询优化。
- 维护成本极高:修改单日某个时间字段需要写多层嵌套的更新路径,极易出错,同时高并发场景下对同一个大文档的频繁更新会触发大量写冲突。
- 索引适配困难:多层嵌套的结构下无法针对高频查询场景创建高效的复合索引,数据量上来后查询延迟会非常高。
适配该场景的最佳实践方案
我们根据高频查询场景(通常是查「某城市某段时间的礼拜时间」),采用分集合、细粒度存储的设计,一共拆为2个集合:
1. 地域基础信息集合 geo_locations
存储省份、城市的静态基础信息,避免数据冗余,单条文档对应单个城市:
{ _id: ObjectId("xxx"), province_name: "Province A", province_slug: "province-a", city_name: "City A", city_slug: "city-a", latitude: "4° 09' 31.76\" N", longitude: "96° 07' 28.86\" E" // 注意你原字段名拼写错误longtitude建议改为longitude }
可以给 province_slug、city_slug 建唯一索引,方便快速关联查询。
2. 礼拜时间集合 prayer_times
如果你的高频查询是按天取数据,采用单条文档对应单个城市单日的礼拜时间,结构如下:
{ _id: ObjectId("xxx"), city_slug: "city-a", year: 2021, month: 1, day: 1, date_str: "Friday, 01/01/2021", imsak: "05:11", subuh: "05:21", terbit: "06:39", dhuha: "07:08", dzuhur: "12:42", ashar: "16:05", maghrib: "18:39", isya: "19:53" }
如果高频查询是按月取整月数据,也可以调整为单条对应单个城市单月的时间,进一步减少文档数量:
{ _id: ObjectId("xxx"), city_slug: "city-a", year: 2021, month: 1, days: { "1": { /* 单日时间字段 */ }, "2": { /* 单日时间字段 */ }, // 当月其他日期 } }
优化后方案的优势
- 单文档体积远小于16MB上限,完全不会触发文档大小限制。
- 查询效率极高:给
prayer_times集合创建复合索引{ city_slug: 1, year: 1, month: 1, day: 1 }后,任意城市单日/单月/单年的时间查询都可以走索引覆盖,响应速度可控制在毫秒级。 - 更新成本极低:修改某一天的时间只需要过滤对应
city_slug和日期即可,路径简单,并发更新冲突概率极低。 - 扩展性强:后续需要新增年份数据只需要直接写入
prayer_times集合,不需要修改原有文档结构。
内容的提问来源于stack exchange,提问作者R.M. Reza
相关产品推荐
相关产品推荐

