我需要提出一个策略来处理创建数据存储条目的客户端重试:
我有一个限制:服务器是STATELESS!它在客户端没有任何会话信息。
我目前的想法如下:
以下是我现在正在考虑的实施方案及其问题:
我正在考虑选项2的一个解决方案是使用某种公式来获取序列号并将它们重新映射到一个独特的,确定的但随机的序列,而不是消除热点。关于这样一个公式可能是什么样的想法?
或许可能有一个更好的方法?
答案 0 :(得分:1)
如何为新实体分配密钥?
如果您自己创建密钥,问题就解决了。重复实体将简单地覆盖现有实体,因为它具有相同的密钥。一个例子是创建一个产品实体,其中产品的SKU用于生成密钥。
如果数据存储分配了密钥,则当请求超时时,向用户显示错误消息并将数据重新加载到客户端。然后,用户将查看是否已创建实体。
它不像“随机序列”那样花哨,但它更简单,更可靠:)
答案 1 :(得分:0)
虽然可以从上面的工作中创建实现选项(1),但通过使用设计巧妙的数据结构,正确的事务和垃圾收集,它将是相当的让它可靠地工作是一种痛苦。
因此,似乎正确的解决方案是使用选项(2)的通用版本:
为创建的实体使用一些唯一标识符作为实体的密钥。
这样,如果在重试中再次创建相同的实体,则可以很容易地可靠地找到现有副本(因为gets
非常一致),或者盲目地再次写入它只会覆盖第一个版本
@Greg 在评论中建议使用哈希来实体唯一标识数据作为密钥。虽然这解决了在参数空间中均匀分布密钥并因此导致跨物理存储位置有效分布数据的问题,但它确实产生了必须管理(或忽略)哈希冲突的新问题,尤其是如果一个人试图让密钥变得很长。
有办法处理这些碰撞。例如:如果发生碰撞,请比较实际内容以检查它是否真的重复,如果没有,则添加" 1"关键。然后,查看该密钥是否也存在,如果存在,再次检查它是否具有相同的内容。如果没有,请添加" 2"相反,再次检查碰撞,等等......虽然这有效,但它会变得非常混乱。
或者你可以说哈希冲突是如此罕见,以至于人们永远不会在一个数据库中拥有足够的用户数据。我个人不喜欢这些"保持你的手指交叉" -approaches,但在许多情况下,它可能是一种可接受的方式。
但是,幸运的是,我已经为数据创建了一个无冲突的全局唯一标识符:create-tag。事实证明,我使用它看到的两个问题都可以通过一些聪明的比特改组来轻松解决:
使用与原始问题相同的标识符,我的create-tag SI.PL.RI 由 SI 组成,它将永远增加, PL ,每次创建新服务器实例时重置为0, RI ,重置每个新客户端会话。所以 RI 可能总是很小, PL 会保持一点点,而 SI 会慢慢变大。
鉴于此,我可以像这样构建密钥(从最重要的位开始):
- Lowest 10 bits of PL
- Lowest 4 bits of RI
- Lowest 17 bits of SI
- 1 bit indicating whether there are any further non-zero values
- Next lowest 10 bits of PL
- Next lowest 4 bits of RI
- Next lowest 17 bits of SI
- 1 bit indicating whether there are any further non-zero values
- ... until ALL bits of RI, PL, and SI are used (eventually breaking 10-4-17 pattern)
这样,如果按词汇顺序排序(如AppEngine那样),生成的键会在参数空间中很好地传播,并且第一个键只有自动生成的键的一半,并且它们只会根据需要变长
除了1:
实际上,如果没有服务器实例存活的时间足以支持超过一千个页面加载,并且没有一个客户端在一个会话中创建超过16个新实体,并且服务器实例不会更快地生成平均每5分钟就会超过一个,平均值超过4个字节需要一年多的时间。
如果没有服务器实例活着的时间超过百万页面加载,并且没有一个客户端在一个会话中创建超过256个新实体,并且服务器实例产生的平均速度不会高于每第二一个实例,在密钥长度超过8个字节(因此长于自动生成的字节)之前,它仍需要500多年的时间。应该没问题...... :)
除了2:
如果我需要使用密钥来索引Java HashMap,我的密钥对象的hashCode()
函数可以返回一个从前4个密钥字节构建的整数。反向命令将密钥分散到桶中。