精华内容
下载资源
问答
  • 数据库乐观锁悲观锁 mysql中 for update 使用 MySQL 共享锁 (lock in share mode),排他锁 (for update)
    展开全文
  • 数据库乐观锁悲观锁 演示案例 为何需要乐观锁,与悲观锁这样的锁? id name money 1 god 1000 假设god同志的账上有1000元,现在有两个线程同时往他的账户上转钱。 1.A线程准备向god账户上转200,读取到...

    数据库乐观锁与悲观锁

    演示案例

    为何需要乐观锁,与悲观锁这样的锁?

    id name money
    1 god 1000

    假设god同志的账上有1000元,现在有两个线程同时往他的账户上转钱。

    1.A线程准备向god账户上转200,读取到账户上有1000元,事务还未提交

    2.B线程准备向god账户上转100,读取到账户上有1000元,事务也还未提交

    3.A线程提交了事务,god账户上变成了1200元,但是B线程此时不知道god账户上变成了1200

    4.B线程随即也提交了事务,god账户上变成了1200,少了100元

    因此,加锁的目的在于保障一个线程修改数据时,这个数据没有被其他的线程修改过

    悲观锁与乐观锁的区别

    1.在事务的基础上,悲观锁更适合短事务(长事务导致其他事务一直被阻塞,影响了系统性能),适用于查少改多

    2.在事务的基础上,乐观锁更适合长事务,他的本质并不是锁,而是通过代码实现的。适用于查多改少

    悲观锁的使用案例

    在查的时候,对数据进行锁定。

    在数据库中:for update

    在Django中:select_for_update()

    原生的sql:

    1 开启事务

    2 查询的时候加锁 —》 select * from user where id =1 for update

    3 结束事务锁被释放

    django中:

    1 开启事务

    2 在查询的时候 —》 user.objects.select_for_update().flilter(id=1).first()

    3 事务结束锁被释放

    乐观锁的使用案例

    乐观锁的本质不是锁。他是通过代码来实现锁的。

    方法:先拿到age数据,在修改的时候再次判断age是否一样。

    (代码实现)目的:将这个数据中的age在原来的基础上+1

    1 开启事务

    2 查询的时候不做任何操作。data = user.objects.flilter(id=1).first()

    3 在修在数据的时候。user.objects.filter(id=1,age=data.age).updata(age=data.age+1)

    从而在我查询到我修改的时候,没有人改动过

    4 如果3中的影响行数为0,证明数据被人修改。循环再次执行。如果为1,证明数据没人动过,修改成功

    ‘’’ 如果是可重复读,上面的乐观锁无效,必须改成read committed’’’

    展开全文
  • 数据库事物乐观锁悲观锁 数据库事物单个逻辑单元工作执行的一系列操作,就是一些sql语句,也可以是多条,一个update操作就是一个事物。 事物具有四种特性 1.原子性 事物必须是原子工作单元,要么都执行成功(也就是...

    数据库事物乐观锁和悲观锁 数据库事物单个逻辑单元工作执行的一系列操作,就是一些sql语句,也可以是多条,一个update操作就是一个事物。 事物具有四种特性 1.原子性 事物必须是原子工作单元,要么都执行成功(也就是说所有的sql语句都执行成功),要么都不

    数据库事物乐观锁和悲观锁

    数据库事物单个逻辑单元工作执行的一系列操作,就是一些sql语句,也可以是多条,一个update操作就是一个事物。

    事物具有四种特性

    1.原子性

    事物必须是原子工作单元,要么都执行成功(也就是说所有的sql语句都执行成功),要么都不执行(所有的sql语句都不执行)。

    www.2cto.com

    2. 一致性

    事务在完成时,必须使所有的数据都保持一致状态。

    3.隔离性

    允许多个用户对数据进行并发访问,而不破环数据的完整行和正确性,同时,并发事物的修改必须与其他并发事物隔离,一般是通过加锁实现。

    4.持久性

    事物完成后,数据必须永久的保存到数据库中。

    事物并发访问如果不设置事物的隔离级别,就会带来如下问题

    1.脏读 读取了其他事物未提交的数据,一个事物修改了某条数据还未提交,一个事物读取了这条数据,而修改的事物由于某种原因失败会滚了,这个时候读取事物就读到了脏数据。

    2.不可重复读

    一个事物两次读取某条数据,但读取的结果不一样,在第二次读取的时候其他实物修改了这条数据。

    www.2cto.com

    3.幻读

    一个事物两次读取,读取到了其他事物插入到数据中的数据。

    针对以上情况,数据库提供了四种事物的隔离级别来解决事物并发带来的问题。

    1.读未提交(read uncommited)

    写事物会阻塞写事物,但不会阻塞读取事物,因此不能解决读取脏数据,读取事物不会阻塞其他事物,这中隔离级别不能解决上面任何问题。

    2 读已经提交(read commited)

    写事物会阻塞写事物和读取事物,因此可以避免读取脏数据,但读取事物不会阻塞写事物,不能解决可重复度去的问题。

    3 可重复读(Repeatable read)

    读事物会阻塞写事物和读事物,因此可以重复读,但其他事物可以进行插入操作,不能解决幻读的问题。

    4 序列化(Serializable)

    事物必须一个一个的执行 ,可以解决上面的问题,但事物基本没有并发性。

    事物的并发控制

    当多个人并发修改同一条数据时,必须实现一个控制系统,使一个人的修改不会对其他人的修改造成负面影响。

    乐观锁和悲观锁控制并发

    1.乐观锁

    乐观的认为其他用户企图访问和更改你正在访问的对象的概率很低,即使有,大不了从来一次,对在做一次的开销不是很大的情况下,如果开销很大则必须使用悲观锁.乐观锁的实现需要在程序中控制,可以通过加一个数据版本号来控制比如两个事物都读取了同一条记录要进行更新

    Sql代码

    select * from person

    select * from person

    update person set name='xiaoming',version=version+1 where id='1'

    and version=0;

    这个更新语句会执行失败,因为找不到版本好为0的数据,第一个事物已经更新了

    pdate person set name='xiaoming',version=version+1 where id='1'

    and version=0;

    www.2cto.com

    如果实物的隔离级别设置为读已提交,使用乐观锁并能解决可重复度,系统要允许不可重复读取。

    悲观锁

    很悲观的认为其他用户访问和更改你正在访问或修改的对象的概率和高,悲观锁的实现是通过加锁来实现的,当要更改数据前就加锁,别的事物不能操作,sql语句如下

    Sql代码

    select * from person for update

    update person set name='ff' where id='1'

    直到上面事物提交,才能释放锁,其他事物才能操作。悲观锁的并发性会降低。因此大多数情况下,使用乐观锁来实现并发修改。

    本文原创发布php中文网,转载请注明出处,感谢您的尊重!

    展开全文
  • 数据锁分为乐观锁悲观锁它们使用的场景如下:乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。悲观锁适用于写多读少的情景,这种情况也...

    在写入数据库的时候需要有锁,比如同时写入数据库的时候会出现丢数据,那么就需要锁机制。

    数据锁分为乐观锁和悲观锁

    它们使用的场景如下:

    乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。

    悲观锁适用于写多读少的情景,这种情况也相当于JAVA的synchronized,reentrantLock等,大量数据过来的时候,只有一条数据可以被写入,其他的数据需要等待。执行完成后下一条数据可以继续。

    他们实现的方式上有所不同。

    乐观锁采用版本号的方式,即当前版本号如果对应上了就可以写入数据,如果判断当前版本号不一致,那么就不会更新成功,

    比如

    update table set column = value

    where version=${version} and otherKey = ${otherKey}

    悲观锁实现的机制一般是在执行更新语句的时候采用for update方式,

    比如

    update table set column='value' for update

    这种情况where条件呢一定要涉及到数据库对应的索引字段,这样才会是行级锁,否则会是表锁,这样执行速度会变慢。

    下面我就弄一个spring boot(springboot 2.1.1 + mysql + lombok + aop + jpa)工程,然后逐渐的实现乐观锁和悲观锁。

    假设有一个场景,有一个catalog商品目录表,然后还有一个browse浏览表,假如一个商品被浏览了,那么就需要记录下浏览的user是谁,并且记录访问的总数。

    表的结构非常简单:

    create table catalog (

    id int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',

    name varchar(50) NOT NULL DEFAULT '' COMMENT '商品名称',

    browse_count int(11) NOT NULL DEFAULT 0 COMMENT '浏览数',

    version int(11) NOT NULL DEFAULT 0 COMMENT '乐观锁,版本号',

    PRIMARY KEY(id)

    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

    CREATE table browse (

    id int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',

    cata_id int(11) NOT NULL COMMENT '商品ID',

    user varchar(50) NOT NULL DEFAULT '' COMMENT '',

    create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY(id)

    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

    POM.XML的依赖如下:

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    4.0.0

    org.springframework.boot

    spring-boot-starter-parent

    2.1.1.RELEASE

    com.hqs

    dblock

    1.0-SNAPSHOT

    dblock

    Demo project for Spring Boot

    1.8

    org.springframework.boot

    spring-boot-starter-web

    org.springframework.boot

    spring-boot-devtools

    runtime

    mysql

    mysql-connector-java

    runtime

    org.springframework.boot

    spring-boot-starter-test

    test

    org.springframework.boot

    spring-boot-starter-data-jpa

    mysql

    mysql-connector-java

    org.projectlombok

    lombok

    true

    org.aspectj

    aspectjweaver

    1.8.4

    org.springframework.boot

    spring-boot-maven-plugin

    项目的结构如下:

    介绍一下项目的结构的内容:

    entity包:实体类包。

    repository包:数据库repository

    service包:提供服务的service

    controller包: 控制器写入用于编写requestMapping。相关请求的入口类

    annotation包:自定义注解,用于重试。

    aspect包:用于对自定义注解进行切面。

    DblockApplication:springboot的启动类。

    DblockApplicationTests:测试类。

    咱们看一下核心代码的实现,参考如下,使用dataJpa非常方便,集成了CrudRepository就可以实现简单的CRUD,非常方便,有兴趣的同学可以自行研究。

    实现乐观锁的方式有两种:

    更新的时候将version字段传过来,然后更新的时候就可以进行version判断,如果version可以匹配上,那么就可以更新(方法:updateCatalogWithVersion)。

    在实体类上的version字段上加入version,可以不用自己写SQL语句就可以它就可以自行的按照version匹配和更新,是不是很简单。

    public interface CatalogRepository extends CrudRepository {

    @Query(value = "select * from Catalog a where a.id = :id for update", nativeQuery = true)

    Optional findCatalogsForUpdate(@Param("id") Long id);

    @Lock(value = LockModeType.PESSIMISTIC_WRITE) //代表行级锁

    @Query("select a from Catalog a where a.id = :id")

    Optional findCatalogWithPessimisticLock(@Param("id") Long id);

    @Modifying(clearAutomatically = true) //修改时需要带上

    @Query(value = "update Catalog set browse_count = :browseCount, version = version + 1 where id = :id " +

    "and version = :version", nativeQuery = true)

    int updateCatalogWithVersion(@Param("id") Long id, @Param("browseCount") Long browseCount, @Param("version") Long version);

    }

    实现悲观锁的时候也有两种方式:

    自行写原生SQL,然后写上for update语句。(方法:findCatalogsForUpdate)

    使用@Lock注解,并且设置值为LockModeType.PESSIMISTIC_WRITE即可代表行级锁。

    还有我写的测试类,方便大家进行测试:

    package com.hqs.dblock;

    import org.junit.Test;

    import org.junit.runner.RunWith;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.boot.test.context.SpringBootTest;

    import org.springframework.boot.test.web.client.TestRestTemplate;

    import org.springframework.test.context.junit4.SpringRunner;

    import org.springframework.util.LinkedMultiValueMap;

    import org.springframework.util.MultiValueMap;

    @RunWith(SpringRunner.class)

    @SpringBootTest(classes = DblockApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

    public class DblockApplicationTests {

    @Autowired

    private TestRestTemplate testRestTemplate;

    @Test

    public void browseCatalogTest() {

    String url = "http://localhost:8888/catalog";

    for(int i = 0; i < 100; i++) {

    final int num = i;

    new Thread(() -> {

    MultiValueMap params = new LinkedMultiValueMap<>();

    params.add("catalogId", "1");

    params.add("user", "user" + num);

    String result = testRestTemplate.postForObject(url, params, String.class);

    System.out.println("-------------" + result);

    }

    ).start();

    }

    }

    @Test

    public void browseCatalogTestRetry() {

    String url = "http://localhost:8888/catalogRetry";

    for(int i = 0; i < 100; i++) {

    final int num = i;

    new Thread(() -> {

    MultiValueMap params = new LinkedMultiValueMap<>();

    params.add("catalogId", "1");

    params.add("user", "user" + num);

    String result = testRestTemplate.postForObject(url, params, String.class);

    System.out.println("-------------" + result);

    }

    ).start();

    }

    }

    }

    调用100次,即一个商品可以浏览一百次,采用悲观锁,catalog表的数据都是100,并且browse表也是100条记录。采用乐观锁的时候,因为版本号的匹配关系,那么会有一些记录丢失,但是这两个表的数据是可以对应上的。

    乐观锁失败后会抛出ObjectOptimisticLockingFailureException,那么我们就针对这块考虑一下重试,下面我就自定义了一个注解,用于做切面。

    package com.hqs.dblock.annotation;

    import java.lang.annotation.ElementType;

    import java.lang.annotation.Retention;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.Target;

    @Target(ElementType.METHOD)

    @Retention(RetentionPolicy.RUNTIME)

    public @interface RetryOnFailure {

    }

    针对注解进行切面,见如下代码。我设置了最大重试次数5,然后超过5次后就不再重试。

    package com.hqs.dblock.aspect;

    import lombok.extern.slf4j.Slf4j;

    import org.aspectj.lang.ProceedingJoinPoint;

    import org.aspectj.lang.annotation.Around;

    import org.aspectj.lang.annotation.Aspect;

    import org.aspectj.lang.annotation.Pointcut;

    import org.hibernate.StaleObjectStateException;

    import org.springframework.orm.ObjectOptimisticLockingFailureException;

    import org.springframework.stereotype.Component;

    @Slf4j

    @Aspect

    @Component

    public class RetryAspect {

    public static final int MAX_RETRY_TIMES = 5;//max retry times

    @Pointcut("@annotation(com.hqs.dblock.annotation.RetryOnFailure)") //self-defined pointcount for RetryOnFailure

    public void retryOnFailure(){}

    @Around("retryOnFailure()") //around can be execute before and after the point

    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {

    int attempts = 0;

    do {

    attempts++;

    try {

    pjp.proceed();

    } catch (Exception e) {

    if(e instanceof ObjectOptimisticLockingFailureException ||

    e instanceof StaleObjectStateException) {

    log.info("retrying....times:{}", attempts);

    if(attempts > MAX_RETRY_TIMES) {

    log.info("retry excceed the max times..");

    throw e;

    }

    }

    }

    } while (attempts < MAX_RETRY_TIMES);

    return null;

    }

    }

    展开全文
  • 在写入数据库的时候需要有锁,比如同时写入数据库的时候会出现丢数据,那么就需要锁机制。数据锁分为乐观锁悲观锁它们使用的场景如下:乐观锁适用于写少读多的情景,因为这种乐观锁...
  • 在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。 悲观并发控制主要用于数据争...
  • 防止多个用户在操作数据的时候,出现和预期数据不一样的现象,产生脏数据,在数据库的层面如果没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题,所以对于并发场景机制的应用是非常有效的,保证了数据的...
  • 数据库乐观锁的实现: 在数据库中增加一个version字段。读数据时,将该version读出,更新数据库数据,在update语句的条件中,增加判断条件,只有当前的版本号等于数据库的版本号才去更新数据库,并且在更新时,将...
  • 悲观锁就是利用数据库机制实现,一般利先通过for update的方式进行加锁,然后再进行修改。这就是比较典型的悲观锁策略。 乐观锁实现方式有一种比较典型的就是CAS(Compare and Swap)。乐观锁一般在where条件中限制。...
  • 数据锁分为乐观锁悲观锁,那么它们使用的场景如下:1. 乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。2. 悲观锁适用于写多读少的情景,...
  • 数据锁分为乐观锁悲观锁,那么它们使用的场景如下: 1. 乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。 2. 悲观锁适用于写多读少的情景...
  • 乐观锁 总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。 ...
  • 在关系数据库管理系统里,乐观并发控制(又名”乐观锁”,Optimistic Concurrency Control,缩写”OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下...
  • 恼骚最近在搞并发的问题,订单的异步通知和主动查询会存在并发的问题,用到了Mysql数据库的for update 在TP5直接通过lock(true),用于数据库机制Db::name('pay_order')->where('order_no',‘S...
  • 防止多个用户在操作数据的时候,出现和预期数据不一样的现象,产生脏数据,在数据库的层面如果没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题,所以对于并发场景机制的应用是非常有效的,保证了数据的...
  • 悲观锁 悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会 修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block 直到它拿到锁。 悲观锁:假定会发生并发冲突,...
  • 悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。   以常用的mysql InnoDB存储...
  • 1.乐观锁:乐观地认为每次读数据别人都不会修改,所以不需要用数据库锁 //查出商店中id=1的商品的版本号 select version currentVersion from shop where id=1; //把id=1的商品库存减1,并把版本号自增1 update shop...
  • 防止多个用户在操作数据的时候,出现和预期数据不一样的现象,产生脏数据,在数据库的层面如果没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题,所以对于并发场景机制的应用是非常有效的,保证了数据的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,815
精华内容 1,926
关键字:

数据库乐观锁悲观锁