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



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

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

我知道UUID密钥生成是该场景的好方法,但我想坚持使用Android平台建议的名称_id和类型长的密钥。

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

因此,我目前最喜欢的是用long数据类型构建我的键(我以前在另一个项目中这样做过)。我认为我将使用high/low方法(参见此处的示例What's the Hi/Lo算法?)并拥有一个由以下内容组成的密钥:
  • 从服务器生成的客户端id(例如~28位)
  • 低值(例如~ 4位)在客户端递增,从未持续
  • 高值(例如~ 32位)在客户端递增,仅在客户端保持

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

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

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

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

我在这个问题上提供了两个赏金,但没有找到我想要的答案。但我花了一些时间思考最好的解决方案,也许这个问题不够开放,太关注我脑海中的解决方案了。

然而,有很多不同的策略可用,现在(在第二次慷慨之后)我认为第一个要回答的问题是,在您的分布式环境中使用哪种数据模型?比如

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

如果你的答案是1),那么你可以从

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

如果你的答案是2),那么我只会想到以下几点

    复合id

我从来都不喜欢复合id,但是当我想到它时(无论如何不要叫它复合id),那么它可能是一个可能的解决方案。下面我想概述一下这个解决方案:

术语表:

  • & lt;客户key>…在客户端生成的主键,因此客户端选择实现(Android的长_id)
  • & lt;服务器key>…在服务器端生成的主键,因此服务器选择实现
  • & lt;客户id>…标识客户端的ID
  • & lt;设备id>…标识设备的ID,客户端和设备之间有1-n的关系

解决方案:

  • 仅当您有一个客户端数据模型和一个服务器数据模型时使用
  • 客户端数据模型有字段
    • & lt;客户key>主键
    • & lt;服务器key>可空数据字段
  • 服务器数据模型有字段
    • & lt;服务器key>作为主键
    • & lt;客户key>可空数据字段
    • & lt;客户id>作为强制数据字段,以区分客户端
  • 从服务器同步到客户端时,生成丢失的客户端密钥>在客户端上并将条目标记为dirty(以便客户端id在一天结束时到达服务器)
  • 从客户端同步到服务器时,生成丢失的<服务器密钥>
  • 客户端和服务器数据模型之间的映射可以由专用框架(如dozer或Orika)处理,但是在执行映射时必须集成密钥生成。

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

这是更多的问题而不是答案…

如果你可以自动生成你所有的id,这确实使事情变得更容易,所以你不必从服务器上获取它们,也不必担心你是否有连接。你提到你不能采用常见的方法(UUID或ANDROID_ID),因为你将使用一个很长的"按照Android平台的建议"。

你是指Android假设你的SQLite表将有一个长_id主键的事实吗?

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

如果你正在使用一个数据存储与层次键(例如谷歌数据存储),那么如何如果你使用UUID/ANDROID_ID作为客户端id,然后一个长的数据项id。然后在客户端只存储long,而在服务器上,实体使用UUID/long的密钥路径存储。

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

为了解决用户可以卸载和重新安装应用程序的问题,为什么不追求你的想法"将当前的高id保存在服务器上,以便在丢失或重新安装时能够恢复它"。既然您已经计划在第一次运行时检索客户端id(并且在获得id之前不能分配id),那么您还可以向服务器请求下一个可用的高id。

你的实体是否有一些其他的关键材料,这样你就可以从这些材料中为你的高id生成一个32位的哈希?假设高id只需要在特定的客户端上是唯一的(并且假设您不会在客户端上有大量的实体),那么我认为如果您有合适的关键材料并使用最小化碰撞的哈希函数,则永远不会发生碰撞。

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

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

动机

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

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

在您的示例中,您可以通过对客户机应用程序的最小更改来更改服务器技术。由于客户机可以离线操作,因此在大多数函数中只能使用本地id。只有同步模块将获取并转换服务器id。

让我看看我是否明白了:你需要一个设备唯一的32位数字?好:

  1. 随机或通过散列当前纳时创建数字。这会得到一个非常独特的字符串
  2. 然后询问服务器该号码是否已被使用。如果有,重新生成号码并重新请求。

如果对纳米时间进行散列,得到相同的数字几乎是不可能的(并非完全不可能,抗碰撞不是防碰撞)。考虑到字符串的其余部分,这将使它完全唯一。此方法在实际需要使用服务器之前不需要与服务器进行交互。假设客户机在开始时没有连接:生成号码,保存它,当它连接时,在发生任何其他事情之前,检查设备是否存在。如果是,从头开始。这样你就能得到一个真正唯一的设备ID。

除非您与服务器通信,否则无法确定您在客户端上生成的密钥在服务器DB上是唯一的。

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

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

我不知道为什么你也试图包括一个客户端id在关键;服务器正在分配高值,这足以获得在客户机上生成的唯一密钥。

最新更新