Android(分布式应用程序)主键策略

时间:2013-04-28 13:15:58

标签: android sqlite database-design

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

生成在分布式环境中使用的主键的最佳方法是什么?有关类似问题,请参阅What is the best primary key strategy for an online/offline multi-client mobile application with SQLite and Azure SQL database as the central store?

我知道 UUID密钥生成对于这种情况来说是一个很好的方法,但我想坚持使用名称为_id的密钥,并按照Android平台的建议输入长。

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

因此,我目前最喜欢的是使用数据类型long构建我的密钥(我之前在另一个项目中执行了此操作)。我想我将使用高/低方法(例如参见此处What's the Hi/Lo algorithm?)并拥有一个包含以下内容的键:

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

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

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

在Android上获取高ID的最佳方法是什么?自动增量键不是解决方案。我需要类似发电机功能的东西。它必须在自己的事务中执行(而不是“用户”事务)。有没有人在Android上体验过这种方法,有人能指出我正确的方向吗? (我只发现了这个answer)。

您为多客户端应用程序(在线和离线)使用了哪些关键策略?

5 个答案:

答案 0 :(得分:2)

这是更多问题然后回答...

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

您是指Android假设您的SQLite表具有长_id主键吗?

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

如果您使用的是带有分层键的数据存储区(例如Google数据存储区),那么如果您使用UUID / ANDROID_ID作为客户端ID,然后使用长数据项ID,那该怎么办?然后在客户端上,您只需存储长整数,并在服务器上存储您的实体,其关键路径为UUID / long。

为什么要写“高id必须是数据库的唯一值”?由于它是以客户端ID为前缀的,或许你的意思是它在本地数据库中必须是唯一的?

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

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

答案 1 :(得分:2)

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

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

动机

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

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

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

答案 2 :(得分:2)

我在这个问题上提供了两个赏金,并没有找到我想要的答案。但是我花了一些时间考虑最好的解决方案,也许这个问题不够开放,并且很专注于我想到的解决方案。

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

  1. 客户端和服务器上的相同(或子集)数据模型
  2. 不同的客户端数据模型和服务器数据模型
  3. 如果您回答1),那么您可以从

    中选择关键策略
    • 使用GUID
    • 使用我的方法高/低
    • 将密钥映射为@ user3603546建议

    如果你用2)回答,那么我脑海中只会出现以下内容

    • 复合ID

    我从不喜欢复合ID,但是当我想到它时(并且不管怎样称它为复合id),那么它可能是一个可能的解决方案。以下我想草拟这个解决方案:

    词汇表:

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

    解决方案:

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

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

答案 3 :(得分:1)

让我看看我是否直截了当:你需要一个32位的数字,这是设备独有的?确定:

  1. 随机或通过散列当前纳米级来创建数字。这将为你提供一个相当独特的字符串。
  2. 然后询问服务器是否已使用该号码。如果有,请再次生成该号码并再次询问。
  3. 如果你对纳米级进行散列,那么实际上不可能(并非完全不可能,碰撞阻力不能防碰撞)得到相同的数字。鉴于你的其余字符串,这将使它完全独特。在实际需要使用服务器之前,此方法不需要与服务器交互。假设客户端在第一次启动时没有连接:生成号码,保存它,以及何时连接,在其他任何事情发生之前,检查设备是否存在。如果是,请从头开始。这样您就可以获得真正独特的设备ID。

答案 4 :(得分:1)

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

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

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

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