支付场景下,乐观锁的实现(简洁版)
1、问题描述
看到一个同事建的数据库表,好奇打开看看。
create table db_paycenter.t_pay_order_divide
(id bigint auto_increment comment '主键id|20250402|XXX'primary key,user_id bigint not null comment 'userId|20250402|XXX',order_no varchar(64) default '' not null comment '订单号|20250402|XXX',status varchar(64) default '' not null comment '分账状态:init 初始化,process处理中, success成功 fail失败',remark varchar(256) null comment '备注|20250402|XXX',delete_flag int default 1 null comment '删除标记(1:正常 0:删除)|20250402|XXX',revision bigint default 1 not null comment '乐观锁|20250402|XXX',created_by varchar(256) null comment '创建人|20250402|XXX',created_time datetime default CURRENT_TIMESTAMP not null comment '创建时间|20250104|XXX',updated_by varchar(45) null comment '更新人|20250104|XXX',updated_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间|20250104|XXX'
)comment '订单分账表|20250402|XXX';create index idx_create_timeon db_paycenter.t_pay_order_divide (created_time);create index idx_updated_timeon db_paycenter.t_pay_order_divide (updated_time);
乐观锁这个字段,很好奇。
revision bigint default 1 not null comment '乐观锁|20250402|XXX',
问一下kimi试试,
2、问题提问
revision bigint default 1 not null comment '乐观锁|20250402|XXX',
数据库字段如上定义,乐观锁一般怎么现实
3、问题解答
乐观锁(Optimistic Locking)是一种在数据库操作中常用的并发控制机制,它假设在大多数情况下,数据在读取和修改期间不会发生冲突。乐观锁通过版本号(Version Number)或时间戳(Timestamp)来实现,每次更新数据时,都会检查版本号或时间戳是否发生变化,从而避免并发冲突。
乐观锁的实现方式
在你的数据库字段定义中,revision
字段被用作乐观锁的版本号字段。以下是乐观锁的典型实现步骤:
1. 数据库字段设计
你的字段定义已经很好地体现了乐观锁的设计:
revision BIGINT DEFAULT 1 NOT NULL COMMENT '乐观锁|20250402|XXX',
-
revision
字段用于存储版本号,默认值为1
。 -
每次更新记录时,
revision
字段的值会递增。
2. 读取数据
当应用程序读取数据时,会同时获取 revision
字段的值。例如:
SELECT id, name, revision FROM your_table WHERE id = 1;
假设查询结果为:
id | name | revision
1 | Test | 1
3. 修改数据
在更新数据时,应用程序会检查 revision
字段的值是否发生变化。如果发生变化,说明其他事务已经修改了这条记录,当前事务需要重新读取数据或放弃更新。
更新操作的 SQL 语句如下:
UPDATE your_table
SET name = 'New Name', revision = revision + 1
WHERE id = 1 AND revision = 1;
-
revision = revision + 1
:将版本号递增。 -
WHERE id = 1 AND revision = 1
:确保只有当版本号未发生变化时,才允许更新。
4. 检查更新结果
执行更新操作后,数据库会返回受影响的行数。如果受影响的行数为 0
,说明更新失败(即 revision
字段的值已经发生变化,其他事务已经修改了这条记录)。
在应用程序中,可以检查更新结果:
int rowsAffected = jdbcTemplate.update("UPDATE your_table SET name = ?, revision = revision + 1 WHERE id = ? AND revision = ?","New Name", 1, 1
);if (rowsAffected == 0) {throw new OptimisticLockException("数据已被其他事务修改");
}
乐观锁的优点和缺点
优点
-
性能较好:在大多数情况下,数据不会发生冲突,因此不需要加锁,减少了锁的开销。
-
适合高并发场景:适用于读多写少的场景,能够有效避免锁竞争。
缺点
-
冲突处理复杂:当发生冲突时,需要重新读取数据或放弃更新,增加了复杂性。
-
不适合写多场景:如果数据频繁更新,冲突的可能性会增加,性能优势可能会减弱。
乐观锁与悲观锁的对比
-
悲观锁(Pessimistic Locking):假设冲突是常态,通过加锁来避免冲突。适合写多场景,但性能开销较大。
-
乐观锁(Optimistic Locking):假设冲突是少数情况,通过版本号或时间戳来检测冲突。适合读多写少的场景,性能较好。
总结
乐观锁通过版本号字段(如 revision
)来实现,每次更新数据时,都会检查版本号是否发生变化。如果版本号发生变化,说明其他事务已经修改了这条记录,当前事务需要重新读取数据或放弃更新。乐观锁适用于读多写少的场景,能够有效减少锁的开销,提高性能。