业务场景
在做数据分析的时候,我们有很多业务场景是需要进行数据覆盖的,用最新数据去覆盖旧数据。
而实现数据覆盖,在数据库里,只需要保证数据的主健相同即可。
正因为有数据覆盖的需求,我们需要有一个根据业务信息生成可覆盖ID的最佳实践。
主键类型
数据库主健指的是一个列或多列的组合,其值能唯一地标识表中的每一行,通过它可强制表的实体完整性。
一般分为逻辑主键和业务主键
- 逻辑主键(代理主键):在数据库表中采用一个与当前表中业务逻辑信息无关的字段作为其主键,或称为“伪主键”;
- 业务主键(自然主键):在数据库表中把具有业务逻辑含义的字段作为主键。
逻辑主键
其中,在 mysql 中,是自增(auto_increment)算法,在 mongodb 中,是objectId
算法,在 Elasticsearch 中,是GUID
算法。
其中 GUID 算法生成的id,长度为 20 个字符,URL 安全,base64 编码,分布式系统并行生成时不可能会发生冲突。GUID 算法可以认为是 UUID 的变种。
除此之外,还有 Twitter 开源的Snowflake
算法。
objectId
算法 、GUID
算法和Snowflake
算法,可以理解都是分布式全局唯一ID生成算法
业务主键
业务主健,常见是把一个或者多个业务字段设置为主健,这种方法通常会受到长度的限制,解决这个问题的方法是进行对业务信息 hash。
hash 常用的算法有,md和sha,下面是各自的代表算法的对比,md5 vs sha1
MD5 | SHA1 |
---|---|
MD5代表消息摘要。 | SHA1代表安全哈希算法。 |
MD5可以具有128位消息摘要的长度。 | SHA1可以具有160位长度的消息摘要。 |
与SHA1的速度相比, MD5的速度快。 | SHA1的速度比MD5的速度慢。 |
为了找出初始消息, 攻击者需要2 ^ 128次运算, 而要利用MD5算法程序。 | 相反, 在SHA1中, 该值为2 ^ 160, 因此查找起来非常麻烦。 |
MD5比SHA1简单。 | SHA1比MD5更复杂。 |
MD5提供了较弱的安全性。 | 它提供了平衡或可容忍的安全性。 |
在MD5中, 如果攻击者需要找出具有相同消息摘要的2条消息, 则攻击者将需要执行2 ^ 64次操作。 | 在SHA1中, 攻击者将需要执行2 ^ 80次操作, 该操作大于MD5。 |
MD5于1992年提出。 | SHA1于1995年推出。 |
整理可以看到sha1是要比md5更好的,在UUID不同的版本里,其中有两个版本分别使用了这两种算法,其中UUID v3版本使用的是md5,UUID v5版本使用的是sha1。
UUID
UUID由16个字节组成,一共是128位,转换成16进制表示后共有32位,其定义如下:
const Size = 16
type UUID [Size]byte
一般会使用-
来连接其中的各个部分,因此,常见的为36个字符:
784b99c1-2a77-11ec-8421-0800270e658d
| |
版本 变体
在一个UUID中,可以通过上述两个位置得知UUID生成器的版本、以及该版本下的变体版本。UUID现有5个版本,每个版本定义了不同的生成逻辑,因此其安全性和适用场景也有所区别。
version | generation rule |
---|---|
Version 1 | date-time and MAC address |
Version 2 | date-time and MAC address, DCE security version (removed) |
Version 3 | based on MD5 hashing of a named value |
Version 4 | random |
Version 5 | based on SHA-1 hashing of a named value |
最佳实践
js
const uuid = require('uuid')
const namespace = 'bb5d0ffa-9a4c-4d7c-8fc2-0a7d2220ba45'
const needToHashString = 'howtoensuredatacanbecover'
console.log(uuid.v5(needToHashString, namespace))
go
import "github.com/google/uuid"
import "crypto/sha1"
namespace := 'bb5d0ffa-9a4c-4d7c-8fc2-0a7d2220ba45'
needToHashString := 'howtoensuredatacanbecover'
uuid := uuid.NewHash(sha1.New(), namespace, needToHashString, 5)
println(uuid.String())
demo
对一串业务信息进行唯一性id生成,生成 UUID 后,去掉 -
得到32位的字符串,直接就可以用了,不需要再做其他处理。
6215dca1ff35c30b03a52d491650016800000youthwomanentranceoneDay => 18391e4c-a83b-5f6f-abde-6eec799930e5 => 18391e4ca83b5f6fabde6eec799930e5
6215dca1ff35c30b03a52d491650013200000youthmanentranceoneDay => b23dd831-b258-5a94-9e64-51c824431581 => b23dd831b2585a949e6451c824431581
6215dca1ff35c30b03a52d491650013200000middle-agedwomanentranceoneDay => 1c71927b-b0b7-5ea8-9b4b-59aee46f04a4 => 1c71927bb0b75ea89b4b59aee46f04a4
6215dca1ff35c30b03a52d491650013200000middle-agedmanentranceoneDay => 6bb05f1a-9a81-54da-9bd3-e20b5a3cfc9a => 6bb05f1a9a8154da9bd3e20b5a3cfc9a
6215dca1ff35c30b03a52d491650013200000youthwomanentranceoneDay => 32bb7c40-e0af-55d9-8704-f239bd966a56 => 32bb7c40e0af55d98704f239bd966a56
6215dca1ff35c30b03a52d491650009600000youthmanentranceoneDay => 88a43d50-a9bc-5ee2-b25c-c2689837c646 => 88a43d50a9bc5ee2b25cc2689837c646
6215dca1ff35c30b03a52d491650009600000youthwomanentranceoneDay => 15678668-b721-5dd5-980f-dc5c5d925775 => 15678668b7215dd5980fdc5c5d925775
6215dca1ff35c30b03a52d491650009600000youthmanentrancetwoDays => 6dbf9b76-3d4e-505e-92e3-4a1e32b335d3 => 6dbf9b763d4e505e92e34a1e32b335d3
6215dca1ff35c30b03a52d491650006000000youthmanentranceoneDay => d3381c67-679d-53ae-a0a8-2e5bd5295f3c => d3381c67679d53aea0a82e5bd5295f3c
6215dca1ff35c30b03a52d491650006000000middle-agedmanentranceoneDay => ae27d2c2-4dc7-5ff6-bebb-0f980400dec7 => ae27d2c24dc75ff6bebb0f980400dec7
建议
面对需要根据业务字段生成一个全局唯一的id,同时还需要进行数据覆盖,最好按照我提供的方案进行。
方案是:对业务信息进行字符串拼接后,再使用 UUID v5 去hash这个字符串,然后去掉 -
得到32位字符串,就可以拿这个去作为数据的主键。
这个hash处理后的字符串,长度足够短,碰撞率几乎默认没有。
本文由 Chakhsu Lau 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。