14

我将使用多个移动客户端和一个基于 Web 的服务器应用程序来实现一个分布式应用程序。所以每个客户端和服务器都可以生成表条目。因此,我需要所有参与者的唯一主键,并且我希望能够离线生成密钥

生成您在分布式环境中使用的主键的最佳方法是什么?对于类似的问题,请参阅使用 SQLite 和 Azure SQL 数据库作为中央存储的在线/离线多客户端移动应用程序的最佳主键策略是什么?

我知道UUID 密钥生成是该场景的好方法,但我想坚持使用名称为 _id 的密钥并按照 Android 平台的建议键入 long。

我不想使用设备(服务器也是设备)ID 和本地 ID的复合ID。这种方法无论如何都不能很好地工作,因为服务器应该能够为某个客户端生成条目。在这种情况下,我还必须在服务器上使用设备 ID。

因此,我目前最喜欢的是使用 long 数据类型构建我的密钥(我之前在另一个项目中这样做过)。我想我将使用高/低方法(例如,请参见此处的 Hi/Lo 算法是什么?)并有一个密钥,其中包括:

  • 从服务器生成的客户端 ID(例如 ~28 位)
  • 客户端递增的低值(例如〜4位),从不持久
  • 客户端上递增的高值(例如 ~ 32 位),仅在客户端上持续存在

客户端 ID 必须在移动应用程序首次启动时从服务器获取。所以第一次启动需要网络连接。这可能是这种方法的缺点。当设备上有客户端 ID 时,我可以在没有网络连接的情况下生成密钥。

通常,高 id 是数据库中的唯一值。当用户卸载应用程序并再次安装时,我必须将他视为新客户,并且必须给他一个新的客户 ID。否则,我将不得不在服务器上保存当前的高 ID,以便能够在丢失或重新安装时恢复它 - 不值得付出努力。

在 Android 上获得高 id 的最佳方法是什么?自动增量键不是解决方案。我需要类似生成器功能的东西。它必须在自己的事务(而不是“用户”事务)中执行。有没有人在 Android 上使用过这种方法,有人能指出我正确的方向吗?(我只找到了这个答案)。

您对多客户端应用程序(在线和离线)使用什么关键策略?

4

5 回答 5

2

根据我的经验:在设备上使用本地 ID,在服务器上使用单独的 ID。每次通过网络传输数据时,都要从一种转换到另一种。这实际上将澄清过程并简化调试。转换例程保持小,隔离良好,并代表应用程序架构中的自然元素。无论如何,通过网络传输的数据预计会相对较小,并且 ID 转换不会是很大的开销。此外,保存在移动设备上的数据量可能很小(大部分在服务器上)。

我建议使用简单的表local_ID <-> server_ID在设备上进行转换。服务器应该只提供一个过程:生成一批密钥,比如 444 个新密钥,据推测,移动设备随后会将其分配给其本地 ID,并仅使用 server_ID 将数据发送到服务器。转换表可以偶尔清除未使用的 ID,本地 ID 可以重复使用,32 位整数肯定足够了。

动机

表保持小,实现对本机设备架构保持最佳状态,与其他地方不可预测的架构变化隔离,并且有一个很好的调试和跟踪点,所有数据都通过它。

我有一个应用程序重新生成每个数据文件保存和加载的所有 ID。它出乎意料地简单、快速,并开辟了优雅的其他可能性,例如 ID 空间碎片整理和整合。

在您的情况下,您可以通过对客户端应用程序的最小更改来更改服务器技术。由于客户端无论如何都可以离线操作,因此在大多数功能中它只能使用本地 ID。只有同步模块会获取和转换服务器 ID。

于 2014-06-03T07:32:42.133 回答
2

这是更多的问题,然后是答案......

如果您可以自动生成所有 id,它确实会使事情变得更容易,因此您不必从服务器获取它们并担心您是否有连接。您提到您不能采用通用方法(UUID 或 ANDROID_ID),因为您将使用较长的“Android 平台建议的”。

您是指 Android 假定您的 SQLite 表将具有长 _id 主键的事实吗?

您在服务器上使用数据存储还是 SQL 数据库?

如果您正在使用具有分层键的数据存储(例如 google 数据存储),那么如果您使用 UUID/ANDROID_ID 作为客户端 ID,然后使用 long 作为数据项 ID。然后在客户端上你只存储long,在服务器上你的实体用UUID/long的关键路径存储。

为什么要写“高 id 必须是数据库中的唯一值”?由于它带有客户端 ID,也许您的意思是它在本地数据库中必须是唯一的?

为了解决用户可以卸载并重新安装应用程序的问题,为什么不追求“将当前的高ID保存在服务器上以便能够在丢失或重新安装时恢复它”的想法。由于您已经计划在第一次运行时检索客户端 ID(并且在您拥有它之前无法分配 ID),您还不如向服务器询问下一个可用的高 ID。

