考虑到以下情况,什么主键策略最适合用于关系数据库模型?
- 数以万计的用户
- 每个用户有多个客户端(手机、平板电脑、台式机)
- 每张表数百万行(不断增长)
Azure SQL 将是中央数据存储,将通过 Web API 公开。客户端将包括一个 Web 应用程序和许多本机应用程序,包括 iOS、Android、Mac、Windows 8 等。Web 应用程序将需要“始终在线”连接,并且没有本地数据存储,而是检索和更新通过 api - 通过 RESTful API 思考 CRUD。
所有其他客户端(手机、平板电脑、台式机)都将具有本地数据库(SQLite)。首次使用此类客户端时,用户必须进行身份验证和同步。一旦经过身份验证和同步,这些客户端就可以在离线模式下运行(在本地 SQLite 数据库中创建、删除和更新记录)。这些更改最终将与 Azure 后端同步。
数据库的分布式特性给我们留下了一个主键问题以及提出这个问题的原因。
以下是我们迄今为止所考虑的:
图形用户界面
每个客户端都创建自己的密钥。在同步时,重复密钥的可能性很小,但我们需要通过将功能写入每个客户端来使用新密钥更新所有关系来解决这个问题。GUID 很大,当考虑到每个表有多个外键时,随着时间的推移,存储可能会成为一个问题。可能最大的问题是 GUID 的随机性,这意味着由于碎片,它们不能(或不应该)用作聚集索引。这意味着我们需要为每个表创建一个聚集索引(可能是任意的)。
身份
每个客户端都创建自己的主键。同步时,这些密钥将替换为服务器生成的密钥。这给同步过程增加了额外的复杂性,并迫使每个客户端“修复”他们的键,包括相关表上的所有外键。
合成的
在第一次同步时,每个客户端都会分配一个客户端 ID。此客户端 ID 与本地自动递增 ID 结合使用,作为每个表的复合主键。此复合键将是唯一的,因此同步时不应该有冲突,但这确实意味着大多数表都需要复合主键。性能和查询复杂性是这里关注的问题。
HiLo(合并复合)
与复合方法一样,每个客户端在第一次同步时都被分配一个客户端 ID (int32)。客户端 ID 与唯一的本地 ID (int32) 合并到一个列中,以形成应用程序范围内的唯一 ID (int64)。这应该不会导致同步期间发生冲突。由于每个客户端生成的 id 是连续的,因此这些键与 GUID 相比有更多的顺序,但会有数千个唯一的客户端 ID,那么我们是否仍然冒着聚集索引碎片的风险?
我们是否忽略了什么?还有其他值得研究的方法吗?讨论每种方法的优缺点会很有帮助。