mysql - 制作类似于 Oracle 序列的机制
mysql - making a mechanism similar to Oracle's seqences
MySQL 提供了一种自动增加记录 ID 的机制。这对于许多用途来说都是可行的,但我需要能够使用 ORACLE 提供的序列。显然,为此目的创建 table 没有意义。
解决方案应该很简单:
1) 创建一个 table 来承载所有需要的序列,
2) 创建一个增加特定序列值的函数,returns 新值,
3) 创建一个 return 序列当前值的函数。
理论上,它看起来很简单......但是......
当增加一个序列的值时(与Oracle中的nextval
非常相似),您需要阻止其他会话执行此操作(甚至获取当前值)直到更新完成。
两个理论上的选择:
a - 使用 UPDATE 语句一次性 return 新值,或者
b - 锁定更新和 SELECT 之间的 table。
不幸的是,MySQL 似乎不允许在函数/过程中锁定 tables,并且试图在单个语句中完成整个事情(如 UPDATE... RETURNING...) 您必须使用在 function/procedure.
完成后仍然存在的 @ 类型变量
有人对此有 idea/working 解决方案吗?
谢谢。
以下是一个带有 FOR UPDATE intention lock 的简单示例。 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
、ASAP.
以上存储过程中:
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(更新后显然不需要),继续执行耗时代码,在释放之前,那么超时后会出现以下情况等待序列号的其他客户端:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting
transaction
希望这永远不会成为问题。
show variables where variable_name='innodb_lock_wait_timeout';
MySQL innodb_lock_wait_timeout.
手册页
目前在我的系统上它的值为 50(秒)。在大多数情况下,超过一两秒的等待可能是无法忍受的。
在 TRANSACTIONS 期间还需要注意的是以下命令的输出部分:
SHOW ENGINE INNODB STATUS;
MySQL 提供了一种自动增加记录 ID 的机制。这对于许多用途来说都是可行的,但我需要能够使用 ORACLE 提供的序列。显然,为此目的创建 table 没有意义。
解决方案应该很简单:
1) 创建一个 table 来承载所有需要的序列,
2) 创建一个增加特定序列值的函数,returns 新值,
3) 创建一个 return 序列当前值的函数。
理论上,它看起来很简单......但是......
当增加一个序列的值时(与Oracle中的nextval
非常相似),您需要阻止其他会话执行此操作(甚至获取当前值)直到更新完成。
两个理论上的选择:
a - 使用 UPDATE 语句一次性 return 新值,或者
b - 锁定更新和 SELECT 之间的 table。
不幸的是,MySQL 似乎不允许在函数/过程中锁定 tables,并且试图在单个语句中完成整个事情(如 UPDATE... RETURNING...) 您必须使用在 function/procedure.
完成后仍然存在的 @ 类型变量有人对此有 idea/working 解决方案吗?
谢谢。
以下是一个带有 FOR UPDATE intention lock 的简单示例。 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
、ASAP.
以上存储过程中:
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(更新后显然不需要),继续执行耗时代码,在释放之前,那么超时后会出现以下情况等待序列号的其他客户端:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
希望这永远不会成为问题。
show variables where variable_name='innodb_lock_wait_timeout';
MySQL innodb_lock_wait_timeout.
手册页目前在我的系统上它的值为 50(秒)。在大多数情况下,超过一两秒的等待可能是无法忍受的。
在 TRANSACTIONS 期间还需要注意的是以下命令的输出部分:
SHOW ENGINE INNODB STATUS;