在MySQL中创建实现自增的序列(Sequence)

本文介绍如何在MySQL中通过创建特殊表及函数模拟Oracle的sequence功能,实现自动递增ID的生成。详细步骤包括创建Sequence管理表、取当前值与下一个值的函数等。

由于mysql和oracle不太一样,不支持直接的sequence,所以需要创建一张table来模拟sequence的功能,理由sql语句如下:
第一步:创建--Sequence 管理表

DROP TABLE IF EXISTS sequence;
CREATE TABLE sequence (
     name VARCHAR(50) NOT NULL,
     current_value INT NOT NULL,
     increment INT NOT NULL DEFAULT 1,
     PRIMARY KEY (name)
) ENGINE=InnoDB;


?

 
第二步:创建--取当前值的函数

DROP FUNCTION IF EXISTS currval;
DELIMITER $
CREATE FUNCTION currval (seq_name VARCHAR(50))
     RETURNS INTEGER
     LANGUAGE SQL
     DETERMINISTIC
     CONTAINS SQL
     SQL SECURITY DEFINER
     COMMENT ''
BEGIN
     DECLARE value INTEGER;
     SET value = 0;
     SELECT current_value INTO value
          FROM sequence
          WHERE name = seq_name;
     RETURN value;
END
$
DELIMITER ;


 
第三步:创建--取下一个值的函数

DROP FUNCTION IF EXISTS nextval;
DELIMITER $
CREATE FUNCTION nextval (seq_name VARCHAR(50))
     RETURNS INTEGER
     LANGUAGE SQL
     DETERMINISTIC
     CONTAINS SQL
     SQL SECURITY DEFINER
     COMMENT ''
BEGIN
     UPDATE sequence
          SET current_value = current_value + increment
          WHERE name = seq_name;
     RETURN currval(seq_name);
END
$
DELIMITER ;


第四步:创建--更新当前值的函数

DROP FUNCTION IF EXISTS setval;
DELIMITER $
CREATE FUNCTION setval (seq_name VARCHAR(50), value INTEGER)
     RETURNS INTEGER
     LANGUAGE SQL
     DETERMINISTIC
     CONTAINS SQL
     SQL SECURITY DEFINER
     COMMENT ''
BEGIN
     UPDATE sequence
          SET current_value = value
          WHERE name = seq_name;
     RETURN currval(seq_name);
END
$
DELIMITER ;


 
第五步:测试函数功能
当上述四步完成后,可以用以下数据设置需要创建的sequence名称以及设置初始值和获取当前值和下一个值。

  • INSERT INTO sequence VALUES ('TestSeq', 0, 1);----添加一个sequence名称和初始值,以及自增幅度
  • SELECT SETVAL('TestSeq', 10);---设置指定sequence的初始值
  • SELECT CURRVAL('TestSeq');--查询指定sequence的当前值
  • SELECT NEXTVAL('TestSeq');--查询指定sequence的下一个值


 
在java代码中,可直接创建sql语句查询下一个值,这样就解决了流水号唯一的问题。
贴出部分代码(已测试通过)

public void testGetSequence() {
  Connection conn = JDBCUtils.getConnection(url, userName, password);
  String sql = "SELECT CURRVAL('TestSeq');";
  PreparedStatement ptmt = null;
  ResultSet rs = null;
  try {
    ptmt = conn.prepareStatement(sql);
    rs = ptmt.executeQuery();
    int count = 0;
    while (rs.next()) {
      count = rs.getInt(1);
    }
    System.out.println(count);
  } catch (SQLException e) {
    e.printStackTrace();
  } finally {
    JDBCUtils.close(rs, ptmt, conn);
  }
}


ps:在应用中,还有一种用java代码去实现模拟自增sequence的方式,具体思路是创建一张存放sequence的table,然后通过java调用sql语句去查询和修改这个table中指定sequence名称的值,这种方式请加上synchronized。具体代码这里就不上传了,因为实现了,未去测试过。


在 oracle 中, sequence 提供多表多字段可共用一个不重复值。 Mysql 中存在自增列,基本可以满足 PK 的要求。但自增列存在限制:

a. 只能用于表中的一个字段,一张不能同时存在两个以上的自增列 ;

b. 自增列必须被定义为 key ( PK 或 FK ) ;

c. 自增列不能被多个表共用 ;

d. 当 insert 语句不包括自增字段或将其值设置为 NULL 时,该值会自动填上。

在不要求字段顺序递增的情况下,可以在 Mysql 中实现序列,再来看下面一个例子:

DROP TABLE IF EXISTS sequence;
  
-- 建sequence表,指定seq列为无符号大整型,可支持无符号值:0(default)到18446744073709551615(0到2^64–1)。
CREATE TABLE sequence (
   name       VARCHAR(50) NOT NULL,
     current_value   BIGINT UNSIGNED NOT NULL DEFAULT 0,
     increment     INT NOT NULL DEFAULT 1,
     PRIMARY KEY (name)  -- 不允许重复seq的存在。
) ENGINE=InnoDB;
  
  
DELIMITER /
  
DROP FUNCTION IF EXISTS currval /
  
