Mysql为什么不用uuid作为表的主键

探讨UUID生成速率低下问题,特别是在多线程环境下,以及UUID作为InnoDB主键时引发的性能下降。分析原因包括SecureRandom依赖、B+树叶子节点分裂及缓存效率降低。
  1. UUID生成速率低下

Java的UUID依赖于SecureRandom.nextBytes方法,而SecureRandom又依赖于操作系统提供的随机数源,在Linux系统下,它的默认依赖是/dev/random,而这个源是阻塞的。最可怕的是,这个nextBytes方法还是一个synchronized方法,也就是说,如果多线程调用UUID,生成速率不升反降。

测试结果:在一台64线程的服务器上,调用UUID.randomUUID方法,生成一千万个uuid平均耗时在130s,tps不到8w

  1. UUID主键在innodb中会引发性能问题

a. innodb中的主键索引也是聚集索引,如果插入的数据是顺序的,那么b+树的叶子基本都是满的,缓存也可以很好的发挥作用。如果插入的数据是完全无序的,那么叶子节点会频繁分裂,缓存也基本无效了。这会减少tps

b. uuid占用的空间较大

  1. UUID完全没有意义,如果有一个主键是全局自增的,那么数据排列顺序就是数据的插入顺序
你提到的是“Python 生成 UUID 作为 MySQL 主键”,但当前上下文是 **Java 专家**,因此我将理解为:你可能是在对比 Python 的做法,而真正想问的是如何在 **Java 中生成 UUID 并用作 MySQL 主键**。下面我将从 Java 的角度详细回答这个问题。 --- ### 回答问题: 在 Java 应用中,可以使用 `java.util.UUID` 生成全局唯一标识符(UUID),并将其作为 MySQL 数据主键,替代传统的自增 ID(`AUTO_INCREMENT`)。这种方式适用于分布式系统,避免主键冲突。 #### 1. MySQL 结构设计 MySQL 中存储 UUID 推荐使用 `CHAR(36)` 或更高效的 `BINARY(16)`(如果存储为二进制格式)。 ```sql -- 方式一:使用 CHAR(36) 存储字符串形式的 UUID(如 '550e8400-e29b-41d4-a716-446655440000') CREATE TABLE users ( id CHAR(36) PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) ); -- 方式二(推荐):使用 BINARY(16) 提高性能和节省空间 CREATE TABLE users_binary ( id BINARY(16) PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) ); ``` --- #### 2. Java 代码示例:生成 UUID 并插入 MySQL ##### ✅ 使用 `CHAR(36)` 存储(简单直观) ```java import java.sql.*; import java.util.UUID; public class UuidAsPrimaryKey { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/testdb"; String username = "root"; String password = "password"; // 生成 UUID(版本 4:随机) UUID uuid = UUID.randomUUID(); String uuidString = uuid.toString(); // 格式:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; try (Connection conn = DriverManager.getConnection(url, username, password); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, uuidString); pstmt.setString(2, "Alice"); pstmt.setString(3, "alice@example.com"); int rows = pstmt.executeUpdate(); if (rows > 0) { System.out.println("插入成功,UUID 主键: " + uuidString); } } catch (SQLException e) { e.printStackTrace(); } } } ``` --- ##### ✅ 使用 `BINARY(16)` 存储(高效存储,推荐用于大数据量) UUID 本质上是 128 位(16 字节),可以用二进制方式存储,节省空间并提高索引效率。 ```java import java.sql.*; import java.util.UUID; public class UuidBinaryStorage { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/testdb"; String username = "root"; String password = "password"; UUID uuid = UUID.randomUUID(); // 将 UUID 转为字节数组(16 字节) byte[] bytes = new byte[16]; long msb = uuid.getMostSignificantBits(); long lsb = uuid.getLeastSignificantBits(); for (int i = 0; i < 8; i++) { bytes[i] = (byte) (msb >>> (8 * (7 - i))); bytes[i + 8] = (byte) (lsb >>> (8 * (7 - i))); } String sql = "INSERT INTO users_binary (id, name, email) VALUES (?, ?, ?)"; try (Connection conn = DriverManager.getConnection(url, username, password); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setBytes(1, bytes); pstmt.setString(2, "Bob"); pstmt.setString(3, "bob@example.com"); int rows = pstmt.executeUpdate(); if (rows > 0) { System.out.println("插入成功,二进制 UUID 主键: " + uuid.toString()); } } catch (SQLException e) { e.printStackTrace(); } } } ``` > ⚠️ 查询时也需要将 UUID 转为字节数组进行比较。 --- ### 解释: - **为什么使用 UUID?** - 分布式系统中避免主键冲突。 - 客户端可生成 ID,减少数据库 round-trip。 - 更安全(不像自增 ID 可被猜测)。 - **UUID 存储方式对比:** | 存储类型 | 空间占用 | 性能 | 可读性 | 推荐场景 | |------------|--------|------|------|----------------| | `CHAR(36)` | 36 字节 | 较低 | 高 | 开发调试、小数据量 | | `BINARY(16)` | 16 字节 | 高 | 低 | 生产环境、大数据量 | - **UUID 版本说明:** - `UUID.randomUUID()` → 生成 v4 UUID(基于随机数) - 还有 v1(时间+MAC)、v3/v5(命名空间哈希)等,根据需求选择。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值