通过乐观锁(版本号)降低并发时的锁竞争问题

 在高并发下,经常需要处理SELECT之后,在业务层处理逻辑,再执行UPDATE的情况。

  若两个连接并发查询同一条数据,然后在执行一些逻辑判断或业务操作后,执行UPDATE,可能出现与预期不相符的结果。

  在不使用悲观锁与复杂SQL的前提下,可以使用乐观锁处理该问题,同时兼顾性能。

  场景模拟:

  假设一张表两个字段,一个id,一个use_count。
表里存了100个id,每个id对应自己的use_count。

  当id每使用一次,use_count要加1。当use_count大于1000时,这个id就不能在被使用了(换句话说 无法从数据库中查出)。

  在高并发情况下,会遇到一种问题:假设数据表中有一条记录为:id=123456, use_count=999
  A与B两个连接并发查询这个id=123456,都执行下列SQL:

SELECT * FROM table WHERE id=123456 and use_count < 1000;

  A先执行,得到id=123456的use_count是999,之后在程序里做了一些逻辑判断或业务操作后执行SQL:

UPDATE table SET use_count + 1 WHERE id=123456;

  在A做判断且没有update之前,B也执行了查询SQL,发现use_count是999,之后它也会执行SQL:

UPDATE table SET use_count + 1 WHERE id=123456;

  但是,事实上B不应该取得这个id,因为A已经是第1000个使用者。

  处理步骤如下:

  1、添加第3个字段version,int类型,default值为0。version值每次update时作加1处理。

ALTER TABLE table ADD COLUMN version INT DEFAULT ‘0‘ NOT NULL AFTER use_count;

  2、SELECT时同时获取version值(例如为3)。

SELECT use_count, version FROM table WHERE id=123456 AND use_count < 1000;

  3、UPDATE时检查version值是否为第2步获取到的值。

UPDATE table SET version=4, use_count=use_count+1 WHERE id=123456 AND version=3;

  如果UPDATE的记录数为1,则表示成功。
如果UPDATE的记录数为0,则表示已经被其他连接UPDATE过了,需作异常处理。

时间: 2024-11-10 13:22:42

通过乐观锁(版本号)降低并发时的锁竞争问题的相关文章

并发浅谈-锁和Token的应用

并发 即在同一时刻内有多个完成同一任务的进程或线程在同时运行.并发一般发生在大流量集中访问如抢购或秒杀等业务场景中,它所带来的影响主要表现在以下两个方面:1:造成系统的负载压力过大.比如说mysql天生在处理大并发时表现的异常吃力,并发大时经常可以造成数据库挂掉.2:造成业务资源的竞争出现.比如说兑换一个激活码,并发下可能会出现两个人同时兑换到的同一个激活码. 从开发的经验来看,一般开发者在写程序逻辑时,绝大多数的情况下是没有考虑并发问题的:这其中有两个方面,一是与业务有关,二是与经验有关:其中

乐观锁解决高并发

对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研究一下常见的并发和同步吧. 为了更好的理解并发和同步,我们需要先明白两个重要的概念:同步和异步    1.同步和异步的区别和联系          所谓同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到 返回的值或消息后才往下执行其它的命令. 异步

利用索引降低并发事务引起的锁【转】

时常,来自不同连接的线程会对同一张表进行读/更新操作,这种并发操作会导致阻塞,同时SQL Server会自动处理以防止脏读.然而,有种情景很常见,那就是每个连接要读/更新的行互相排斥,换句话说,就是各个连接读/更新的行没有交集.在这片文章中,将像大家展示如何恰当地使用索引来降低阻塞的发生,以便多个读/更新能够同时操作同一张表. 创建TEST表如下: SET ANSI_NULLS ON GO   SET QUOTED_IDENTIFIER ON GO   CREATE TABLE [dbo].[T

ElasticSearch 并发冲突+悲观锁与乐观锁+基于_version和external version进行乐观锁并发控制

1.图解剖析Elasticsearch并发冲突问题 2.图解剖析悲观锁与乐观锁两种并发控制方案 3.图解Elasticsearch内部如何基于_version进行乐观锁并发控制 (1)_version元数据 PUT /test_index/test_type/6{ "test_field": "test test"} { "_index": "test_index", "_type": "test

网上资料笔记总结!!数据库事务并发问题,锁机制和对应的4种隔离级别

数据库事务并发问题 数据库的操作通常为写和读,就是所说的CRUD:增加(Create).读取(Read).更新(Update)和删除(Delete).事务就是一件完整要做的事情.事务是恢复和并发控制的基本单位.事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少.事务在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序.是数据库中各种数据项的一个程序执行单元.事务是用户定义的一个操作序列(多个表同时读写).这些操作要么都做,要么都不做,是一个不可分割的工作单位

SQL-乐观锁,悲观锁之于并发

SQL-乐观锁,悲观锁之于并发 每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不爱说话,默默承受着编程的巨大压力,除了技术上的交流外,他们不愿意也不擅长和别人交流,更不乐意任何人走进他们的内心! 最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来.我们都知道计算机技术发展日新月异,速度惊人的快,你我稍不留神

zbb20180929 thread 自旋锁、阻塞锁、可重入锁、悲观锁、乐观锁、读写锁、对象锁和类锁

1.自旋锁自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行.若线程依然不能获得锁,才会被挂起.使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强.因此,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具有一定的积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,往往毅然无法获得对应的锁,不仅仅白白浪费了CPU时间,最终还是免不了被挂起的操作 ,

java并发编程常见锁类型

锁是java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息.锁是解决并发冲突的重要工具.在开发中我们会用到很多类型的锁,每种锁都有其自身的特点和适用范围.需要深刻理解锁的理念和区别,才能正确.合理地使用锁.常用锁类型乐观锁与悲观锁悲观锁对并发冲突持悲观态度,先取锁后访问数据,能够较大程度确保数据安全性.而乐观锁认为数据冲突的概率比较低,可以尽可能多地访问数据,只有在最终提交数据进行持久化时才获取锁.悲观锁总是先获取锁,会增加很多额外的开销,

Java并发机制及锁的实现原理

Java并发编程概述 并发编程的目的是为了让程序运行得更快,但是,并不是启动更多的线程就能让程序最大限度地并发执行.在进行并发编程时,如果希望通过多线程执行任务让程序运行得更快,会面临非常多的挑战,比如上下文切换的问题.死锁的问题,以及受限于硬件和软件的资源限制问题,本章会介绍几种并发编程的挑战以及解决方案. 上下文切换 即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制.时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行