您的实体是否有其他一些关键材料,以便您可以从该材料为您的高 ID 生成 32 位散列?假设高 id 只需要在特定客户端上是唯一的(并且假设您在客户端上不会有大量的实体),那么我认为如果您拥有不错的密钥材料并使用散列,您将永远不会发生冲突最小化碰撞的功能。

于 2013-04-28T15:07:30.473 回答
2

我在这个问题上提供了两个赏金,但没有找到我正在寻找的答案。但我花了一些时间思考最佳解决方案,也许这个问题不够开放,并没有集中在我想到的解决方案上。

但是有很多不同的策略可用,现在(在第二次赏金之后)我认为要回答的第一个问题是您的分布式环境中有哪些数据模型?你可能有

  1. 客户端和服务器上的相同(或子集)数据模型
  2. 不同的客户端数据模型和服务器数据模型

如果您回答 1),那么您可以从以下选项中选择您的关键策略

  • 使用 GUID
  • 使用我的方法高/低
  • @user3603546 建议的映射键

如果您回答 2),那么我脑海中只有以下内容

  • 复合标识

我从不喜欢复合 id,但是当我考虑它时(无论如何不要称它为复合 id),那么它可能是一个可能的解决方案。下面我想勾勒出这个解决方案:

词汇表:

  • <client key> ...在客户端生成的主键,因此客户端选择实现(Android为long _id)
  • <server key> ...在服务器端生成的主键,所以服务器选择实现
  • <client id> ... 用于识别客户端的 ID
  • <device id> ... 用于标识设备的ID,客户端和设备之间存在1-n关系

解决方案:

  • 仅当您有客户端数据模型和服务器数据模型时才使用它
  • 客户端数据模型具有字段
    • <client key> 主键
    • <服务器密钥> 可为空的数据字段
  • 服务器数据模型具有字段
    • <server key> 作为主键
    • <client key> 可为空的数据字段
    • <client id> 作为区分客户端的强制数据字段
  • 从服务器到客户端同步时,在客户端生成缺少的 <client key> 并将条目标记为脏(以便客户端 ID 在一天结束时到达服务器)
  • 从客户端同步到服务器时,在保存之前在服务器上生成缺少的 <server key>
  • 客户端和服务器数据模型之间的映射可以由dozerOrika等专门的框架处理,但是在执行映射时必须集成密钥生成。

我从不喜欢这个解决方案,因为我一直在考虑服务器数据模型方面的问题。我有只存在于服务器上的实体,我一直想在客户端上创建这些实体,这是不可能的。但是当我在客户端数据模型中思考时,我可能有一个实体,例如。在服务器上产生两个实体(产品和客户端产品)的产品。

于 2014-06-03T16:12:46.757 回答
1

让我看看我是否明白这一点:您需要一个设备独有的 32 位数字吗?好的:

  1. 随机或通过散列当前纳米时间来创建数字。这将为您提供一个相当独特的字符串。
  2. 然后询问服务器该号码是否已被使用。如果有,请再次生成号码并再次询问。

如果你对纳米时间进行散列,那么实际上不可能(并非完全不可能,抗碰撞性不是防碰撞)获得相同的数字。鉴于您的字符串的其余部分,这将使其完全独一无二。在实际需要使用服务器之前,此方法不需要与服务器交互。假设客户端在第一次启动时没有连接:生成号码,保存它,当它连接时,在其他任何事情发生之前,检查设备是否存在。如果是这样,请从头开始。这样您就可以获得真正唯一的设备 ID。

于 2013-07-02T18:07:08.747 回答
1

在您与服务器通信之前,无法确定您在客户端生成的密钥在服务器数据库上是唯一的。

如果您在客户端创建任何记录之前预先与服务器通信,那么您可以在服务器上保留一系列密钥。例如,服务器可以分批分发 10,000 个密钥。客户端与服务器通信,并保留下一批可用密钥的开始,例如 60,000。然后,客户端可以自由创建 ID 为 60,000 到 69,999 的记录。一旦客户端用完密钥,它就需要请求新的密钥范围。如果所有客户端和服务器都像这样为自己保留密钥,那么所有生成的 id 在服务器的数据库中都是唯一的。

现在,如果您在与服务器通信之前在客户端创建记录,那么在您将这些记录同步到服务器之前,一旦您从服务器获得保留范围以使它们在该范围内,您将不得不更正这些 id .

我不确定您为什么还要尝试在密钥中包含客户端 ID;服务器正在分配高值,这足以获得在客户端上生成的唯一密钥。

于 2014-05-30T15:27:09.287 回答