<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在日常業務開發中,會經常遇到需要保證唯一性的資料業務,如使用者註冊業務。一般註冊業務中允許使用者以手機號或email註冊賬號,且需要保證唯一,不允許重複註冊。當用戶輸入手機號或email登入時,程式會判定輸入資訊的存在與否性,存在則走登入,不存在則走註冊。而保證唯一性就不僅僅需要在程式端做判斷,還需要MySQL的唯一索引去做最後一道防線。那麼唯一索引在一些業務中使用,如果唯一索引欄位中預設值設定為了null,會造成什麼後果呢?
在阿里的《阿里巴巴Java開發手冊》中關於MySQL-索引規範中寫道: 【強制】業務上具有唯一特性的欄位,即使是多個欄位的組合,也必須建立唯一索引。
說明:
不要以為唯一索引影響了insert速度,這個速度的損耗可以忽略不計,但提高查詢的速度是明顯的;
另外,即使在應用層做了非常完善的校驗控制,只要沒有唯一索引,根據墨菲定律,必然有髒資料產生。
在MySQL中,唯一索引樹是一個非聚簇索引,每次插入資料時,都會在唯一索引樹上進行遍歷查詢該插入值是否唯一,這也就是為什麼會影響insert的速度,因為多一步遍歷判斷唯一性。
mysql> select version(); +-----------+ | version() | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec)
#建表語句 CREATE TABLE `user_1` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵', `email` varchar(32) NOT NULL DEFAULT '' COMMENT '郵箱', `name` varchar(11) DEFAULT '' COMMENT '名字', PRIMARY KEY (`id`), UNIQUE KEY `uk-email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#第一次插入: insert into user(email,name) values('aaa@qq.com','aaa'); Affected rows: 1, Time: 0.003000s #再次插入同樣的資料: insert into user(email,name) values('aaa@qq.com','aaa'); 1062 - Duplicate entry 'aaa@qq.com' for key 'uk-email', Time: 0.005000s
此時對於唯一性來說是沒問題的,可以保證業務的email的唯一性。假設隨著業務的發展,此時需要增加手機號註冊功能,那麼表中就需要增加手機號欄位,且需要保證手機號和郵箱的關聯唯一性。
#建表語句,注意此時phone欄位的預設值為null CREATE TABLE `user_2` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵', `email` varchar(32) NOT NULL DEFAULT '' COMMENT '郵箱', `phone` char(11) DEFAULT NULL COMMENT '手機號', `name` varchar(11) DEFAULT '' COMMENT '名字', PRIMARY KEY (`id`), UNIQUE KEY `uk-email-phone` (`email`,`phone`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert資料
insert into user_2(email,name) values('aaa@qq.com','aaa'); Affected rows: 1, Time: 0.003000s insert into user_2(email,name) values('aaa@qq.com','aaa'); Affected rows: 1, Time: 0.003000s insert into user_2(email,name) values('aaa@qq.com','aaa'); Affected rows: 1, Time: 0.003000s insert into user_2(email,phone,name) values('bbb@qq.com','13333333333','bbb'); Affected rows: 1, Time: 0.003000s insert into user_2(email,phone,name) values('bbb@qq.com','13333333333','bbb'); 1062 - Duplicate entry 'bbb@qq.com-13333333333' for key 'uk-email-phone', Time: 0.002000s
此時會發現,不帶phone值得前三條資料都能插入成功,帶上郵箱和手機號的值卻能正常判斷唯一性
mysql> select * from user_2; +----+------------+-------------+------+ | id | email | phone | name | +----+------------+-------------+------+ | 1 | aaa@qq.com | NULL | aaa | | 2 | aaa@qq.com | NULL | aaa | | 3 | aaa@qq.com | NULL | aaa | | 4 | bbb@qq.com | 13333333333 | bbb | +----+------------+-------------+------+ 4 rows in set (0.00 sec)
這時就需要牽扯到MySQL的唯一索引機制了:在MySQL官方檔案中MySQL索引檔案,描述到:
A UNIQUE index creates a constraint such that all values in the index must be distinct. An error occurs if you try to add a new row with a key value that matches an existing row. If you specify a prefix value for a column in a UNIQUE index, the column values must be unique within the prefix length. A UNIQUE index permits multiple NULL values for columns that can contain NULL.
解釋一下:唯一索引建立一個約束,使得索引中的所有值都必須是不同的。如果嘗試新增一個鍵值與現有行匹配的新行,則會發生錯誤。如果在唯一索引中為列指定字首值,則列值在字首長度內必須是唯一的。唯一索引允許包含空值的列有多個空值。
先看下explain執行計劃:
mysql> explain select * from user_2 where email='aaa@qq.com' and phone is NULL; +----+-------------+--------+------------+------+----------------+----------------+---------+-------------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+----------------+----------------+---------+-------------+------+----------+-----------------------+ | 1 | SIMPLE | user_2 | NULL | ref | uk-email-phone | uk-email-phone | 132 | const,const | 3 | 100.00 | Using index condition | +----+-------------+--------+------------+------+----------------+----------------+---------+-------------+------+----------+-----------------------+ 1 row in set, 1 warning (0.01 sec) mysql> mysql> explain select * from user_2 where email='bbb@qq.com' and phone='13333333333'; +----+-------------+--------+------------+-------+----------------+----------------+---------+-------------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+----------------+----------------+---------+-------------+------+----------+-------+ | 1 | SIMPLE | user_2 | NULL | const | uk-email-phone | uk-email-phone | 132 | const,const | 1 | 100.00 | NULL | +----+-------------+--------+------------+-------+----------------+----------------+---------+-------------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
有沒有發現一個有趣的現象,雖然兩個sql語句都使用到了uk-email-phone唯一索引,但是 第一條sql的type為ref 第二條sql的type為const 我們知道,explain執行計劃中,const一般是主鍵查詢或者唯一索引查詢是才會出現,而ref一般是使用普通索引時出現。所以,可以得出結論,MySQL在底層對唯一索引的null值做了特殊處理。
我們通過檢視原始碼檔案的1863行,有這麼個註釋:
Scans a unique non-clustered index at a given index entry to determine whether a uniqueness violation has occurred for the key value of the entry. Set shared locks on possible duplicate records
意思是掃描給定索引項處的唯一非聚集索引以確定條目的鍵值是否發生唯一性衝突。對可能重複的記錄設定共用鎖。
也就是說row_ins_scan_sec_index_for_duplicate()
該方法就是處理唯一索引的,繼續往下看,在1892行,有一串註釋:
If the secondary index is unique, but one of the fields in the n_unique first fields is NULL, a unique key violation cannot occur, since we define NULL != NULL in this case
如果二級索引是唯一的,但是唯一索引的欄位存在NULL,則不會發生唯一性衝突,在此程式碼中定義了NULL != NULL
繼續往下走,在1996行,走到了row_ins_dupl_error_with_rec()
函數,該函數在1825行。在該函數中有以下程式碼:
/* In a unique secondary index we allow equal key values if they contain SQL NULLs 在唯一的二級索引中,如果包含sql NULL值 */ if (!index->is_clustered() && !index->nulls_equal) { for (i = 0; i < n_unique; i++) { if (dfield_is_null(dtuple_get_nth_field(entry, i))) { return (FALSE); } } }
也就是說,在唯一索引中欄位為NULL的情況下,返回false,沒有丟擲DB_DUPLICATE_KEY異常.
唯一索引重複插入之終極解決方案:給欄位設定空字串初始值,NOT NULL DEFAULT ''
即可,不要用null值作為初始值。
以上就是詳解MySQL的欄位預設null對唯一索引的影響的詳細內容,更多關於MySQL欄位預設null唯一索引的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45