当前位置: 首页 > 面试题库 >

不同的交易必须保证选择不同的项目;避免争执

邢烨烨
2023-03-14
问题内容

作为注册新用户的一部分;我们从预编译列表(表)中为他们分配资源(在这种情况下,为Solr核心)。

如果有5个用户注册,则必须为他们分配5个 不同的内核 ;如果用户成功注册,则分配完成(请参见下面的说明)。

但是在现实世界中,并发注册新用户 竞争同一行,而不是选择不同的行
。如果X需要5秒钟来注册,则X的“持续时间”中的Y和Z的注册将失败,因为它们争用X的同一行。

问题: 即使在高并发率(例如每秒100次注册)下,如何使交易选择而没有争用?

table: User
user_id   name  core   
      1    Amy h1-c1
      2    Anu h1-c1
      3    Raj h1-c1
      4    Ron h1-c2
      5    Jon h1-c2

table: FreeCoreSlots
core_id  core status   
      1 h1-c1   used
      2 h1-c1   used
      3 h1-c1   used
      4 h1-c2   used
      5 h1-c2   used #these went to above users already
      6 h1-c2   free
      7 h1-c2   free
      8 h1-c2   free
      9 h1-c2   free

伪代码(如果隔离了东西):

sql = SQLTransaction()
core_details = sql.get("select * from FreeCoreSlots limit 1")
sql.execute("update FreeCoreSlots set status = 'used' where id = {id}".format(
   id = core_details["id"]))
sql.execute("insert into users (name,core) values ({name},{core})".format(
   name = name,
   id   = core_details["id"]))
sql.commit()

如果第二次进行100次注册,则它们将 争夺第一行FreeCoreSlots并导致严重的失败。

有一个select … for更新,如InnoDB SELECT … FOR
UPDATE语句锁定表中的所有行作为解决方案,但它们似乎建议降低隔离度。这种方法正确吗?


问题答案:

我要问的问题是,为什么用户可能需要5秒钟才能完成。在START TRANSACTION和之间COMMIT应该只有几分之一秒。

为了防止FreeCoreSlots再次将同一行分配给同一用途,您必须再次使用SELECT for UPDATE。我认为锁定级别并不是真正的问题。设计数据库的下一个空闲行的FreeCoreSlots方式实际上已锁定,直到完成事务为止。请在下面查看我的测试结果。而且我确实认为,即使对于每秒100个新用户来说,这仍然足够。但是,如果您甚至想克服这一点,则必须找到一种方法来锁定其中的另一个下一个空闲行FreeCoreSlots给每个用户。不幸的是,没有“选择第一行,除非它有锁”的功能。也许改为使用一些随机或模数逻辑。但是,正如我已经说过的那样,即使对于不可思议的每秒100个新用户,我认为这也不应该是您的问题。如果我对此有错,请随时发表评论,我愿意再看一遍。

这是我的测试结果。默认的InnoDB锁定级别:不带的 可重复读取FOR UPDATE。这样就行不通了。

User 1:
    START TRANSACTION
    SELECT * FROM FreeCoreSlots WHERE status = 'FREE' LIMIT 1 -- returns id 1
User 2:
    START TRANSACTION
    SELECT * FROM FreeCoreSlots WHERE status = 'FREE' LIMIT 1 -- also returns id 1 !!!
User 1:
    UPDATE FreeCoreSlots SET status = 'USED' where ID = 1;
User 2:
    UPDATE FreeCoreSlots SET status = 'USED' where ID = 1; -- WAITS !!!
User 1:
    INSERT INTO user VALUES (...
    COMMIT;
USER 2:
    wait ends and updates also ID = 1 which is WRONG

锁定级别 可重复读取, 但带有FOR UPDATE。这样就可以了。

User 1:
    START TRANSACTION
    SELECT * FROM FreeCoreSlots WHERE status = 'FREE' LIMIT 1 FOR UPDATE -- returns id 1
User 2:
    START TRANSACTION
    SELECT * FROM FreeCoreSlots WHERE status = 'FREE' LIMIT 1 FOR UPDATE -- WAITS
User 1:
    UPDATE FreeCoreSlots SET status = 'USED' where ID = 1;
User 2:
    still waits
User 1:
    INSERT INTO user VALUES (...
    COMMIT;
USER 2:
    Gets back Id 2 from the select as the next free Id


 类似资料:
  • 问题内容: 我正在尝试选择mysql表中的重复行,它对我来说很好,但问题是它不是让我选择该查询中的所有字段,而是让我选择用作不同的字段名,让我写查询更好的了解 第一个工作正常 现在,当我尝试选择所有字段时,我最终会出现错误 我正在尝试选择最新的重复项,比如说ticket_id 127是行ID 7、8、9的3倍,因此我想选择一次,最新条目在这种情况下为9,这适用于所有其余项ticket_id的 任何

  • 问题内容: 我在SQL数据库表中使用了带有2个字段的select Disctinct语句。这是我的代码。 并产生错误:。请帮忙 问题答案: 您在sql语句中只选择了一个列Author。BookCode不存在,因此也不会出现在数据集中。 将BookCode包含在Sql语句中,它将得到修复

  • 问题内容: 我一直在尝试 HQL 和 Criteria的 不同组合,但我无法避免某些不必要的 联接 (在这两者中)和某些不必要的 选择 (在Criteria中)。 在我们的场景中, 段* 和 应用程序 实体之间具有 @ManyToMany 关系(导航是从段到应用程序)。 *** 首先,我尝试了以下 条件 : 威奇产生这个SQL: 如您所见, Criteria从APPLICATIONS选择列 ,我不

  • 我一直在尝试HQL和Criteria的不同组合,但我无法避免一些不必要的连接(在两者中)和一些不必要选择(在Criteria中)。 在我们的场景中,我们在Segment和Application实体之间有@ManyTo的关系(导航是从Segment到应用程序)。 首先我尝试了这个标准: Wich 生成此 SQL: 正如您所看到的,Criteria从APPLICATION中选择列,我不想被选择。我还没

  • 本文向大家介绍d3.js 不同的选择器,包括了d3.js 不同的选择器的使用技巧和注意事项,需要的朋友参考一下 示例 您可以使用不同的选择器选择元素: 按标签: "div" 按类别: ".class" 依编号: "#id" 按属性: "[color=blue]" 多个选择器(OR): "div1, div2, class1" 多个选择器(AND): "div1 div2 class1"

  • 我有一个从XML映射的类。为了简单起见,让我们想象这个类是这样的: 现在,现有的代码中充满了像这样的方法: …等等。你肯定有这个想法。 我需要包含一个新的方法来从employee返回一个新的属性,但是由于我觉得这对mantain来说是可怕的,所以我拒绝在那里添加一个新的方法。我正在考虑使用action模式,以某种方式避免至少重复againg和for循环,但我不得不说,我找不到一个聪明的解决方案。