代码之家  ›  专栏  ›  技术社区  ›  Michael Pliskin

在MySQL中模拟事务安全序列

  •  10
  • Michael Pliskin  · 技术社区  · 15 年前

    我们在InnoDB存储引擎和事务中大量使用MySQL,我们遇到了一个问题:我们需要一种很好的方法来模拟MySQL中Oracle的序列。要求如下: -并发支持 -交易安全 -最大性能(意味着最小化锁和死锁)

    我们不关心某些值是否不会被使用,即序列中的间隙是否正常。有一种简单的归档方法,通过使用计数器创建一个单独的innodb表,但是这意味着它将参与事务并引入锁和等待。我想试试带手动锁的Myisam表,还有其他想法或最佳实践吗?

    4 回复  |  直到 8 年前
        1
  •  15
  •   Arne Claassen    15 年前

    如果自动增量不足以满足您的需要,可以使用 n 这样命名的序列:

    创建一个表来存储序列:

    CREATE TABLE sequence (
      seq_name varchar(20) unique not null,
      seq_current unsigned int not null
    );
    

    假设您在表中有一行“foo”,您可以原子地获取下一个序列ID,如下所示:

    UPDATE sequence SET seq_current = (@next := seq_current + 1) WHERE seq_name = 'foo';
    SELECT @next;
    

    不需要锁。这两个语句都需要在同一个会话中执行,以便在执行select时实际定义本地变量@next。

        2
  •  9
  •   sth    15 年前

    正确的方法在 MySQL manual :

    UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1);
    SELECT LAST_INSERT_ID();
    
        3
  •  5
  •   Kalle Richter    8 年前

    我们是一家高交易量的游戏公司,需要这些解决方案来满足我们的需求。Oracle序列的一个特性也是可以设置的增量值。

    解决方案使用 DUPLICATE KEY .

    CREATE TABLE sequences (
      id BIGINT DEFAULT 1,
      name CHAR(20),
      increment TINYINT,
      UNIQUE KEY(name)
    );
    

    要获取下一个索引:

    用存储过程或函数抽象以下内容 sp_seq_next_val(VARCHAR) :

    INSERT INTO sequences (name) VALUES ("user_id") ON DUPLICATE KEY UPDATE id = id + increment;<br/>
    SELECT id FROM sequences WHERE name = "user_id";
    
        4
  •  0
  •   Jon Hopkins    15 年前

    表上的mysql identity列不会处理这个问题吗?

    创建表表名称 ( ID整数自动递增主键 )

    或者,您是否希望将它用于除插入另一个表之外的其他用途?

    如果您也使用过程语言(而不仅仅是SQL)进行编写,那么另一个选项是创建一个包含单个整数(或长整型)值的表,以及一个存储过程,该存储过程将其锁定(从中选择),然后递增并解锁,然后返回值。

    (注意-在返回值之前始终递增-如果有错误,它会最大限度地增加不重复的机会-或将整个事务包装在事务中。)

    然后,您可以独立于主插入/更新来调用它(这样它就不会被调用机制自动创建的任何事务捕获),然后将其作为参数传递到您想要使用它的任何地方。

    因为它独立于你正在做的其他事情,所以它应该很快并且避免锁定问题。即使您确实看到了由锁定引起的错误(除非数据库过载,否则不太可能),您也可以第二次/第三次调用它。