数据库序列(Sequence)
数据库序列Sequence是一种生成唯一整数序列的对象主要用于自动生成主键值或唯一标识符。核心概念序列是一个数据库对象按照指定的规则起始值、增量、最大值等生成一系列唯一的整数。每次调用序列时都会返回下一个值。主要特点1. 唯一性保证序列生成的每个值都是唯一的即使在高并发环境下也能保证不重复2. 独立性序列独立于表存在可以被多个表共享不依赖于事务即使事务回滚序列值也不会回退3. 可配置性起始值START WITH增量INCREMENT BY最小值/最大值MINVALUE/MAXVALUE是否循环CYCLE/NOCYCLE缓存大小CACHE不同数据库的实现Oracle-- 创建序列 CREATE SEQUENCE user_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; -- 使用序列 INSERT INTO users (id, name) VALUES (user_seq.NEXTVAL, 张三); SELECT user_seq.CURRVAL FROM dual; -- 查看当前值PostgreSQL-- 创建序列 CREATE SEQUENCE order_id_seq START WITH 1000 INCREMENT BY 1; -- 使用序列 INSERT INTO orders (id, amount) VALUES (nextval(order_id_seq), 100.00); -- 或者在表中直接使用 CREATE TABLE products ( id SERIAL PRIMARY KEY, -- 自动创建序列 name VARCHAR(100) );SQL Server-- 创建序列 CREATE SEQUENCE employee_seq AS INT START WITH 1 INCREMENT BY 1; -- 使用序列 INSERT INTO employees (id, name) VALUES (NEXT VALUE FOR employee_seq, 李四);MySQLMySQL 没有原生的序列对象但可以通过以下方式实现方式1: AUTO_INCREMENT最常用CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) );方式2: 模拟序列CREATE TABLE sequence_table ( seq_name VARCHAR(50) PRIMARY KEY, current_value INT ); -- 获取下一个值 UPDATE sequence_table SET current_value LAST_INSERT_ID(current_value 1) WHERE seq_name user_seq; SELECT LAST_INSERT_ID();序列 vs 自增主键注意事项⚠️序列值不连续的情况事务回滚后已使用的序列值不会回收数据库重启可能导致缓存的序列值丢失高并发下可能出现跳号⚠️性能考虑使用CACHE可以提高性能减少磁盘I/O但缓存越大数据库重启时丢失的序列值越多序列和索引序列Sequence和索引Index是数据库中两个完全不同的概念它们的用途、工作原理和使用场景都有本质区别。核心区别对比详细对比分析1. 功能目的不同序列生成唯一值-- 创建序列用于生成主键 CREATE SEQUENCE user_id_seq START WITH 1 INCREMENT BY 1; -- 使用序列生成唯一ID INSERT INTO users (id, name) VALUES (user_id_seq.NEXTVAL, 张三);索引加速查询-- 在用户名字段上创建索引加速查询 CREATE INDEX idx_user_name ON users(name); -- 查询时会使用索引加速 SELECT * FROM users WHERE name 张三;2. 工作原理不同序列的工作方式序列内部维护一个计数器 调用 NEXTVAL → 计数器 1 → 返回新值 当前值: 100 → 调用 NEXTVAL → 返回 101计数器更新为 101索引的工作方式索引是数据的有序副本以B树为例 原始数据: 索引结构: id | name B树索引(idx_user_name) ---|-------- root: [李, 王, 张] 1 | 张三 / | \ 2 | 李四 李四 王五 张三,赵六 3 | 王五 ↑ 4 | 赵六 查询张三时通过树快速定位 查询 WHERE name张三 → 遍历B树(3层) → 直接定位到数据 → 无需全表扫描3. 使用场景不同序列的典型场景-- 场景1: 主键自动生成 CREATE TABLE orders ( id NUMBER PRIMARY KEY, order_no VARCHAR2(50), amount NUMBER ); INSERT INTO orders (id, order_no, amount) VALUES (order_seq.NEXTVAL, ORD20260506001, 1000); -- 场景2: 订单号生成 SELECT ORD || TO_CHAR(SYSDATE, YYYYMMDD) || LPAD(order_seq.NEXTVAL, 8, 0) as order_no FROM dual; -- 结果: ORD2026050600000001 -- 场景3: 多表共享ID生成器 INSERT INTO table_a (id) VALUES (common_seq.NEXTVAL); INSERT INTO table_b (id) VALUES (common_seq.NEXTVAL); -- 保证跨表的ID唯一性索引的典型场景-- 场景1: 加速 WHERE 条件查询 CREATE INDEX idx_email ON users(email); SELECT * FROM users WHERE email testexample.com; -- 快速定位 -- 场景2: 加速 ORDER BY 排序 CREATE INDEX idx_create_time ON orders(create_time); SELECT * FROM orders ORDER BY create_time DESC; -- 利用索引顺序 -- 场景3: 加速 JOIN 连接 CREATE INDEX idx_order_user_id ON orders(user_id); SELECT u.name, o.order_no FROM users u JOIN orders o ON u.id o.user_id; -- 连接时使用索引 -- 场景4: 唯一性约束 CREATE UNIQUE INDEX idx_unique_phone ON users(phone); -- 既加速查询又保证唯一性4. 性能影响不同序列的性能特点// 序列几乎不影响性能 // INSERT 操作只是获取一个数字开销极小 Long id sequenceGenerator.nextValue(); // 内存操作非常快 insertUser(id, 张三);✅ 读取NEXTVAL 操作非常快尤其是使用 CACHE 时✅ 写入不增加额外的写入开销⚠️ 并发高并发下可能成为瓶颈可通过 CACHE 优化索引的性能特点-- 查询性能提升明显 -- 无索引: 全表扫描 1000万条记录 → 耗时 5秒 SELECT * FROM users WHERE phone 13800138000; -- 有索引: B树查找只需3-4次I/O → 耗时 5毫秒 CREATE INDEX idx_phone ON users(phone); SELECT * FROM users WHERE phone 13800138000; -- 但写入性能下降 INSERT INTO users (...) VALUES (...); -- 需要同时更新数据和所有相关索引✅ 查询大幅提升 SELECT 性能❌ 写入每次 INSERT/UPDATE/DELETE 都要维护索引降低写入性能⚠️ 存储索引占用额外的磁盘空间5. 维护成本不同序列的维护-- 几乎不需要维护 -- 查看序列状态 SELECT sequence_name, last_number, cache_size FROM user_sequences WHERE sequence_name USER_ID_SEQ; -- 修改序列很少需要 ALTER SEQUENCE user_id_seq INCREMENT BY 2;索引的维护-- 需要定期维护 -- 重建索引碎片整理 ALTER INDEX idx_user_name REBUILD; -- 分析索引使用情况 SELECT index_name, leaf_blocks, distinct_keys FROM user_indexes WHERE table_name USERS; -- 删除无用索引减少写入开销 DROP INDEX idx_unused_column;总结