欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

為什么不能用uuid作為數(shù)據(jù)庫主鍵

這篇文章主要介紹“為什么不能用uuid作為數(shù)據(jù)庫主鍵”,在日常操作中,相信很多人在為什么不能用uuid作為數(shù)據(jù)庫主鍵問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”為什么不能用uuid作為數(shù)據(jù)庫主鍵”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

創(chuàng)新互聯(lián)建站企業(yè)建站,10余年網(wǎng)站建設(shè)經(jīng)驗,專注于網(wǎng)站建設(shè)技術(shù),精于網(wǎng)頁設(shè)計,有多年建站和網(wǎng)站代運營經(jīng)驗,設(shè)計師為客戶打造網(wǎng)絡(luò)企業(yè)風(fēng)格,提供周到的建站售前咨詢和貼心的售后服務(wù)。對于成都網(wǎng)站制作、網(wǎng)站設(shè)計中不同領(lǐng)域進(jìn)行深入了解和探索,創(chuàng)新互聯(lián)在網(wǎng)站建設(shè)中充分了解客戶行業(yè)的需求,以靈動的思維在網(wǎng)頁中充分展現(xiàn),通過對客戶行業(yè)精準(zhǔn)市場調(diào)研,為客戶提供的解決方案。

 一、摘要

在日常開發(fā)中,數(shù)據(jù)庫中主鍵id的生成方案,主要有三種

數(shù)據(jù)庫自增ID

采用隨機(jī)數(shù)生成不重復(fù)的ID

采用jdk提供的uuid

對于這三種方案,我發(fā)現(xiàn)在數(shù)據(jù)量少的情況下,沒有特別的差異,但是當(dāng)單表的數(shù)據(jù)量達(dá)到百萬級以上時候,他們的性能有著顯著的區(qū)別,光說理論不行,還得看實際程序測試,今天小編就帶著大家一探究竟!

二、程序?qū)嵗?/h4>

首先,我們在本地數(shù)據(jù)庫中創(chuàng)建三張單表tb_uuid_1、tb_uuid_2、tb_uuid_3,同時設(shè)置tb_uuid_1表的主鍵為自增長模式,腳本如下:

