久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

MySQL為什么不能用uuid做主鍵

154次閱讀
沒有評論

共計 4746 個字符,預計需要花費 12 分鐘才能閱讀完成。

今天丸趣 TV 小編給大家分享一下 MySQL 為什么不能用 uuid 做主鍵的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

前言

在 mysql 中設計表的時候,mysql 官方推薦不要使用 uuid 或者不連續不重復的雪花 id(long 形且唯一,單機遞增), 而是推薦連續自增的主鍵 id, 官方的推薦是 auto_increment, 那么為什么不建議采用 uuid, 使用 uuid 究竟有什么壞處?

一、mysql 和程序實例

1.1. 要說明這個問題, 我們首先來建立三張表

分別是 user_auto_key,user_uuid,user_random_key, 分別表示自動增長的主鍵,uuid 作為主鍵,

隨機 key 作為主鍵, 其它我們完全保持不變.

根據控制變量法, 我們只把每個表的主鍵使用不同的策略生成, 而其他的字段完全一樣,然后測試一下表的插入速度和查詢速度:

注:這里的隨機 key 其實是指用雪花算法算出來的前后不連續不重復無規律的 id: 一串 18 位長度的 long 值

1.2. 光有理論不行, 直接上程序, 使用 spring 的 jdbcTemplate 來實現增查測試:

技術框架:springboot+jdbcTemplate+junit+hutool, 程序的原理就是連接自己的測試數據庫, 然后在相同的環境下寫入同等數量的數據,來分析一下 insert 插入的時間來進行綜合其效率,為了做到最真實的效果, 所有的數據采用隨機生成,比如名字、郵箱、地址都是隨機生成。

