【问题标题】:Autoincrement ID in App Engine datastoreApp Engine 数据存储区中的自动增量 ID
【发布时间】:2012-01-13 16:21:31
【问题描述】:

我正在使用 App Engine 数据存储区,并希望确保行 ID 的行为类似于 mySQL DB 中的“自动增量”字段。

尝试了几种生成策略,但似乎无法控制所发生的事情:

  • ID 不连续,似乎有多个“流”并行增长。
  • 删除旧行后,ID 将被“回收”

这样的事情有可能吗? 我真的很想避免为每一行保留(索引)时间戳。

【问题讨论】:

  • 您可能需要查看 Nick Johnson 的 answer
  • 为什么你的数字需要单调递增?
  • 感谢您的回复。我们需要“老式”自动增量的原因是数据库维护。我们想删除旧项目(例如,价值超过两个月的项目)。因此,从旧的思想流派出发,我们认为最好保留一张表格,记录每个日期的最后一个 ID。您能否为 GAE 提出更好的实施建议?

标签: google-app-engine google-cloud-datastore auto-increment


【解决方案1】:

听起来你can't rely on IDs being sequential 没有大量的额外工作。但是,有一种简单的方法可以实现您想要做的事情:

我们想删除旧项目(价值超过两个月,对于 例子)

这是一个自动跟踪其创建和修改时间的模型。只需使用 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 jobstask queue 来安排删除旧内容的维护任务。查找最旧的东西就像按创建日期或修改日期排序一样简单:

db.Query(Document).order("modified")
# or
db.Query(Document).order("created")

【讨论】:

  • 感谢您的回复。正如我在问题中提到的,如果可能的话,我想避免保留索引时间戳列。为此,我正在寻找一种“滥用”行 ID 的技巧。
【解决方案2】:

据我所知,Google App Engine 中可以使用自动生成的长整数 ID,但不能保证该值会增加,也不能保证这些数字是真正的一增量。

因此,如果您需要时间戳和增量,请添加以毫秒为单位的 DateTime 字段,但您不知道这些数字是唯一的。

所以,最好的做法(我们正在使用的)是:(很抱歉,但这确实是恕我直言的最佳选择)

  • 使用自动生成的 ID 作为 Long(我们在 Java 中使用 Objectify)
  • 在每个实体上使用时间戳并使用索引来查询实体(使用降序索引)以获取顶部 X

【讨论】:

    【解决方案3】:

    我认为这可能是一个相当不错的解决方案,但请注意,我没有以任何方式、形状或形式测试过它。语法甚至可能不正确!

    原理是使用memcache生成单调序列,如果memcache失败,使用datastore提供回退。

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-11
      • 2011-02-16
      • 2013-12-03
      • 1970-01-01
      相关资源
      最近更新 更多