App Engine 数据库中的自增ID

3
我正在使用App Engine数据存储,并希望确保行ID的行为类似于MySQL数据库中的“自动递增”字段。
尝试了几种生成策略,但好像无法控制发生的情况:
  • ID不是连续的,似乎有几个“流”并行增长。
  • 旧行被删除后,ID会“回收利用”。
这种事情是否可能?我真的不想为每一行保留(索引的)时间戳。

2
你可能想查看Nick Johnson的答案 - jan zegan
请参考以下链接:https://dev59.com/v2865IYBdhLWcg3wHKpe - max
1
为什么你需要让你的数字单调递增呢? - Nick Johnson
感谢您的回复。我们需要“老派”的自增是为了数据库维护。我们想删除旧的项目(例如超过两个月的项目)。因此,我们从传统的思路出发,认为将每个日期上的最后一个ID保留在表中是一个不错的主意。请问您能否建议一种更好的GAE实现方式? - user1053539
3个回答

2
听起来你不能仅依靠ID连续而不需要进行大量额外的工作。然而,有一种简单的方法可以实现你想要做的事情:

我们想删除旧物品(例如两个月前的)

这里有一个模型,它自动跟踪其创建和修改时间。只需使用auto_now_addauto_now参数即可轻松实现。

from google.appengine.ext import db

class Document(db.Model):
  user = db.UserProperty(required=True)
  title = db.StringProperty(default="Untitled")
  content = db.TextProperty(default=DEFAULT_DOC)
  created = db.DateTimeProperty(auto_now_add=True)
  modified = db.DateTimeProperty(auto_now=True)

然后你可以使用 cron jobs 或者 任务队列 来安排删除旧物品的维护任务。查找最旧的物品就像按创建日期或修改日期排序一样容易:
db.Query(Document).order("modified")
# or
db.Query(Document).order("created")

感谢您的回复。正如我在问题中提到的,如果可能的话,我想避免保留索引时间戳列。我正在寻找一种“滥用”行ID的技巧来实现此目的。 - user1053539

1
我知道的是,Google App Engine中提供了自动生成的Long整数ID,但不能保证值的增长,并且也不能保证数字是真正的递增。
因此,如果您需要时间戳和递增,可以添加一个带有毫秒的DateTime字段,但是这样您就不知道数字是否唯一。
因此,最好的做法(我们正在使用)是:
  • 使用自动生成的Long ID(我们在Java中使用Objectify)
  • 在每个实体上使用时间戳,并使用索引查询实体(使用降序索引)以获取前X个

0

我认为这可能是一个相当不错的解决方案,但请注意我没有进行任何测试。语法甚至可能是不正确的!

原则是使用memcache生成单调序列,使用数据存储作为备用方案,以防memcache失败。

class IndexEndPoint(db.Model):
    index = db.IntegerProperty (indexed = False, default = 0)

def find_next_index (cls):
    """ finds the next free index for an entity type """
    name = 'seqindex-%s' % ( cls.kind() )

    def _from_ds ():
        """A very naive way to find the next free key.

        We just take the last known end point and loop untill its free.
        """

        tmp_index = IndexEndPoint.get_or_insert (name).index

        index = None
        while index is None:
            key = db.key.from_path (cls.kind(), tmp_index))
            free = db.get(key) is None
            if free:
                index = tmp_index
            tmp_index += 1

        return index

    index = None

    while index is None:   
        index = memcache.incr (index_name)
        if index is None:  # Our index might have been evicted
            index = _from_ds ()
            if memcache.add (index_name, index):  # if false someone beat us to it
                index = None

    # ToDo:
    # Use a named task to update IndexEndPoint so if the memcache index gets evicted
    # we don't have too many items to cycle over to find the end point again.

    return index


def make_new (cls):
    """ Makes a new entity with an incrementing ID """

    result = None

    while result is None:
        index = find_next_index (cls)


        def txn ():
            """Makes a new entity if index is free.

            This should only fail if we had a memcache miss 
            (does not have to be on this instance).
            """
            key = db.key.from_path (cls.kind(), index)
            if db.get (key) is not None:
                return

            result = cls (key)
            result.put()
            return result

        result = db.run_in_transaction (txn)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接