如何在Google AppEngine上实现“自增”

31 投票
9 回答
11668 浏览
提问于 2025-04-16 05:50

我需要以一种“严格单调递增”的方式给一些东西编号,比如发票号码、运输标签号码等等。

  1. 一个号码不能被使用两次。
  2. 每个号码应该在所有更小的号码都被使用后才使用(不能有遗漏)。

简单来说,就是我需要从1开始数,依次是2、3、4……

我可用的号码范围通常有10万个,而我每天大约需要1000个号码。

我知道在分布式系统中,这个问题比较复杂,通常我们会用GUID(全局唯一标识符)来解决。但因为法律原因,我在这个情况下需要“传统的编号”。

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

9 个回答

7

gaetk - Google AppEngine工具包现在提供了一个简单的库函数,可以用来获取序列中的数字。这个功能是基于Nick Johnson的事务处理方法,使用起来非常简单,可以作为Martin von 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)
7

如果你不要求ID必须严格按顺序排列,那么你可以使用一种分层的分配方式。这个方法的基本思路是:交易(也就是操作)不能影响多个存储组。

举个例子,假设你有“用户”这个概念,你可以为每个用户分配一个存储组(也就是为每个用户创建一个全局对象)。每个用户都有一份保留的ID列表。当你要为某个用户分配一个ID时,就从他们的保留列表中选一个(在一个交易中进行)。如果没有可用的ID了,就开启一个新的交易,从全局池中分配100个ID(比如说),然后再开启一个新的交易,把这些ID添加到用户的列表中,同时从他们的保留列表中扣除一个。假设每个用户与应用的互动都是顺序进行的,那么在用户对象上就不会出现并发问题。

25

如果你一定要有一个没有间隔的连续递增数字,你就需要使用一个单独的实体,并在一个事务中更新它,以“消耗”每一个新的数字。实际上,你每秒生成的数字大约只能在1到5个之间,这听起来应该能满足你的需求。

撰写回答