前言
在mysql中設計表的時候,mysql官方建議不要使用uuid或者不連續不重復的雪花id(長且唯一,單機遞增),而是推薦連續自增的主鍵id。官方推薦是auto_increment,那為什么不用uuid呢?使用uuid的缺點是什么?在這篇博客中,我們將分析這個問題,并討論其內在原因。這個博客目錄mysql程序的例子
使用uuid和自增長id的索引結構的比較
摘要
一、mysql和程序實例
1.1.要說明這個問題,我們首先來建立三張表
分別是user _ auto _ key、user _ uuid、user _ random _ key,分別代表自動增長主鍵,uuid為主鍵,random key為主鍵。其余的完全沒變。按照控制變量法,我們只使用不同的策略生成每個表的主鍵,其他字段完全一樣,然后測試表的插入速度和查詢速度。注意:這里的隨機鍵其實指的是雪花算法。
自動id生成表:
用戶uuid表
隨機主鍵表:
1.2.光有理論不行,直接上程序,使用spring的jdbcTemplate來實現增查測試:
技術框架:Spring Boot JDBC模板JUnit胡工具。程序的原理是連接你自己的測試數據庫,然后在同樣的環境下寫同樣的數據量分析插入時間來合成它的效率。為了達到最真實的效果,所有數據都是隨機生成的,比如姓名、郵箱、地址。package com . wyq . MySQL demo;
import cn . Hu tool . core . collection . collection util;
import com . wyq . MySQL demo . database object . userkeyauto;
import com . wyq . MySQL demo . database object . userkeyrandom;
import com . wyq . MySQL demo . database object . userkeyuuid;
import com . wyq . MySQL demo . diffkeytest . autokeytableservice;
import com . wyq . MySQL demo . diffkeytest . randomkeytableservice;
進口nb
sp;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.程序寫入結果
user_key_auto寫入結果:


1.4.效率測試結果


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

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

2.3.使用自增id的缺點
那么使用自增的id就完全沒有壞處了嗎?并不是,自增id也會存在以下幾點問題:①別人一旦爬取你的數據庫,就可以根據數據庫的自增id獲取到你的業務增長信息,很容易分析出你的經營情況②對于高并發的負載,innodb在按主鍵進行插入的時候會造成明顯的鎖爭用,主鍵的上界會成為爭搶的熱點,因為所有的插入都發生在這里,并發插入會導致間隙鎖競爭③Auto_Increment鎖機制會造成自增鎖的搶奪,有一定的性能損失附:Auto_increment的鎖爭搶問題,如果要改善需要調優innodb_autoinc_lock_mode的配置
三、總結
本篇博客首先從開篇的提出問題,建表到使用jdbcTemplate去測試不同id的生成策略在大數據量的數據插入表現,然后分析了id的機制不同在mysql的索引結構以及優缺點,深入的解釋了為何uuid和隨機不重復id在數據插入中的性能損耗,詳細的解釋了這個問題。在實際的開發中還是根據mysql的官方推薦最好使用自增id,mysql博大精深,內部還有很多值得優化的點需要我們學習。