package com.wyq.mysqldemo;
import cn.hutool.core.collection.CollectionUtil;
import com.wyq.mysqldemo.databaseobject.UserKeyAuto;
import com.wyq.mysqldemo.databaseobject.UserKeyRandom;
import com.wyq.mysqldemo.databaseobject.UserKeyUUID;
import com.wyq.mysqldemo.diffkeytest.AutoKeyTableService;
import com.wyq.mysqldemo.diffkeytest.RandomKeyTableService;
import com.wyq.mysqldemo.diffkeytest.UUIDKeyTableService;
import com.wyq.mysqldemo.util.JdbcTemplateService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.StopWatch;
import java.util.List;
@SpringBootTest
class MysqlDemoApplicationTests {
 @Autowired
 private JdbcTemplateService jdbcTemplateService;
 @Autowired
 private AutoKeyTableService autoKeyTableService;
 @Autowired
 private UUIDKeyTableService uuidKeyTableService;
 @Autowired
 private RandomKeyTableService randomKeyTableService;
 @Test
 void testDBTime() {
 StopWatch stopwatch = new StopWatch( 執行 sql 時間消耗 
 /**
 * auto_increment key 任務
 */
 final String insertSql =  INSERT INTO user_key_auto(user_id,user_name,sex,address,city,email,state) VALUES(?,?,?,?,?,?,?) 
 List UserKeyAuto  insertData = autoKeyTableService.getInsertData();
 stopwatch.start( 自動生成 key 表任務開始 
 long start1 = System.currentTimeMillis();
 if (CollectionUtil.isNotEmpty(insertData)) { boolean insertResult = jdbcTemplateService.insert(insertSql, insertData, false);
 System.out.println(insertResult);
 }
 long end1 = System.currentTimeMillis();
 System.out.println(auto key 消耗的時間:  + (end1 - start1));
 stopwatch.stop();
 /**
 * uudID 的 key
 */
 final String insertSql2 =  INSERT INTO user_uuid(id,user_id,user_name,sex,address,city,email,state) VALUES(?,?,?,?,?,?,?,?) 
 List UserKeyUUID  insertData2 = uuidKeyTableService.getInsertData();
 stopwatch.start( UUID 的 key 表任務開始 
 long begin = System.currentTimeMillis();
 if (CollectionUtil.isNotEmpty(insertData)) { boolean insertResult = jdbcTemplateService.insert(insertSql2, insertData2, true);
 System.out.println(insertResult);
 }
 long over = System.currentTimeMillis();
 System.out.println(UUID key 消耗的時間:  + (over - begin));
 stopwatch.stop();
 /**
 *  隨機的 long 值 key
 */
 final String insertSql3 =  INSERT INTO user_random_key(id,user_id,user_name,sex,address,city,email,state) VALUES(?,?,?,?,?,?,?,?) 
 List UserKeyRandom  insertData3 = randomKeyTableService.getInsertData();
 stopwatch.start( 隨機的 long 值 key 表任務開始 
 Long start = System.currentTimeMillis();
 if (CollectionUtil.isNotEmpty(insertData)) { boolean insertResult = jdbcTemplateService.insert(insertSql3, insertData3, true);
 System.out.println(insertResult);
 }
 Long end = System.currentTimeMillis();
 System.out.println(隨機 key 任務消耗時間:  + (end - start));
 stopwatch.stop();
 String result = stopwatch.prettyPrint();
 System.out.println(result);
 }

1.3. 程序寫入結果

可以看出在數據量 100W 左右的時候,uuid 的插入效率墊底,并且在后序增加了 130W 的數據,uudi 的時間又直線下降。

時間占用量總體可以打出的效率排名為:auto_key random_key uuid,uuid 的效率最低,在數據量較大的情況下,效率直線下滑。那么為什么會出現這樣的現象呢?帶著疑問, 我們來探討一下這個問題:

二、使用 uuid 和自增 id 的索引結構對比

2.1. 使用自增 id 的內部結構

自增的主鍵的值是順序的, 所以 Innodb 把每一條記錄都存儲在一條記錄的后面。當達到頁面的最大填充因子時候(innodb 默認的最大填充因子是頁大小的 15/16, 會留出 1 /16 的空間留作以后的 修改):

①下一條記錄就會寫入新的頁中,一旦數據按照這種順序的方式加載,主鍵頁就會近乎于順序的記錄填滿,提升了頁面的最大填充率,不會有頁的浪費

②新插入的行一定會在原有的最大數據行下一行,mysql 定位和尋址很快,不會為計算新行的位置而做出額外的消耗

③減少了頁分裂和碎片的產生

2.2. 使用 uuid 的索引內部結構

因為 uuid 相對順序的自增 id 來說是毫無規律可言的, 新行的值不一定要比之前的主鍵的值要大, 所以 innodb 無法做到總是把新行插入到索引的最后, 而是需要為新行尋找新的合適的位置從而來分配新的空間。

這個過程需要做很多額外的操作,數據的毫無順序會導致數據分布散亂,將會導致以下的問題:

①寫入的目標頁很可能已經刷新到磁盤上并且從緩存上移除,或者還沒有被加載到緩存中,innodb 在插入之前不得不先找到并從磁盤讀取目標頁到內存中,這將導致大量的隨機 IO

②因為寫入是亂序的,innodb 不得不頻繁的做頁分裂操作, 以便為新的行分配空間, 頁分裂導致移動大量的數據,一次插入最少需要修改三個頁以上

③由于頻繁的頁分裂,頁會變得稀疏并被不規則的填充,最終會導致數據會有碎片

在把隨機值(uuid 和雪花 id)載入到聚簇索引 (innodb 默認的索引類型) 以后, 有時候會需要做一次 OPTIMEIZE TABLE 來重建表并優化頁的填充,這將又需要一定的時間消耗。

結論:使用 innodb 應該盡可能的按主鍵的自增順序插入,并且盡可能使用單調的增加的聚簇鍵的值來插入新行

2.3. 使用自增 id 的缺點

那么使用自增的 id 就完全沒有壞處了嗎?并不是,自增 id 也會存在以下幾點問題:

①別人一旦爬取你的數據庫, 就可以根據數據庫的自增 id 獲取到你的業務增長信息,很容易分析出你的經營情況

②對于高并發的負載,innodb 在按主鍵進行插入的時候會造成明顯的鎖爭用,主鍵的上界會成為爭搶的熱點,因為所有的插入都發生在這里,并發插入會導致間隙鎖競爭

③Auto_Increment 鎖機制會造成自增鎖的搶奪, 有一定的性能損失

附:Auto_increment 的鎖爭搶問題,如果要改善需要調優 innodb_autoinc_lock_mode 的配置

以上就是“MySQL 為什么不能用 uuid 做主鍵”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,丸趣 TV 小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注丸趣 TV 行業資訊頻道。

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-15發表,共計4746字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 兴和县| 元阳县| 奎屯市| 页游| 那坡县| 宜良县| 澄迈县| 浦东新区| 乌拉特后旗| 陵水| 文成县| 微博| 抚顺县| 高青县| 日土县| 九台市| 辰溪县| 康保县| 沈丘县| 雅安市| 桦南县| 朔州市| 连江县| 远安县| 平塘县| 石门县| 乌拉特后旗| 尉犁县| 雅江县| 栾川县| 香河县| 新宾| 佛冈县| 夏邑县| 高阳县| 家居| 搜索| 湘乡市| 汶上县| 阿克苏市| 枣阳市|