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

mysql-建立类似于Oracle序列的机制

颜文康
2023-03-14
问题内容

MySQL提供了一种自动增加记录ID的机制。这可以用于许多目的,但是我需要能够使用ORACLE提供的序列。显然,为此目的创建表是没有意义的。

解决方案应该很简单:

1)创建一个表以托管所有需要的序列,

2)创建一个增加特定序列的值并返回新值的函数,

3)创建一个返回序列当前值的函数。

从理论上讲,它看起来很简单…但是…

当增加一个序列的值时(与nextvalOracle中的相同),您需要阻止其他会话执行此操作(甚至获取当前值),直到更新完成。

两个理论选择:

a-使用UPDATE语句,一次即可返回新值,或者

b-将表锁定在UPDATE和SELECT之间。

不幸的是,MySQL似乎不允许在函数/过程中锁定表,并且在尝试在单个语句(例如UPDATE … RETURNING
…)中完成整个操作时,必须使用可以保留的@类型变量功能/程序的完成。

有人对此有想法/可行的解决方案吗?

谢谢。


问题答案:

以下是带有FOR UPDATE 意向锁的简单示例。使用INNODB引擎的行级锁。该示例显示了四行用于下一个可用序列,这些序列不会受到众所周知的INNODB间隙异常(在AUTO_INCREMENT使用失败后出现间隙的情况)的影响。

架构:

-- drop table if exists sequences;
create table sequences
(   id int auto_increment primary key,
    sectionType varchar(200) not null,
    nextSequence int not null,
    unique key(sectionType)
) ENGINE=InnoDB;

-- truncate table sequences;
insert sequences (sectionType,nextSequence) values
('Chassis',1),('Engine Block',1),('Brakes',1),('Carburetor',1);

样例代码:

START TRANSACTION; -- Line1
SELECT nextSequence into @mine_to_use from sequences where sectionType='Carburetor' FOR UPDATE; -- Line2 
select @mine_to_use; -- Line3
UPDATE sequences set nextSequence=nextSequence+1 where sectionType='Carburetor'; -- Line4
COMMIT; -- Line5

理想情况下,您根本没有Line3或繁琐的代码,这会使其他客户端延迟等待锁定。意思是,让你的下一个序列来使用,执行更新(递增部分),并且COMMIT
尽快

上面的一个存储过程:

DROP PROCEDURE if exists getNextSequence;
DELIMITER $$
CREATE PROCEDURE getNextSequence(p_sectionType varchar(200),OUT p_YoursToUse int)
BEGIN
    -- for flexibility, return the sequence number as both an OUT parameter and a single row resultset
    START TRANSACTION;
    SELECT nextSequence into @mine_to_use from sequences where sectionType=p_sectionType FOR UPDATE;
    UPDATE sequences set nextSequence=nextSequence+1 where sectionType=p_sectionType;
    COMMIT; -- get it and release INTENTION LOCK ASAP
    set p_YoursToUse=@mine_to_use; -- set the OUT parameter
    select @mine_to_use as yourSeqNum; -- also return as a 1 column, 1 row resultset
END$$
DELIMITER ;

测试:

set @myNum:= -1;
call getNextSequence('Carburetor',@myNum);
+------------+
| yourSeqNum |
+------------+
|          4 |
+------------+
select @myNum; -- 4

根据您的需要相应地修改存储过程,例如只有两种检索序号的机制中的一种(OUT参数或结果集)。换句话说,很容易放弃OUT参数概念。

如果您不遵循LOCK的ASAP发行(显然在更新后不需要),并继续执行耗时的代码,则在发行之前,对于等待序列的其他客户端,可能会在超时后发生以下情况数:

错误1205(HY000):超出了锁定等待超时;尝试重新启动事务

希望这绝不是问题。

show variables where variable_name='innodb_lock_wait_timeout';

MySQL手册页innodb_lock_wait_timeout。

目前在我的系统上,它的值为50(秒)。在大多数情况下,超过一两秒钟的等待可能无法忍受。

在TRANSACTIONS期间,另一个有趣的地方是以下命令的输出部分:

SHOW ENGINE INNODB STATUS;


 类似资料:
  • 本文向大家介绍MySQL实现类似Oracle序列的方案,包括了MySQL实现类似Oracle序列的方案的使用技巧和注意事项,需要的朋友参考一下 MySQL实现类似Oracle的序列 Oracle一般使用序列(Sequence)来处理主键字段,而MySQL则提供了自增长(increment)来实现类似的目的; 但在实际使用过程中发现,MySQL的自增长有诸多的弊端:不能控制步长、开始索引、是否循环等

  • 本文向大家介绍有没有类似于MySQL中Oracle的trunc(sysdate)的功能?,包括了有没有类似于MySQL中Oracle的trunc(sysdate)的功能?的使用技巧和注意事项,需要的朋友参考一下 是的,您可以用于仅获取MySQL中的日期部分,并且可以用于获取MySQL中的当前日期。 当前日期如下- 让我们首先创建一个表- 使用插入命令在表中插入一些记录- 使用select语句显示表

  • 问题内容: 我的输入就是这样。 O / p:应该像 我想要一个可以在数据库级别上触发的查询。我尝试了各种方法,但无法做到这一点… 问题答案: 11g及更高版本: 使用listagg: 10g及以下: 一种方法是使用功能: 要使用该功能: 注意:在某些较早版本的Oracle上有一个(不受支持的)功能,该功能可能会对您有所帮助-有关详细信息,请参见此处。 在MySQL中:

  • 本文向大家介绍mysql类似oracle rownum写法实例详解,包括了mysql类似oracle rownum写法实例详解的使用技巧和注意事项,需要的朋友参考一下 rownum是oracle才有的写法,rownum在oracle中可以用于取第一条数据,或者批量写数据时限定批量写的数量等 mysql取第一条数据写法 oracle取第一条数据写法 ok,上面是mysql和oracle取第一条数据的

  • 问题内容: 我有类似于Oracle的下表。 我有序列前缀/后缀的逻辑,但为简单起见,此处略过。 假设在当前表中有两条记录。 我的foo表结构是这样的, ; 约束: 我不能使用MySQL AUTO_INCREMENT列,因为它包含不同的客户数据,并且每个客户都可以选择自动生成或手动输入,并且如果客户选择了auto_generation,则应该有空白。因此,customer = 1选择了它,foo#应

  • 本文向大家介绍使用MySQL LIKE创建类似于常规表的临时表,包括了使用MySQL LIKE创建类似于常规表的临时表的使用技巧和注意事项,需要的朋友参考一下 让我们首先创建一个表- 使用插入命令在表中插入一些记录- 使用select语句显示表中的所有记录- 这将产生以下输出- 以下是创建类似于常规表的临时表的查询- 让我们检查表的描述- 这将产生以下输出-