侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

MongoDB中唯一索引(Unique)的那些事

数据库  /  管理员 发布于 5年前   180

写在前面

MongoDB支持的索引种类很多,诸如单键索引,复合索引,多键索引,TTL索引,文本索引,空间地理索引等。同时索引的属性可以具有唯一性,即唯一索引。唯一索引用于确保索引字段不存储重复的值,即强制索引字段的唯一性。缺省情况下,MongoDB的_id字段在创建集合的时候会自动创建一个唯一索引。本文主要描述唯一索引的用法。

关于什么是索引以及唯一索引这里就不做说明了,不清楚的可以自行谷歌或者百度。是什么引起我写这篇文章呢,这来自于之前项目中的一个问题。

我们用的是MongoDB数据存储用户信息,用户表中曾经用户注册是通过手机号注册的,所以很理所当然的给手机号加上了唯一索引(Unique),这是没有什么毛病。后期,我们需求改了。你也可以想到变成了既可以手机号注册又可以邮箱注册,这个时候由于手机号加了Unique索引,事实上这时候是会出现问题的。

func init() { phoneIndex := mgo.Index{ Key: []string{"phone"}, Unique: true, } col := db.Collection(&User{}) col.EnsureIndex(phoneIndex)}

当然这问题其实也容易想到,当用户通过邮箱注册此时手机号填空的时候,第一次没什么问题,下个用户再以这种方式注册的时候便会提示建立在phone上的索引值重复,很正常嘛,因为插入了两个空值,注意这里是空字符串,而不是null。

于是我们尝试修改,由于MongoDB是文档型灵活的数据库,少插多插一两个字段不受影响,所以我们尝试修改User实体Phone字段的入口,当phone是空字符串的时候,不让插入此字段。于是,我们便在phone字段中加入了omitempty标签(我们微服务用Go语言写的)。下面展示User一部分内容:

type User struct { Email  string `bson:"email"` Salt  string `bson:"salt"` Phone  string `bson:"phone,omitempty"` IDCard string `bson:"idcard"` RealName string `bson:"realname"` AuthStatus int `bson:"auth_status"`}

可以看到phone字段后加了omitempty标签,表示当该字段为空的时候不插入。这还是会出现问题,那么既然还是会出问题为什么会想到这么解决呢?这源于对Mysql的使用经验,习惯性的以为MongoDB和Mysql那样,对null的值会不做其索引。也就是说,在Mysql中,若在多条记录中Phone值为Null是被允许的。

上面那种做法,还是会报错,提示插入了重复的值,只不过这时不是空字符串,而是null。所以有时候就不要把Mysql那套拿来了,Mysql是可以的,但Mongo不行。mongo还是会对该条记录索引,即使该字段为被插入。

我喜欢看官方文档,下面给出MongoDB官方文档说明:

If a document does not have a value for the indexed field in a unique
index, the index will store a null value for this document. Because of
the unique constraint, MongoDB will only permit one document that
lacks the indexed field. If there is more than one document without a
value for the indexed field or is missing the indexed field, the index
build will fail with a duplicate key error.

其实已经说得很清楚了,稍微会点英语应该都能看懂,下面还是给出翻译版:

如果文档没有唯一索引中索引字段的值,则索引将为此文档存储null值。由于唯一约束,MongoDB只允许一个缺少索引字段的文档。如果有多个文档没有索引字段的值或缺少索引字段,则索引构建将失败并出现重复键错误。

也就是说这个字段哪怕在文档中没有,那么该字段将会存null值,该字段上也不能同时出现两个null值,这就是为什么上面那种做法还是行不通的原因,其实上面那种做法也打破了数据结构,虽然手机号未填,但数据库中也不应该缺少这个字段,尽管是非关系数据库,毕竟还得考虑下业务设计。

解决方式

是不是就没有解决方式了呢?当然有,Mongo提供了Sparse Index,被翻译为稀疏索引。下面是创建稀疏索引的例子:

db.getCollection("test").createIndex( { "phone": 1 }, { sparse: true })

执行上面的语句后,不会去索引不存在phone字段的文档。也就是说存在才对其索引,那么此时和Unique索引结合起来就可以派上用场了。Unqiue是唯一,Sparse是存在才索引。所以,当phone或email为空的时候我们可以不将其插入这是可以实现的。

db.getCollection("test").createIndex( { "phone": 1 }, { sparse: true,unique: true } )

上面是是mongo shell语法,通常我们一般通过代码中建立索引,修改如下(当然User结构体中Phone字段omitempty标签还是要有的):

func init() { phoneIndex := mgo.Index{ Key: []string{"phone"}, Unique: true, Sparse: true, } col := db.Collection(&User{}) col.EnsureIndex(phoneIndex)}

但是这又正如我们前面说的那样,打破了数据原有的数据结构。哎,有得有得。当然我们还可以从业务层面去解决,比如注册时对其查询等操作,当然会耗一定性能,不管你是那空间换时间,还是拿时间换空间总得付出一个,别做一个太贪心的人。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对AIDI的支持。


  • 上一条:
    mongodb实现数组对象求和方法实例
    下一条:
    MongoDB特点与体系结构等简介
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 分库分表的目的、优缺点及具体实现方式介绍(0个评论)
    • DevDB - 在 VS 代码中直接访问数据库(0个评论)
    • 在ubuntu系统中实现mysql数据存储目录迁移流程步骤(0个评论)
    • 在mysql中使用存储过程批量新增测试数据流程步骤(0个评论)
    • php+mysql数据库批量根据条件快速更新、连表更新sql实现(0个评论)
    • 近期文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2017-06
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-01
    • 2018-05
    • 2018-10
    • 2018-11
    • 2020-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-08
    • 2020-09
    • 2021-02
    • 2021-04
    • 2021-07
    • 2021-08
    • 2021-11
    • 2021-12
    • 2022-02
    • 2022-03
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-07
    • 2023-08
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-03
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客