CREATE FUNCTION currval(seq_name VARCHAR(50))
RETURNS BIGINT
BEGIN
     DECLARE value BIGINT;
     SELECT current_value INTO value
     FROM sequence
     WHERE upper(name) = upper(seq_name); -- 大小写不区分.
     RETURN value;
END;
/
  
DELIMITER ;
  
  
DELIMITER /
  
DROP FUNCTION IF EXISTS nextval /
  
CREATE FUNCTION nextval (seq_name VARCHAR(50))
RETURNS BIGINT
BEGIN
     DECLARE value BIGINT;
     UPDATE sequence
     SET current_value = current_value + increment
     WHERE upper(name) = upper(seq_name);
     RETURN currval(seq_name);
END;
/
  
DELIMITER ;
  
DELIMITER /
  
DROP FUNCTION IF EXISTS setval /
  
CREATE FUNCTION setval (seq_name VARCHAR(50), value BIGINT)
RETURNS BIGINT
BEGIN
     UPDATE sequence
     SET current_value = value
     WHERE upper(name) = upper(seq_name);
     RETURN currval(seq_name);
END;
/
  
DELIMITER ;


 在 SQL 中使用序列:
创建序列,往sequence表插入值即可:
mysql> insert into sequence set name='myseq';


查看当前已建序列:
mysql> select * from sequence;

+-------+---------------+-----------+
| name | current_value | increment |
+-------+---------------+-----------+
| myseq |       0 |     1 |
+-------+---------------+-----------+
1 row in set (0.00 sec)

获得序列的下一个值,第一次使用,因此值为1:

mysql> select nextval('myseq');

+------------------+
| nextval('myseq') |
+------------------+
|        1 |
+------------------+
1 row in set (0.00 sec)

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/28282660/viewspace-2061242/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/28282660/viewspace-2061242/

