电话号码是否作为主键?如果是,如何处理国际区号?

Maximize job database potential with expert discussions and advice.
Post Reply
muskanislam99
Posts: 243
Joined: Sat Dec 28, 2024 5:47 am

电话号码是否作为主键?如果是,如何处理国际区号?

Post by muskanislam99 »

对于像 WhatsApp 这样以电话号码作为核心用户身份的应用,电话号码是否作为主键是一个关键设计决策。简短的回答是:通常不会将原始电话号码直接作为数据库主键,但会使用电话号码的标准化形式作为用户身份的核心标识符,并基于此生成或关联一个独立的代理主键。

1. 为什么不直接使用原始电话号码作为主键?
尽管电话号码是 WhatsApp 用户身份的基石,但直接将其作为主键存在几个问题:

数据格式不一致: 电话号码的格式多种多样(例如,带空格、破折号、括号、国家代码前缀“+”或“00”等)。直接使用原始格式会造成数据混乱和查询困难。
可变性: 尽管不常见,但用户可能会更换电话号码。如果电话号码是主键,更改主键会非常复杂,涉及大量相关表的级联更新,或者需要删除旧记录并创建新记录,这会带来数据一致性问题和操作负担。
隐私和安全: 将电话号码作为主键意味着它在数据库中无处不在,增加了数据泄露的风险。使用代理主键可以更好地隔离敏感信息。
性能: 字符串类型的主键通常不如整数类型的主键在索引和查询性能上高效,尤其是在处理 JOIN 操作时。
国际区号和本地格式: 电话号码必须包含国际区号才能在全球范围内唯一识别用户,但用户在输入时可能只输入本地格式。这需要在存储前进行标准化处理。
2. 推荐方案:代理主键 + 标准化电话号码作为唯一索引
最佳实践是使用一个独立的、不可变的代理主键(Surrogate Primary Key),并结合标准化后的电话号码作为唯一索引来标识用户。

Users 表结构示例:

SQL

CREATE TABLE Users (
**user_id BIGINT PRIMARY KEY, -- 代理主键,如自增ID或UUID**
**phone_number_standardized VARCHAR(20) UNIQUE NOT NULL, -- 标准化后的电话号码,唯一索引**
country_code VARCHAR(5) NOT NULL, -- 国际区号 (如 "+1", "+91")
national_number VARCHAR(15) NOT NULL, -- 国内部分号码 (不含区号)
display_name VARCHAR(255), -- 用户显示名称
profile_picture_url VARCHAR(255),
about_text TEXT,
last_seen_timestamp BIGINT,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
-- ... 其他用户信息字段
);
user_id:

这是一个代理主键,通常是 BIGINT 类型的自增 ID 或 UUID (Universally Unique Identifier)。
它是用户在整个 WhatsApp 系统中的唯一且不 捷克共和国 whatsapp 数据库 可变的标识符。所有其他表(如 Messages, GroupMembers, CallRecords, Payments 等)都将使用 user_id 作为外键来引用用户。
优点: 不变性、性能高、隐私性更好。
phone_number_standardized:

这是一个标准化后的电话号码,通常采用 E.164 国际标准格式。
格式: +[国家代码][区号][本地号码],例如 +12125550123 (美国纽约), +919876543210 (印度)。这种格式是全球唯一的。
数据类型: VARCHAR(20) 足够存储所有符合 E.164 标准的号码(最长 15 位数字 + + 号)。
唯一索引: 这个字段上会建立一个唯一索引,确保每个标准化后的电话号码只对应一个用户。
作用: 用于用户登录、查找联系人以及在服务器端识别用户。
country_code 和 national_number:

这些字段是可选的,但有助于更好地管理和显示电话号码,以及支持国际化功能。它们可以将标准化号码拆分为其组成部分。
3. 如何处理国际区号和标准化?
这是一个在用户注册和联系人导入时就必须解决的关键步骤:

用户输入/注册:

当用户注册或登录时,客户端会引导用户输入电话号码,并通常会提供国家/地区选择器来辅助输入正确的国际区号。
客户端或服务器会使用电话号码解析库(如 Google 的 libphonenumber 库,WhatsApp 可能会有自己的定制版本)来:
解析: 从用户的输入中识别出国家代码、国家部分号码等。
验证: 检查号码是否有效、是否为实际可用的号码。
格式化: 将号码统一格式化为 E.164 标准格式 (+<country_code><national_number>)。
存储: 将格式化后的 E.164 号码存储到 phone_number_standardized 字段中。
联系人匹配:

当用户导入本地通讯录时,WhatsApp 客户端会将通讯录中的电话号码发送到服务器。
服务器会使用相同的电话号码解析和格式化逻辑,将联系人号码标准化为 E.164 格式。
然后,服务器通过查询 Users 表中 phone_number_standardized 字段,来匹配哪些本地联系人已经是 WhatsApp 用户。
用户更改电话号码:

如果用户更改电话号码,他们会经历一个特定的流程。
在数据库层面,这会涉及更新 Users 表中该 user_id 对应的 phone_number_standardized 字段。由于 user_id 是主键且不变,这个操作对其他表没有影响,极大地简化了数据管理。
总结
WhatsApp 不会直接将原始电话号码作为主键。相反,它会采用以下策略:

使用一个独立的 BIGINT 类型代理主键 (user_id) 来唯一标识用户,所有其他表都通过此 ID 引用用户。
维护一个 phone_number_standardized 字段,存储符合 E.164 国际标准格式的电话号码。这个字段上建立唯一索引,用于高效地识别用户和执行联系人匹配。
通过在客户端和服务器端使用电话号码解析和格式化库来严格处理国际区号和各种电话号码格式,确保存储的标准化格式是全球唯一的。
Post Reply