Python 如何实施";“自动递增”;关于谷歌AppEngine

Python 如何实施";“自动递增”;关于谷歌AppEngine,python,database,google-app-engine,Python,Database,Google App Engine,我必须以“强单调递增”的方式给某些东西贴上标签。无论是发票号、装运标签号还是类似的 一个数字不能用两次 当使用了所有较小的数字(无孔)时,应使用每个数字 别出心裁地说:我需要数一、二、三、四。。。 我可用的数字空间通常是100.000个数字,我每天可能需要1000个 我知道这在分布式系统中是一个困难的问题,我们通常更擅长使用guid。但在这种情况下,出于法律原因,我需要“传统编号” 这可以在Google AppEngine上实现吗(最好是用Python实现)?看一下是如何实现的。这可能对你有帮助

我必须以“强单调递增”的方式给某些东西贴上标签。无论是发票号、装运标签号还是类似的

  • 一个数字不能用两次
  • 当使用了所有较小的数字(无孔)时,应使用每个数字
  • 别出心裁地说:我需要数一、二、三、四。。。 我可用的数字空间通常是100.000个数字,我每天可能需要1000个

    我知道这在分布式系统中是一个困难的问题,我们通常更擅长使用guid。但在这种情况下,出于法律原因,我需要“传统编号”


    这可以在Google AppEngine上实现吗(最好是用Python实现)?

    看一下是如何实现的。这可能对你有帮助。你真的需要它们是数字吗。如果unique令人满意,只需使用实体键。

    如果您绝对必须有连续递增的数字,且没有间隔,则需要使用单个实体,您可以在事务中更新该实体以“使用”每个新数字。实际上,每秒生成的数字将被限制在1-5个左右,这听起来很适合您的要求。

    如果您放弃ID必须严格按顺序排列的要求,您可以使用分层分配方案。基本思想/限制是事务不能影响多个存储组

    例如,假设您有“用户”的概念,您可以为每个用户分配一个存储组(为每个用户创建一些全局对象)。每个用户都有一个保留ID列表。为用户分配ID时,选择一个保留ID(在事务中)。如果没有剩余ID,则创建一个新事务,从全局池中分配100个ID(比如),然后创建一个新事务,将它们添加到用户中,同时提取一个ID。假设每个用户只按顺序与应用程序交互,那么用户对象上就不会存在并发性。

    现在提供了一个简单的库函数,可以按顺序获取数字。它是基于尼克·约翰逊的交易方法,并且可以很容易地被用作马丁冯·L·WIS的分割方法的基础:

    >>> from gaeth.sequences import * 
    >>> init_sequence('invoce_number', start=1, end=0xffffffff)
    >>> get_numbers('invoce_number', 2)
    [1, 2]
    
    功能基本上是这样实现的:

    def _get_numbers_helper(keys, needed):
      results = []
    
      for key in keys:
        seq = db.get(key)
        start = seq.current or seq.start
        end = seq.end
        avail = end - start
        consumed = needed
        if avail <= needed:
          seq.active = False
          consumed = avail
        seq.current = start + consumed
        seq.put()
        results += range(start, start + consumed)
        needed -= consumed
        if needed == 0:
          return results
      raise RuntimeError('Not enough sequence space to allocate %d numbers.' % needed)
    
    def get_numbers(needed):
      query = gaetkSequence.all(keys_only=True).filter('active = ', True)
      return db.run_in_transaction(_get_numbers_helper, query.fetch(5), needed)
    
    def\u获取数字\u帮助程序(键,需要):
    结果=[]
    对于键入键:
    seq=db.get(键)
    开始=顺序当前或顺序开始
    结束=顺序结束
    avail=结束-开始
    消耗=需要
    
    如果avail如果你对顺序没有太严格,你可以“切分”你的递增项。这可以被认为是一个“最终顺序”计数器

    基本上,您有一个实体是“主”计数。然后,您有许多实体(基于您需要处理的负载)具有自己的计数器。这些碎片保留来自主服务器的ID块,并在其范围内提供服务,直到它们的值用完为止

    快速算法:

  • 你需要一个身份证
  • 随便挑一块碎片
  • 如果碎片的起点小于终点,则取其起点并递增
  • 如果碎片的起点等于(或大于)其终点,则转到主节点,获取值并向其添加一个数量
    n
    。将碎片开始设置为检索值加1,结束设置为检索值加1
  • 这可以很好地扩展,但是,您可以使用的数量是碎片数乘以
    n
    值。如果你想让你的记录看起来更高,这可能会起作用,但如果你想让它们代表顺序,那就不准确了。还需要注意的是,最新的值可能有漏洞,因此如果出于某种原因使用这些漏洞进行扫描,则必须注意漏洞

    编辑
    我的应用程序需要这个(这就是为什么我搜索问题:p),所以我实现了我的解决方案。它可以抓取单个ID,也可以高效抓取批次。我已经在一个受控的环境中(在appengine上)对它进行了测试,它的性能非常好。您可以找到代码。

    请记住:切分会增加获得唯一、自动递增值的可能性,但不能保证它。如果你必须有一个独特的自动插入,请接受Nick的建议。

    我为我的博客实现了一个非常简单的东西,它增加了一个IntegerProperty,
    iden,而不是Key ID

    我定义
    max\u iden()
    以查找当前使用的最大
    iden
    整数。此函数扫描所有现有的博客文章

    def max_iden():
        max_entity = Post.gql("order by iden desc").get()
        if max_entity:
            return max_entity.iden
        return 1000    # If this is the very first entry, start at number 1000
    
    然后,在创建一篇新的博客文章时,我给它分配了
    max_iden()+1的
    iden
    属性

    new_iden = max_iden() + 1
    p = Post(parent=blog_key(), header=header, body=body, iden=new_iden)
    p.put()
    
    我想知道您是否还想在这之后添加某种验证函数,即在转到下一张发票之前,确保max_iden()现在已增加


    总之:脆弱、低效的代码。

    或者,您可以按照人们的建议使用allocate_ids(),然后预先创建这些实体(即使用占位符属性值)

    然后,在创建新发票时,您的代码可以遍历这些条目以查找ID最低的条目,这样占位符属性就不会被实际数据覆盖


    我还没有将其付诸实践,但从理论上看它应该是可行的,很可能与人们已经提到的限制相同。

    我考虑使用以下解决方案:使用CloudSQL(MySQL)插入记录并分配顺序ID(可能带有任务队列),稍后(使用Cron任务)将记录从CloudSQL移回数据存储


    实体还可以有一个UUID,因此我们可以从CloudSQL中的数据存储映射实体,还可以有顺序ID(出于法律原因)。

    否,它必须是数字。别人给了我这个数字空间(想想UPS或联邦快递),谢谢你的回答!如果我削弱了无差距(2.)要求,有哪些替代方案?不幸的是,我的数字每次需要33(a)
    first, last = MyModel.allocate_ids(1000000)
    keys = [Key(MyModel, id) for id in range(first, last+1)]