CREATE TABLE `tb_uuid_1` (   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,   `name` varchar(20) DEFAULT NULL,   PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='主鍵ID自增長';
CREATE TABLE `tb_uuid_2` (   `id` bigint(20) unsigned NOT NULL,   `name` varchar(20) DEFAULT NULL,   PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='主鍵ID隨機(jī)數(shù)生成';
CREATE TABLE `tb_uuid_3` (   `id` varchar(50)  NOT NULL,   `name` varchar(20) DEFAULT NULL,   PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='主鍵采用uuid生成';

下面,我們采用Springboot + mybatis來實現(xiàn)插入測試。

2.1、數(shù)據(jù)庫自增

以數(shù)據(jù)庫自增為例,首先編寫好各種實體、數(shù)據(jù)持久層操作,方便后續(xù)進(jìn)行測試

/**  * 表實體  */ public class UUID1 implements Serializable {      private Long id;      private String name;      //省略set、get }
/**  * 數(shù)據(jù)持久層操作  */ public interface UUID1Mapper {      /**      * 自增長插入      * @param uuid1      */     @Insert("INSERT INTO tb_uuid_1(name) VALUES(#{name})")     void insert(UUID1 uuid1); }
/**  * 自增ID,單元測試  */ @Test public void testInsert1(){     long start = System.currentTimeMillis();     for (int i = 0; i < 1000000; i++) {         uuid1Mapper.insert(new UUID1().setName("張三"));     }     long end = System.currentTimeMillis();     System.out.println("花費時間:" +  (end - start)); }

2.2、采用隨機(jī)數(shù)生成ID

這里,我們采用twitter的雪花算法來實現(xiàn)隨機(jī)數(shù)ID的生成,工具類如下:

public class SnowflakeIdWorker {      private static SnowflakeIdWorker instance = new SnowflakeIdWorker(0,0);      /**      * 開始時間截 (2015-01-01)      */     private final long twepoch = 1420041600000L;     /**      * 機(jī)器id所占的位數(shù)      */     private final long workerIdBits = 5L;     /**      * 數(shù)據(jù)標(biāo)識id所占的位數(shù)      */     private final long datacenterIdBits = 5L;     /**      * 支持的最大機(jī)器id,結(jié)果是31 (這個移位算法可以很快的計算出幾位二進(jìn)制數(shù)所能表示的最大十進(jìn)制數(shù))      */     private final long maxWorkerId = -1L ^ (-1L << workerIdBits);     /**      * 支持的最大數(shù)據(jù)標(biāo)識id,結(jié)果是31      */     private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);     /**      * 序列在id中占的位數(shù)      */     private final long sequenceBits = 12L;     /**      * 機(jī)器ID向左移12位      */     private final long workerIdShift = sequenceBits;     /**      * 數(shù)據(jù)標(biāo)識id向左移17位(12+5)      */     private final long datacenterIdShift = sequenceBits + workerIdBits;     /**      * 時間截向左移22位(5+5+12)      */     private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;     /**      * 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095)      */     private final long sequenceMask = -1L ^ (-1L << sequenceBits);     /**      * 工作機(jī)器ID(0~31)      */     private long workerId;     /**      * 數(shù)據(jù)中心ID(0~31)      */     private long datacenterId;     /**      * 毫秒內(nèi)序列(0~4095)      */     private long sequence = 0L;     /**      * 上次生成ID的時間截      */     private long lastTimestamp = -1L;     /**      * 構(gòu)造函數(shù)      * @param workerId     工作ID (0~31)      * @param datacenterId 數(shù)據(jù)中心ID (0~31)      */     public SnowflakeIdWorker(long workerId, long datacenterId) {         if (workerId > maxWorkerId || workerId < 0) {             throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));         }         if (datacenterId > maxDatacenterId || datacenterId < 0) {             throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));         }         this.workerId = workerId;         this.datacenterId = datacenterId;     }     /**      * 獲得下一個ID (該方法是線程安全的)      * @return SnowflakeId      */     public synchronized long nextId() {         long timestamp = timeGen();         // 如果當(dāng)前時間小于上一次ID生成的時間戳,說明系統(tǒng)時鐘回退過這個時候應(yīng)當(dāng)拋出異常         if (timestamp < lastTimestamp) {             throw new RuntimeException(                     String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));         }         // 如果是同一時間生成的,則進(jìn)行毫秒內(nèi)序列         if (lastTimestamp == timestamp) {             sequence = (sequence + 1) & sequenceMask;             // 毫秒內(nèi)序列溢出             if (sequence == 0) {                 //阻塞到下一個毫秒,獲得新的時間戳                 timestamp = tilNextMillis(lastTimestamp);             }         }         // 時間戳改變,毫秒內(nèi)序列重置         else {             sequence = 0L;         }         // 上次生成ID的時間截         lastTimestamp = timestamp;         // 移位并通過或運算拼到一起組成64位的ID         return ((timestamp - twepoch) << timestampLeftShift) //                 | (datacenterId << datacenterIdShift) //                 | (workerId << workerIdShift) //                 | sequence;     }     /**      * 阻塞到下一個毫秒,直到獲得新的時間戳      * @param lastTimestamp 上次生成ID的時間截      * @return 當(dāng)前時間戳      */     protected long tilNextMillis(long lastTimestamp) {         long timestamp = timeGen();         while (timestamp <= lastTimestamp) {             timestamp = timeGen();         }         return timestamp;     }     /**      * 返回以毫秒為單位的當(dāng)前時間      * @return 當(dāng)前時間(毫秒)      */     protected long timeGen() {         return System.currentTimeMillis();     }      public static SnowflakeIdWorker getInstance(){         return instance;     }       public static void main(String[] args) throws InterruptedException {         SnowflakeIdWorker idWorker = SnowflakeIdWorker.getInstance();         for (int i = 0; i < 10; i++) {             long id = idWorker.nextId();             Thread.sleep(1);             System.out.println(id);         }     } }

其他的操作,與上面類似。

2.3、uuid

同樣的,uuid的生成,我們事先也可以將工具類編寫好:

public class UUIDGenerator {      /**      * 獲取uuid      * @return      */     public static String getUUID(){         return UUID.randomUUID().toString();     } }

最后的單元測試,代碼如下:

@RunWith(SpringRunner.class) @SpringBootTest() public class UUID1Test {      private static final Integer MAX_COUNT = 1000000;      @Autowired     private UUID1Mapper uuid1Mapper;      @Autowired     private UUID2Mapper uuid2Mapper;      @Autowired     private UUID3Mapper uuid3Mapper;      /**      * 測試自增ID耗時      */     @Test     public void testInsert1(){         long start = System.currentTimeMillis();         for (int i = 0; i < MAX_COUNT; i++) {             uuid1Mapper.insert(new UUID1().setName("張三"));         }         long end = System.currentTimeMillis();         System.out.println("自增ID,花費時間:" +  (end - start));     }      /**      * 測試采用雪花算法生產(chǎn)的隨機(jī)數(shù)ID耗時      */     @Test     public void testInsert2(){         long start = System.currentTimeMillis();         for (int i = 0; i < MAX_COUNT; i++) {             long id = SnowflakeIdWorker.getInstance().nextId();             uuid2Mapper.insert(new UUID2().setId(id).setName("張三"));         }         long end = System.currentTimeMillis();         System.out.println("花費時間:" +  (end - start));     }      /**      * 測試采用UUID生成的ID耗時      */     @Test     public void testInsert3(){         long start = System.currentTimeMillis();         for (int i = 0; i < MAX_COUNT; i++) {             String id = UUIDGenerator.getUUID();             uuid3Mapper.insert(new UUID3().setId(id).setName("張三"));         }         long end = System.currentTimeMillis();         System.out.println("花費時間:" +  (end - start));     } }

三、性能測試

程序環(huán)境搭建完成之后,啥也不說了,直接擼起袖子,將單元測試跑起來!

首先測試一下,插入100萬數(shù)據(jù)的情況下,三者直接的耗時結(jié)果如下:

為什么不能用uuid作為數(shù)據(jù)庫主鍵

為什么不能用uuid作為數(shù)據(jù)庫主鍵

為什么不能用uuid作為數(shù)據(jù)庫主鍵

為什么不能用uuid作為數(shù)據(jù)庫主鍵

在原有的數(shù)據(jù)量上,我們繼續(xù)插入30萬條數(shù)據(jù),三者耗時結(jié)果如下:

為什么不能用uuid作為數(shù)據(jù)庫主鍵

可以看出在數(shù)據(jù)量 100W 左右的時候,uuid的插入效率墊底,隨著插入的數(shù)據(jù)量增長,uuid 生成的ID插入呈直線下降!

時間占用量總體效率排名為:自增ID > 雪花算法生成的ID >> uuid生成的ID。

在數(shù)據(jù)量較大的情況下,為什么uuid生成的ID遠(yuǎn)不如自增ID呢?

關(guān)于這點,我們可以從 MySQL 主鍵存儲的內(nèi)部結(jié)構(gòu)來進(jìn)行分析。

3.1、自增ID內(nèi)部結(jié)構(gòu)

自增的主鍵的值是順序的,所以 Innodb 把每一條記錄都存儲在一條記錄的后面。

為什么不能用uuid作為數(shù)據(jù)庫主鍵

當(dāng)達(dá)到頁面的最大填充因子時候(innodb默認(rèn)的最大填充因子是頁大小的15/16,會留出1/16的空間留作以后的修改),會進(jìn)行如下操作:

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

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

3.2、使用uuid的索引內(nèi)部結(jié)構(gòu)

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

為什么不能用uuid作為數(shù)據(jù)庫主鍵

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

