3

这是一个一般的设计问题 - 我想在用户输入值和制表符时验证用户名字段的唯一性。我进行 Ajax 验证并从服务器获得响应。这都是非常标准的。现在,如果我有一个巨大的用户数据库怎么办?如何处理这种情况?我想找出 1.5 亿个用户名中是否存在用户名“foozbarz”?

  1. 数据库查询没有问题[编辑] - 读取用户名数据库一次并填充缓存/哈希以更快地查找(澄清Emil Vikström的观点)
  2. 在内存数据库中也无济于事
  3. 保留一个内存散列(或缓存/内存缓存)来存储所有用户名 - 用户名可以很容易地散列并且查找速度非常快。但是这样做有一些问题:散列的大小——我们可以优化以减小散列大小吗?湾。哈希/缓存刷新频率(我们验证时​​可能会添加用户)
  4. 根据某些条件对用户名表进行分片(例如:表 username_1 中的 AB 等) -感谢 piotrek的建议

或者,还有其他更好的方法吗?

4

5 回答 5

2

为什么不简单地对数据进行分区?如果您有/计划拥有超过 1.5 亿用户,我假设您有/将有预算。如果您刚刚开始(有 2k 个用户),则使用传统的方式在数据库上进行简单的索引搜索。当您有这么多用户以至于您观察到性能问题并测量这是因为您的数据库(而不是例如 www 服务器)时,您只需放置另一个数据库。在第一个上,您将拥有名称从 a 到 m 的用户,并在另一个上休息。您可以选择其他标准,例如哈希,以使数据平衡。当您需要更多时,您将添加更多数据库。但是如果你现在没有这么多用户,我建议你不要做任何过早的优化。有很多事情可能会成为这种数据量的瓶颈

于 2012-06-23T10:55:15.367 回答
1

您最有可能在存储取名的地方进行某种散列处理是正确的,显然,没有散列处理意味着它是免费的。

您不应该做的是依赖该验证。在用户按下注册和用户检查姓名是否空闲之间可能有很多时间。

公平地说,您在这里只有一个问题,那就是您是否真的需要担心您是否会获得 1.5 亿用户。可扩展性通常是一个问题,但除非这种情况发生在一夜之间,否则您可能可以在此之前更换更好的解决方案。

其次,您担心两个用户都得到一个 THIS NAME IS FREE 然后一个用户得到它。首先,发生这种情况的可能性非常低。其次,我能想到的“解决”这个问题的唯一方法是,用户永远不会使用经过验证的名称单击 OK 并获得一个 USERNAME TAKEN 是 a) 记住用户最后验证的内容,存储它,如果其他人注册了同时,使用 AJAX 将 name 字段更改为 take 并通知用户。不要这样做。很多浪费的周期和真正太多的努力来实施。b) 在用户验证用户名时锁定用户名,时间很短。这会导致出现很多免费用户名,但实际上并非如此。你可能也不想要这个。

最简单的解决方案是在用户实际单击“确定”时简单地将散列内容放入表中,但在此之前,请检查名称是否再次存在。如果是这样,只需使用 USERNAME TAKEN 将用户发送回来。有人与其他人争夺名字的机会非常非常渺茫,我怀疑任何人都会对你的验证器(它完成了它的工作,名字在检查时是免费的)如何向用户“撒谎”大惊小怪.

基本上你唯一的问题是你想如何存储昵称。

于 2012-06-23T06:01:17.547 回答
1

Your #1 criteria is flawed because this is exactly what you have a database system for: to store and manage data. Why do you even have a table with usernames if you're not going to read it?

The first thing to do is improving the database system by adding an index, preferably a HASH index if your database system supports it. You will have a hard time writing anything near the performance of this yourself.

If this is not enough, you must start scaling your database, for example by building a clustered database or by partitioning the table into multiple sub-tables.

What I think is a fair thing to do is implement caching in front of the database, but for single names. Not all usernames will have a collision attempt, so you may cache the small subset where the collisions typically happen. A simple algorithm for checking the collision status of USER:

  1. Check if USER exist in your cache. If it does:
    1. Set a "last checked" timestamp for USER inside the cache
    2. You are done and USER is a collision
  2. Check the database for USER. If it does exist:
    1. Add USER to the cache
    2. If the cache is full (all X slots is used), remove the least recently used username from the cache (or the Y least recently used usernames, if you want to minimize cache pruning).
    3. You are done and USER is a collision
  3. If it didn't match the cache or the db, you are done and USER is NOT a collision.

You will of course still need a UNIQUE contraint in your database to avoid race conditions.

于 2012-06-23T11:36:24.587 回答
0

如果您要走传统路线,则可以使用适当的索引来改进数据库查找。

您还可以尝试使用 ElasticSearch 之类的东西,它在大型数据集上具有非常低的延迟查找。

于 2012-06-23T05:56:40.223 回答
0

如果您有 150M+ 用户,您将必须具备以下功能:

  1. 检查用户是否存在,如果没有找到则发出信号
  2. 验证密码是否正确,如果不正确则发出信号
  3. 检索用户的数据

这个问题你会遇到,而且必须解决。很可能与用户查询类似。即使您严重依赖会话,您仍然会遇到“从 150M+ 池中的许多中查找会话 X”的问题,这在结构上与“从 150M+ 池中的许多中查找用户 X”相同。

一旦你解决了更大的问题,你现在遇到的问题就是它的第一步。

所以我会检查一个可扩展的数据库解决方案(可能是一个 NoSQL 解决方案),并使用它来实现“可用性检查”。

你可能会以一个结尾

retrieveUserData(user, password = None)

如果用户和密码有效且正确,则返回用户信息。对于可用性检查,您将不发送密码,并且如果用户名可用,则会出现 UserNotFound 异常。

于 2012-06-23T09:11:22.077 回答