<think>好的,我现在需要帮用户解决一个问题:在没有数据库权限的情况下实现序列功能。用户之前的问题是关于如何在没有mysql.help_topic权限的情况下拆分字符串,现在他进一步想寻找依赖数据库功能序列生成方案。 首先,我需要回顾用户之前提供的解决方案。用户之前创建了一个自序列custom_sequence,通过插入空值利用AUTO_INCREMENT生成连续ID。但现在的需求是使用数据库的自功能,也就是能依赖AUTO_INCREMENT。这可能是因为用户权限足,或者希望完全控制序列生成,避免依赖数据库特性。 接下来,我需要参考用户提供的引用内容。引用1提到在MySQL创建序列,但用户现在希望依赖自功能。引用2和5涉及Oracle和达梦数据库的系统序列创建,但用户的环境可能是MySQL,且需要跨数据库解决方案。引用3提到MyBatis获取自主键,但同样依赖数据库的自机制。引用4是关于创建数据库链接,可能相关。因此,用户的需求是寻找一种独立于数据库功能的通用方法。 用户可能的需求包括: 1. 完全控制序列生成,依赖数据库的自特性。 2. 支持并发访问,保证序列的唯一性和连续性。 3. 适应同的数据库系统,如MySQL、Oracle等。 4. 避免权限问题,需要特殊权限即可实现。 接下来,我需要思考如何在使用自功能的情况下生成序列。常见的方法包括使用应用程序控制序列、使用数据库模拟序列、利用时间戳或UUID等。但用户想要的是序列功能,所以可能倾向于用数据库来管理序列,但需要避免使用自列。 可能的解决方案: 1. 使用锁机制和事务来手动递序列值。 2. 使用行级锁确保并发安全。 3. 通过应用程序或存储过程管理序列的生成。 4. 考虑性能问题,尤其是在高并发场景下。 然后,我需要考虑数据库的兼容性。例如,在MySQL中使用存储过程,而在Oracle中使用序列对象,但用户希望依赖这些特性。因此,可能需要创建一个通用的序列结构,通过更新操作来获取下一个值。 需要解决的问题: - 如何保证并发下的序列唯一性? - 如何处理事务回滚导致的序列间隙? - 如何提高性能,避免成为瓶颈? 可能的实现步骤: 1. 创建序列,包含序列名称、当前值、步长等字段。 2. 使用事务和行锁在获取下一个值时更新当前值。 3. 提供函数或存储过程来获取下一个序列值。 验证方案的可行性,例如在MySQL中测试,确保在没有自权限的情况下,通过手动更新和锁机制实现序列生成。同时,需要考虑数据库的语法差异,比如在Oracle中使用SELECT FOR UPDATE,而在MySQL中使用类似的锁机制。 还需要考虑用户可能没有创建存储过程的权限,或者希望尽量减少数据库操作,因此可能需要应用程序层面的序列管理,比如使用Redis或其他缓存系统生成序列过用户的问题可能更倾向于数据库层面的解决方案。 最后,总结可能的解决方案,并评估优缺点,选择最适合用户需求的方法,同时参考之前的回答结构,提供分步实现、关键点说明、性能对比等,确保用户能够理解和实施。</think>### 依赖数据库功能序列实现方案 #### 一、核心设计原理 通过创建自定义序列+原子更新操作实现序列生成,避免依赖数据库特性,适用于所有主流数据库MySQL/Oracle/PostgreSQL等)。关键实现逻辑如下: ```sql -- 创建通用序列结构(支持多业务序列) CREATE TABLE custom_sequences ( seq_name VARCHAR(50) PRIMARY KEY, -- 序列名称 current_val BIGINT NOT NULL, -- 当前序列值 increment_val INT DEFAULT 1, -- 步长值 max_val BIGINT DEFAULT 9999999999 -- 溢出保护 ); ``` #### 二、分步实现方法 **步骤1:初始化业务序列** ```sql -- 创建用户ID序列(初始值=当前系统最大用户ID+1) INSERT INTO custom_sequences(seq_name, current_val) SELECT 'user_id_seq', MAX(user_id)+1 FROM users; ``` **步骤2:实现线程安全的取号逻辑(MySQL版)** ```sql DELIMITER // CREATE FUNCTION nextval(seq VARCHAR(50)) RETURNS BIGINT BEGIN DECLARE current BIGINT; START TRANSACTION; -- 使用行级锁保证原子性[^1] SELECT current_val INTO current FROM custom_sequences WHERE seq_name = seq FOR UPDATE; UPDATE custom_sequences SET current_val = current_val + increment_val WHERE seq_name = seq; COMMIT; RETURN current; END // DELIMITER ; ``` **步骤3:使用序列生成数据** ```sql -- 插入新用户时调用 INSERT INTO users(user_id, name) VALUES (nextval('user_id_seq'), '张三'); ``` #### 三、关键特性对比 | 特性 | 数据库 | 本方案 | Redis生成 | |-----------------------|------------|--------------|-------------| | 跨数据库兼容性 | ❌ | ✅ | ✅ | | 无超管权限要求 | ❌ | ✅ | ✅ | | 支持序列重置 | ❌ | ✅ | ✅ | | 事务回滚值回收 | ❌ | ❌ | ✅ | | 10万次/秒并发能力 | ✅ | 需优化锁机制 | ✅ | #### 四、性能优化策略 1. **批量取号优化** ```sql -- 一次性获取100个序列号 UPDATE custom_sequences SET current_val = current_val + 100 WHERE seq_name = 'order_id_seq'; SELECT current_val - 100 AS start_id, current_val AS end_id; ``` 2. **分段缓存方案** ```java // 应用层缓存示例(Java) class SeqCache { private long current = 0; private long end = 0; synchronized long nextId() { if(current >= end) { // 调用数据库获取新批次 long[] range = getNextRange(); current = range[0]; end = range[1]; } return current++; } } ``` 3. **锁机制优化(Oracle版)** ```sql -- 使用RETURNING子句减少锁时间 DECLARE next_num NUMBER; BEGIN UPDATE custom_sequences SET current_val = current_val + 1 WHERE seq_name = 'product_id_seq' RETURNING current_val INTO next_num; :NEW.product_id := next_num; END; ``` #### 五、异常处理机制 1. **断号检测** ```sql -- 每日校验断号率(阈值可配置) SELECT (MAX(current_val) - COUNT(*)) AS missing_count, (MAX(current_val) - COUNT(*)) / MAX(current_val) AS missing_rate FROM custom_sequences JOIN business_table ON business_id >= seq_start; ``` 2. **事务回滚补偿** ```sql -- 创建断号回收 CREATE TABLE seq_recycle_bin ( seq_name VARCHAR(50), lost_val BIGINT, recover_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 定时任务回收断号 INSERT INTO seq_recycle_bin SELECT 'user_id_seq', t.missing_id, NOW() FROM ( SELECT (seq.current_val - nums.num) AS missing_id FROM custom_sequences seq CROSS JOIN number_table nums WHERE nums.num BETWEEN 1 AND seq.current_val AND NOT EXISTS ( SELECT 1 FROM users WHERE user_id = (seq.current_val - nums.num) ) ) t; ``` #### 六、跨数据库适配示例 **PostgreSQL实现(利用RETURNING特性)** ```sql CREATE FUNCTION nextval_pg(seq_name TEXT) RETURNS BIGINT AS $$ DECLARE result BIGINT; BEGIN UPDATE custom_sequences SET current_val = current_val + increment_val WHERE seq_name = $1 RETURNING current_val INTO result; RETURN result; END; $$ LANGUAGE plpgsql; ``` **SQL Server实现(使用OUTPUT子句)** ```sql CREATE PROCEDURE nextval_mssql @seq_name VARCHAR(50), @next_val BIGINT OUTPUT AS BEGIN UPDATE custom_sequences SET @next_val = current_val, current_val += increment_val WHERE seq_name = @seq_name; END; ``` ### 七、方案选择建议 1. **简单业务场景** 直接使用基础版`nextval`函数,单条记录更新方式 2. **高并发场景** 采用分段缓存方案,建议缓存量=QPS×平均处理时间×2 3. **分布式系统** 组合使用数据库方案+Redis集群,通过双号段缓冲保证可用性 4. **财务类敏感数据** 必须关闭缓存功能,每次实时获取序列号,确保绝对连续
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值