LangFlow Qualys资产发现与风险评估
2025/12/23 2:40:50
| 维度 | 自增ID | UUID |
|---|---|---|
| 存储空间 | 4-8字节(int/bigint) | 16字节(128位) |
| 生成方式 | 数据库自动递增 | 应用层生成(随机/有序) |
| 顺序性 | 严格递增,天然有序 | 通常无序(有序UUID除外) |
| 唯一性范围 | 单库/表内唯一 | 全球唯一 |
| 可读性 | 简单直观(1,2,3…) | 复杂(550e8400-e29b-41d4-a716-446655440000) |
| 安全性 | 暴露业务量,易遍历 | 难以猜测,相对安全 |
-- MySQL表结构对比-- 自增ID表CREATETABLEusers_auto(idBIGINTAUTO_INCREMENTPRIMARYKEY,-- 8字节nameVARCHAR(100),emailVARCHAR(255),INDEXidx_name(name));-- UUID表CREATETABLEusers_uuid(idCHAR(36)PRIMARYKEYDEFAULT(UUID()),-- 36字符 ≈ 36字节(实际存储36字节)nameVARCHAR(100),emailVARCHAR(255),INDEXidx_name(name));-- 二进制UUID更高效CREATETABLEusers_uuid_bin(idBINARY(16)PRIMARYKEYDEFAULT(UUID_TO_BIN(UUID())),-- 16字节nameVARCHAR(100),emailVARCHAR(255));性能影响分析:
-- 性能测试示例(MySQL)-- 测试1:连续插入100万条数据SETprofiling=1;-- 自增ID表INSERTINTOusers_auto(name,email)SELECTCONCAT('user',n),CONCAT('user',n,'@test.com')FROMgenerate_series(1,1000000)ASn;-- UUID表(随机)INSERTINTOusers_uuid(name,email)SELECTCONCAT('user',n),CONCAT('user',n,'@test.com')FROMgenerate_series(1,1000000)ASn;SHOWPROFILES;-- 结果:自增ID插入耗时通常比UUID快2-3倍自增ID的B+树索引: [1,2,3,4,5] ← 插入6 → [1,2,3,4,5,6]// 追加到末尾,无分裂 随机UUID的B+树索引: [A,C,E,G,I] ← 插入B → 需要分裂和重平衡 有序UUID的B+树索引: [U1,U2,U3,U4] ← 插入U5(时间顺序) → [U1,U2,U3,U4,U5] // 类似自增ID-- 电商订单系统(MySQL)CREATETABLEorders(idBIGINTUNSIGNEDAUTO_INCREMENTPRIMARYKEY,-- 自增IDorder_noVARCHAR(32)UNIQUE,-- 业务订单号(对外)user_idINTNOTNULL,amountDECIMAL(10,2),created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP,INDEXidx_user_created(user_id,created_at),-- 复合索引高效INDEXidx_order_no(order_no));-- 优势:快速插入,索引紧凑,范围查询高效SELECT*FROMordersWHEREuser_id=1001ANDcreated_atBETWEEN'2024-01-01'AND'2024-01-31'ORDERBYidDESC;-- 按自增ID排序效率高-- 用户-订单关系(外键关联)CREATETABLEusers(idINTAUTO_INCREMENTPRIMARYKEY,usernameVARCHAR(50)UNIQUE);CREATETABLEorders(idINTAUTO_INCREMENTPRIMARYKEY,user_idINT,FOREIGNKEY(user_id)REFERENCESusers(id)ONDELETECASCADE);-- 优势:外键关联更紧凑,JOIN性能更好SELECTu.username,o.*FROMusers uJOINorders oONu.id=o.user_id-- 整型JOIN效率高WHEREu.idIN(1001,1002,1003);-- 基于自增ID的高效分页-- 传统分页(数据量大时慢)SELECT*FROMproductsORDERBYidLIMIT1000000,20;-- 优化分页(使用自增ID)SELECT*FROMproductsWHEREid>1000000-- 记录上次查询的最大IDORDERBYidLIMIT20;// 微服务架构,多数据库实例// 服务A(用户服务)- 数据库A@EntitypublicclassUser{@Id@GeneratedValue(generator="UUID")@GenericGenerator(name="UUID",strategy="org.hibernate.id.UUIDGenerator")@Column(columnDefinition="BINARY(16)")// 使用16字节存储privateUUIDid;privateStringusername;}// 服务B(订单服务)- 数据库B@EntitypublicclassOrder{@Id@GeneratedValue(generator="UUID")@GenericGenerator(name="UUID",strategy="org.hibernate.id.UUIDGenerator")@Column(columnDefinition="BINARY(16)")privateUUIDid;@Column(columnDefinition="BINARY(16)")privateUUIDuserId;// 跨服务引用,无需中心分配}// 前端JavaScript生成UUID,离线创建数据classOfflineTodoApp{createTodo(text){consttodo={id:crypto.randomUUID(),// 前端生成UUIDtext:text,completed:false,createdAt:newDate().toISOString()};// 本地存储localStorage.setItem(`todo_${todo.id}`,JSON.stringify(todo));// 同步到服务器时无需ID转换fetch('/api/todos',{method:'POST',body:JSON.stringify(todo)});}}-- 分支机构数据合并到总部-- 分支A数据INSERTINTOcustomers_branch_a(id,name)VALUES('550e8400-e29b-41d4-a716-446655440001','Alice'),('550e8400-e29b-41d4-a716-446655440002','Bob');-- 分支B数据INSERTINTOcustomers_branch_b(id,name)VALUES('6ba7b810-9dad-11d1-80b4-00c04fd430c1','Charlie'),('6ba7b811-9dad-11d1-80b4-00c04fd430c2','David');-- 合并到总部(无ID冲突)INSERTINTOcustomers_headquarters(id,name,branch)SELECTid,name,'A'FROMcustomers_branch_aUNIONALLSELECTid,name,'B'FROMcustomers_branch_b;CREATETABLEusers(-- 内部使用:自增ID,用于关联和索引internal_idBIGINTAUTO_INCREMENTPRIMARYKEY,-- 对外暴露:UUID,保证安全性和唯一性public_idCHAR(36)UNIQUENOTNULLDEFAULT(UUID()),usernameVARCHAR(50),emailVARCHAR(255),INDEXidx_public_id(public_id),INDEXidx_username(username));-- 对外API返回public_id-- 内部关联使用internal_id-- MySQL 8.0+ 使用有序UUIDCREATETABLEevents(idBINARY(16)PRIMARYKEYDEFAULT(UUID_TO_BIN(UUID(),1)),-- 参数1表示有序UUIDevent_typeVARCHAR(50),created_atTIMESTAMP(6)DEFAULTCURRENT_TIMESTAMP(6),INDEXidx_created(created_at));-- 有序UUID生成原理(时间戳 + 随机部分)-- 2024年时间戳(6字节) + 随机值(10字节)-- 插入时基本按时间顺序,减少索引分裂-- 方案对比-- 自增ID方案CREATETABLEseckill_orders(idBIGINTAUTO_INCREMENTPRIMARYKEY,order_noVARCHAR(32),-- 需要额外生成唯一订单号user_idINT,product_idINT,INDEXidx_user_product(user_id,product_id));-- 问题:热点写入,最后一个数据页竞争-- UUID方案(有序v7)CREATETABLEseckill_orders(idBINARY(16)PRIMARYKEYDEFAULT(UUID_TO_BIN(UUID(),1)),user_idINT,product_idINT,INDEXidx_user_product(user_id,product_id));-- 优势:分散写入热点,但索引效率降低-- 最佳实践:分库分表 + 雪花ID-- 自增ID的迁移问题-- 源数据库INSERTINTOusers(id,name)VALUES(1001,'Alice');-- 目标数据库(已有数据)INSERTINTOusers(id,name)VALUES(1,'Bob');-- 迁移时ID冲突,需要重置自增或重新映射SETFOREIGN_KEY_CHECKS=0;INSERTINTOtarget_users(id,name)SELECTid+1000000,nameFROMsource_users;-- 需要偏移SETFOREIGN_KEY_CHECKS=1;-- UUID无此问题INSERTINTOtarget_users(id,name)SELECTid,nameFROMsource_users;-- 直接迁移-- 自增ID的安全隐患-- 容易推测数据量:id=1000000 表示有100万用户-- 容易遍历:/api/users/1, /api/users/2, ...-- 可能暴露业务信息:订单ID连续可能暴露订单量-- UUID解决方案CREATETABLEusers(idCHAR(36)PRIMARYKEYDEFAULT(UUID()),internal_idSERIAL,-- 内部管理用,不对外暴露emailVARCHAR(255),INDEXidx_internal(internal_id));-- API返回UUID,不返回自增IDGET/api/users/550e8400-e29b-41d4-a716-446655440000-- 无法推测其他用户ID// Twitter Snowflake:结合时间戳+机器ID+序列号// 64位ID结构:1位符号位 + 41位时间戳 + 10位机器ID + 12位序列号publicclassSnowflakeIdGenerator{privatefinallongmachineId;privatelongsequence=0L;privatelonglastTimestamp=-1L;publicsynchronizedlongnextId(){longtimestamp=System.currentTimeMillis();if(timestamp<lastTimestamp){thrownewRuntimeException("时钟回拨");}if(timestamp==lastTimestamp){sequence=(sequence+1)&4095;// 12位序列号if(sequence==0){timestamp=tilNextMillis(lastTimestamp);}}else{sequence=0L;}lastTimestamp=timestamp;return((timestamp-1288834974657L)<<22)// 41位时间戳|(machineId<<12)// 10位机器ID|sequence;// 12位序列号}}-- PostgreSQL序列CREATESEQUENCE global_id_seqSTART1INCREMENT1;CREATETABLEusers(idBIGINTPRIMARYKEYDEFAULTnextval('global_id_seq'),nameVARCHAR(100));-- 多表共享序列,全局唯一IDINSERTINTOorders(id,user_id)VALUES(nextval('global_id_seq'),1001);INSERTINTOproducts(id,name)VALUES(nextval('global_id_seq'),'iPhone');// ULID:26字符,时间有序,Crockford's Base32编码// 结构:48位时间戳 + 80位随机数constulid=ULID.ulid();// 示例:01ARYZ6S41TSV4RRFFQ69G5FAV// 特性:// 1. 按时间排序// 2. 比UUID更短(26 vs 36字符)// 3. 没有特殊字符,URL安全// 4. 128位,与UUID相同熵-- 使用BINARY(16)代替CHAR(36)-- 存储空间:36字节 → 16字节-- 查询性能:提升约30%-- 创建表CREATETABLEusers(idBINARY(16)PRIMARYKEYDEFAULT(UUID_TO_BIN(UUID())),nameVARCHAR(100));-- 插入时转换INSERTINTOusers(id,name)VALUES(UUID_TO_BIN(UUID()),'Alice');-- 查询时转换回字符串SELECTBIN_TO_UUID(id),nameFROMusersWHEREid=UUID_TO_BIN('uuid-string');-- 有序UUID(UUID v7/v8)INSERTINTOusers(id,name)VALUES(UUID_TO_BIN(UUID(),1),'Bob');-- 对于UUID主键,创建复合索引提升查询性能CREATETABLEorders(idCHAR(36)PRIMARYKEY,user_idINTNOTNULL,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP,-- 复合索引:优先高频查询条件INDEXidx_user_created(user_id,created_at),INDEXidx_created_id(created_at,id));-- 查询使用覆盖索引SELECTid,user_idFROMordersWHEREuser_id=1001ANDcreated_at>='2024-01-01'ORDERBYcreated_atDESC;-- UUID分页的Keyset Pagination-- 第一页SELECT*FROMproductsWHEREcreated_at>'2024-01-01'ORDERBYcreated_at,id-- 加上ID保证顺序稳定LIMIT20;-- 下一页:记住上一页最后一条的created_at和idSELECT*FROMproductsWHERE(created_at>'2024-01-10'OR(created_at='2024-01-10'ANDid>'last-uuid'))ORDERBYcreated_at,idLIMIT20;开始选择主键 │ ├── 是否分布式系统? │├── 是 → 继续 │└── 否 → 考虑自增ID │ ├── 是否需要前端生成ID? │├── 是 → UUID或ULID │└── 否 → 继续 │ ├── 是否有高并发写入? │├── 是 → 雪花算法或有序UUID │└── 否 → 继续 │ ├── 是否需要数据合并? │├── 是 → UUID或ULID │└── 否 → 继续 │ ├── 是否对安全要求高? │├── 是 → UUID(不暴露业务信息) │└── 否 → 继续 │ ├── 存储成本是否敏感? │├── 是 → 自增ID(节约存储) │└── 否 → 继续 │ └── 性能要求? ├── 读多写少 → 自增ID ├── 写多读少 → 有序UUID或雪花算法 └── 读写均衡 → 根据其他因素决定记住:没有绝对最好的方案,只有最适合当前业务场景的方案。在系统设计初期就要考虑到未来的扩展性需求。