  • 寫入的目標(biāo)頁很可能已經(jīng)刷新到磁盤上并且從緩存上移除,或者還沒有被加載到緩存中,innodb在插入之前不得不先找到并從磁盤讀取目標(biāo)頁到內(nèi)存中,這將導(dǎo)致大量的隨機(jī)IO

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

  • 由于頻繁的頁分裂,頁會變得稀疏并被不規(guī)則的填充,最終會導(dǎo)致數(shù)據(jù)會有碎片

在把值載入到聚簇索引(innodb默認(rèn)的索引類型)以后,有時候會需要做一次OPTIMEIZE  TABLE來重建表并優(yōu)化頁的填充,這將又需要一定的時間消耗。

因此,在選擇主鍵ID生成方案的時候,盡可能別采用uuid的方式來生成主鍵ID,隨著數(shù)據(jù)量越大,插入性能會越低!

四、總結(jié)

在實際使用過程中,推薦使用主鍵自增ID和雪花算法生成的隨機(jī)ID。

但是使用自增ID也有缺點:

1、別人一旦爬取你的數(shù)據(jù)庫,就可以根據(jù)數(shù)據(jù)庫的自增id獲取到你的業(yè)務(wù)增長信息,很容易進(jìn)行數(shù)據(jù)竊取。2、其次,對于高并發(fā)的負(fù)載,innodb在按主鍵進(jìn)行插入的時候會造成明顯的鎖爭用,主鍵的上界會成為爭搶的熱點,因為所有的插入都發(fā)生在這里,并發(fā)插入會導(dǎo)致間隙鎖競爭。

總結(jié)起來,如果業(yè)務(wù)量小,推薦采用自增ID,如果業(yè)務(wù)量大,推薦采用雪花算法生成的隨機(jī)ID。

到此,關(guān)于“為什么不能用uuid作為數(shù)據(jù)庫主鍵”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

標(biāo)題名稱:為什么不能用uuid作為數(shù)據(jù)庫主鍵
網(wǎng)址分享:http://aaarwkj.com/article24/pdegje.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營銷型網(wǎng)站建設(shè)網(wǎng)站設(shè)計公司、網(wǎng)站設(shè)計、網(wǎng)站收錄、移動網(wǎng)站建設(shè)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作
成人午夜黄色福利视频| 在线中文字幕日韩有码| 亚洲女同成人在线观看| 人妻少妇系列一区二区| 亚洲黄色片一区二区三区| 亚洲av天堂在线观看| 国产欧美日韩综合激情| 日韩熟女av中文字幕| 亚洲一区二区三区精品国产| 欧美精品久久久久九九九| 亚洲欧美日韩精品麻豆| 日本免费精品人成视频| 日本不卡一区二区在线播放| 丰满少妇一区二区三区专区| 亚洲欧美日韩一区91| 欧美大片高清在线观看| 久久日韩制服丝袜人妻| 美女呻吟被爽到高潮在线| 亚洲欧美综合精品二区| 久久国产亚洲欧美日韩精品| 亚洲一区二区三区在线播| 成人在线视频国产自拍| 18禁视频免费无遮挡| 在线观看亚洲毛片网站| 国产av自拍在线免费观看| 天天免费日日夜夜夜夜| av电影网站中文字幕| 亚洲欧美日韩一区二区视频| 久久精人妻一区二区三区| 国产成人激情自拍视频在线观看| 老汉av免费在线观看| 在线免费观看成人午夜福利| 在线观看国产高清自拍| 欧美另类精品一区二区| 免费在线观看欧美色妇| 91麻豆精品国产综合久久久| 极品美女粉嫩啪啪高潮| 蜜臀午夜精品视频在线观看| 亚洲人成伊人成综合网中文| 高清欧美一区二区三区日本| 欧美午夜福利一级高清|