写在前面

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

我们用的是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)}

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

access建立两个字段唯一索引_关于MongoDB唯一索引(Unique)的那些事相关推荐

  1. access建立两个字段唯一索引_数据库索引原理及优化

    微信公众号:云计算通俗讲义 持续输出技术干货,欢迎关注! 通过本文你将了解: 概述 分类 索引底层实现原理 基本操作 索引失效 索引优化 01 概述 索引是帮助MySQL高效获取数据的排好序的数据结构 ...

  2. access建立两个字段唯一索引_面试官:谈谈你对mysql索引的认识?

    引言 这篇我们就来谈谈关于索引方面的mysql面试题.还是老规矩,讲的是在Innodb存储引擎下的情形,毕竟我还真没用过Mysiam之类的存储引擎. ps:其实很早就想写了,一直偷懒! 其实这下面每个 ...

  3. mysql 添加唯一索引_浅谈Mysql索引

    文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. 我们都知道,数据库索引可以帮助我们更加快速的找出符合的数据,但是如果不使用索引,Mysql则会从第一条开始查询 ...

  4. mysql字段的区分度_详解MySQL索引长度和区分度之间的平衡,值得收藏

    概述 前面我们讲了怎么去计算索引所占用的长度?那么换个方式想?索引又应该设置多少长度比较合理呢? 区分度与索引长度的权衡 首先索引长度和区分度是相互矛盾的, 索引长度太短,那么区分度就很低,吧索引长度 ...

  5. mysql单列索引和多列索引_浅谈MySQL索引优化

    索引基础知识总结及常见索引优化手段 一.索引简介 什么是索引? MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构. 可以简单理解为"排好序的快速查找数据 ...

  6. mysql主键创建非聚集索引_什么是聚集索引,非聚集索引,索引覆盖,回表,索引下推...

    聚集索引 我们先建如下的一张表 CREATE TABLE `student` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '学号',`name` var ...

  7. 覆盖索引与联合索引_浅析MySQL的索引覆盖和索引下推

    写在前面 在MySQL数据库中,索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点,索引就是为了提高数据查询的效率.今天我们来聊聊在MySQL索引优化中两种常见的方式,索引覆盖和 ...

  8. 覆盖索引与联合索引_什么是覆盖索引?

    前言 要搞明白覆盖索引首先就得明白主键索引和辅助索引的区别,以及查询时引擎的工作方式. 当然,以上都是基于innoDB引擎来说. 主键索引与辅助索引的区别 相信大家也了解过这方面的知识,这里就不展开了 ...

  9. mysql linux 使用索引_正确使用MySQL索引

    MySQL之所以能够高效的检索数据,可以说全赖索引之功.在索引使用过程中,要注意一下几点. 1.MySQL在使用索引时候,采用的是最左匹配原则. 如果是单列索引,则很容易理解,若是多列索引,例如idx ...

最新文章

  1. html页面表格导出到excel总结
  2. Puppeteer + TypeScript 模拟 Ctrl + A 操作
  3. iOS NSTextAttachment - 图文混排
  4. iso镜像添加软件包_超薄Docker容器-减少Docker镜像大小的指南
  5. 消息队列MQ如何保证消息的幂等性
  6. centos PIL 安装
  7. c 语言 怎样编写图形窗口,「分享」C语言如何编写图形界面
  8. 面试鹅厂,我被虐的体无完肤。。。
  9. linux 串口是否可写,串口编程可写入不能读取 怎么解决
  10. 2016 杭州云栖大会随笔
  11. 微信添加表情提示过大怎么解决?GIF太大怎么变小?
  12. Byval和ByRef
  13. 计算机64位只有2g,电脑插了4G内存,但只有2G左右可以用,为什么 WIN7 64位
  14. c语言的源程序的后缀名是,C语言源程序文件的后缀名是()。
  15. 联手百度腾讯,恒大汽车押注智能化
  16. (学习记录)Win32开发之键盘
  17. 基于Visual Graph快速开发出电力系统
  18. Windows常用bat脚本打开目录或文件
  19. Python语言程序设计 (第11期) 期末测验: 课程水平综合测验
  20. apache ii评分和死亡率_APACHEⅢ、SAPSⅡ与APACHEⅡ评分系统对急诊内科危重患者病情评估的临床意义...

热门文章

  1. java冗余_Java使用lombok消除冗余代码的方法步骤
  2. CRMEB v4二开文档
  3. git批量删除没用的远程分支
  4. Linux7的ftp日志怎么看,centos7打开sftp操作日志
  5. 256qam调制星座图_5G调制怎么实现的?一文读懂
  6. 计算机科学讨论,第七章计算机科学讨论报告
  7. android 加减乘除计算器,【03-21求助】写一个简易计算器的安卓app,一按加减乘除就退出...
  8. mysql base64
  9. 【uni-app】动态计算图片高度且保持宽高比
  10. Java NIO之Selector