用mysql锁保证线程安全

需求

  • 场景一:多线程对表的一个计数字段原子性递增+1
  • 场景二:多线程查询表的一条未删除数据,获取后将数据标记为删除,每个线程不能获取相同数据

在数据库压力小的业务场景,可以借用mysql锁去实现业务逻辑。

概念

  • 锁:mysql按锁的范围有库锁、表锁、行锁、间隙锁,常用的Innodb引擎锁的粒度是行锁,其他不提供事务的MyIsam、Memory 锁的粒度是表锁。
  • 锁的时间:如果是update语句,执行update语句开始上锁,事务结束时,释放锁。所以平常编码中,尽量将修改删除方法放在一个事务中的最后执行,使其尽量晚的占据锁、尽量早的释放锁。
  • 如何确定锁的范围:这很复杂,有很多相关条件。简单说innodb下,在我们要用的update语句中:

如果update的条件语句使用的字段,是非索引字段的话,会锁整张表,比如下面语句,user表只有主键索引,没有其他辅助索引

update user set age=26 where name='melo'

如果update条件字段有索引,则不会锁表,会符合条件的字段以及周围间隙。 如下面语句,id为5的数据肯定会锁住,但不一定光锁这条语句,mysql有间隙锁,可能锁的是id 1-10之间的数据,间隙锁的判断条件很多。总之,对update条件字段加索引可以防止锁表、减少锁范围,因为锁其实锁的是索引。

update user set age=26 where id = 5

场景一实现

对test表的 use_count原子性递增,利用update语句会锁符合条件数据的特点,同一时刻只有一个事务能占据id为1的锁,在此事务中,查询计数字段use_count 并+1,当事务提交或回滚,其他线程事务才有机会。

注意update语句,子查询是本表的话要利用中间表实现

	UPDATE test
SET use_count = (
	SELECT
		b.use_count +1
	FROM
		(
			SELECT
				z.id,
				z.use_count
			FROM
				test z
			WHERE
				z.id = 1
		) AS b
)
WHERE
	id = 1

场景二实现:

1.开启事务

2.用for update语句,对查询到的数据上锁

3.修改查询到的语句

4.提交事务,释放锁

SELECT
	a.id,
	a.deleted
FROM
	test a
WHERE
	a.deleted = 0
LIMIT 1 
FOR UPDATE;

猜你喜欢

转载自blog.csdn.net/u014203449/article/details/111404798