假设我们有:
class User(db.Model):
nickname = db.StringProperty()
我们在User中有500k个实体,每个实体都有一个独特的昵称。
我现在想再添加一个实体,它必须是一个独特的昵称。所以我运行这个:
to_check = User.gql("WHERE nickname = :1",new_nickname).get()
if to_check is None:
# proceed to create entity
User(nickname=new_nickname).put()
这种方法适用于超过500k的用户吗?我会经历缓慢的处理时间吗?
这有什么优化方法?
PS:将昵称属性编入索引是一种很好的方法吗?
我现在只能想到这个:
class User(db.Model):
nickname = db.StringProperty(indexed=True) # index this property
EDITED: 顺便说一句,我有两个我想要维护的独特属性:userid和昵称。 userid将被自动指定为keyname(我正在创建一个facebook应用程序,它获取用户的facebook id并创建用户实体)
所以对我来说,userid更重要,所以我将它用作键名。
昵称将由facebook用户手动输入,因此我需要一种机制来检查它是否是唯一的。
现在的问题是,我怎么处理昵称?我不能有两个关键名:(
答案 0 :(得分:4)
你应该查看Brett Slatkin的Google I / O视频:
http://code.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html
具体来说,关于关系指数实体的位。他处理的问题与你的问题非常相似。
您可以创建另一个实体,用于存储用户昵称(并将其设置为key_name)。创建它时,将父项设置为用户实体:
UserNickname(
parent=user,
key_name=nickname,
nickname=nickname
)
现在您可以非常快速地查询昵称(get_by_key_name),如果您想要排除当前用户(如果您让用户更改其昵称,您将使用它),您可以轻松地从keys_only查询中获取父级或使用直接在查询中的祖先。
编辑:刚刚注意到Kris Walker已在评论中提出这一建议。您可以使用引用属性或父级将两者链接在一起,两者都可以正常工作。
答案 1 :(得分:2)
只要您在SDK中运行此类查询,nickname属性就会自然地位于index.yaml中,因此请不要过于担心。 indexed
属性默认为True
(通常仅用于将其明确设置为False
)。
使用索引,无论如何,搜索可能出现0或1次的昵称将会非常快,无论表中有多少条目 - 比如数量级,50-100毫秒;建立一个新的实体,可能是两倍长。整件事应该在300毫秒或更短的时间内完成。
一个担心是竞争条件 - 如果两个单独的会话试图在同一时间注册完全相同的昵称怎么办?可能不太可能,但是当它发生时,你的代码就没有防御。获得这样的防御(通过在事务中运行)意味着事务锁定,因此可能影响性能(如果几个这样的会话在完全相同的时间运行,它们将被序列化)。
答案 2 :(得分:1)
get_by_key_name 将成为您最好的朋友。
我经常使用如下代码模式:
user = User.get_by_key_name(user_key_name)
if not user:
user = User(key_name = user_key_name)
这往往比GQL查询快得多。
如果您要一次向数据存储区写入多个实体,则还应使用db.put(entities_list)模式,其中列表最多可包含500个实体 - 他们不会甚至必须是同一种型号。
答案 3 :(得分:0)
所以我会这样做(这已经说明了)
class User(db.Model):
# other properties go here, but not nickname
# put a new user
if User.get_by_key_name(user_nick) is None:
User(key_name=user_nick).put()
索引策略是一种浪费,即使只是“500k”。
还有db.Model.get_or_insert()
http://code.google.com/appengine/docs/python/datastore/modelclass.html#Model_get_or_insert
答案 4 :(得分:0)
基本上当用户手动输入昵称时,我会自动将他/她的用户ID添加到其中以使其唯一。
例如:
user_nickname是托马斯。 我将userid附加到它,成为thomas_8937459874(唯一!)
所以我不需要检查以前是否存在昵称。保存我的GQL查询。
当显示昵称的时候,我只会使用字符串操作来只检索名称“thomas”
你觉得怎么样?答案 5 :(得分:0)
所以我尝试使用ReferenceProperty来执行此操作:
告诉我你们的想法:添加了附加功能:用户最多只能更改昵称3次
# models.py
# key_name will be whatever the user manually enters to be the nickname
class UserNickname(db.Model):
name = db.StringProperty()
# key_name = facebook id
class User(db.Model):
nickname = db.ReferenceProperty(UserNickname)
nickname_change_count = db.IntegerProperty(default=0)
# create unique entity with facebook id
User(key_name="123456789").put()
*****以下代码位于注册页面*****
# in the signup page , signup.py
# userid of 123456789 is taken from cached session
user = User.get_by_key_name("123456789")
# this is the nickname manually entered by the user
manually_entered_nick = "Superman"
to_check = UserNickname.get_by_key_name(manually_entered_nick)
if to_check is None:
#create usernickname entity
key = UserNickname(key_name=manually_entered_nick,name=manually_entered_nick).put()
#assign this key to the user entity
user.nickname = key
db.put(user)
print 'Unique nickname registered'
else:
print 'Choose another nick pls'
*****以下代码位于“更改用户昵称”页面*****
# change_nickname.py
# userid is taken from cached session
user = User.get_by_key_name("123456789")
# max no. of nickname changes allowed is 3 ( hardcoded )
# checks if user can change nick
if user.nickname_change_count >= 3:
print 'you cannot change nicks anymore. contact admin'
else:
# delete entire nickname entity
to_delete = UserNickname.get_by_key_name(user.nickname.key().name())
db.delete(to_delete)
# adds to count
user.nickname_change_count += 1
# for security purposes, user account is "disabled" until he/she chooses a new nick.
# user manually enters new nickname
new_nick = "Batman"
to_check = UserNickname.get_by_key_name(new_nick)
if to_check is None:
#create usernickname entity
key = UserNickname(key_name=new_nick,name=new_nick).put()
#assign this nick to user entity
user.nickname = key
db.put(user)
print 'new Nick registered'
else:
print 'Choose another nick pls'