精华内容
下载资源
问答
  • 数据库乐观锁和悲观锁 mysql中 for update 使用 MySQL 共享锁 (lock in share mode),排他锁 (for update)

    一分钟教你知道乐观锁和悲观锁的区别

    数据库乐观锁和悲观锁

    mysql中 for update 使用

    MySQL 共享锁 (lock in share mode),排他锁 (for update)

    悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会一直block阻塞直到它拿到锁。
    传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
    
    乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁。
    但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
    乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
    
    两种锁各有优缺点,不可认为一种好于另一种,
    像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。
    但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
    
    展开全文
  • 作者:黄青石链接:...数据锁分为乐观锁和悲观锁它们使用的场景如下:乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。...

    点击上方“Java知音”,选择“置顶公众号”

    技术文章第一时间送达!

    作者:黄青石

    链接:www.cnblogs.com/huangqingshi

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

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

    它们使用的场景如下:

    • 乐观锁适用于写少读多的情景,因为这种乐观锁相当于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的依赖如下:

    xml version="1.0" encoding="UTF-8"?>

    xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       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,非常方便,有兴趣的同学可以自行研究。

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

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

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

    public interface CatalogRepository extends CrudRepository<Catalog, Long> {

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

       Optional<Catalog> findCatalogsForUpdate(@Param("id") Long id);

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

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

       Optional<Catalog> 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);

    }

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

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

    2. 使用@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<String, String> 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<String, String> 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;

       }

    }

    大致思路是这样了,示例中的代码:

    https://github.com/stonehqs/dblock


    更多Java技术文章,尽在【Java知音】网站。

    网址:www.javazhiyin.com  ,搜索Java知音可达!

    看完本文有收获?请转发分享给更多人

    18b3f67e6954a9df63ad88103ae719dd.png

    展开全文
  • 点击上方Coding这件小事,选择星标公众号重磅资讯、干货,第一时间送达作者:黄...数据锁分为乐观锁和悲观锁它们使用的场景如下:乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过...

    点击上方 Coding这件小事 ,选择 星标 公众号

    重磅资讯、干货,第一时间送达623a1c8c2a24873a9f6aea35d35dc89e.png

    作者:黄青石

    链接:www.cnblogs.com/huangqingshi

    文章整理自:Java知音

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

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

    它们使用的场景如下:

    • 乐观锁适用于写少读多的情景,因为这种乐观锁相当于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的依赖如下:

    xml version="1.0" encoding="UTF-8"?>

    xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       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,非常方便,有兴趣的同学可以自行研究。

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

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

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

    public interface CatalogRepository extends CrudRepository<Catalog, Long> {

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

       Optional<Catalog> findCatalogsForUpdate(@Param("id") Long id);

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

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

       Optional<Catalog> 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);

    }

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

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

    2. 使用@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<String, String> 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<String, String> 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;

       }

    }

    大致思路是这样了,示例中的代码:

    https://github.com/stonehqs/dblock

    3e2b19c5eb2d539f1c2163c628f3c7e9.png

    能点个「在看」再走嘛?

    展开全文
  • 乐观锁和悲观锁 适用场景:乐观锁适用于写操作少,读频繁的情况。悲观锁适用于写频繁,读少的情况。 悲观锁: 在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写...

    乐观锁和悲观锁

        适用场景:乐观锁适用于写操作少,读频繁的情况。悲观锁适用于写频繁,读少的情况。      

    悲观锁:

            在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。

    悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

    悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据).

    在数据库中,悲观锁的流程如下:

    在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。

    如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。

    如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

    其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常.

    MySQL InnoDB中使用悲观锁

    要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。 set autocommit=0;

    //0.开始事务

    begin;/begin work;/start transaction; (三者选一就可以)

    //1.查询出商品信息

    select status from t_goods where id=1 for update;

    //2.根据商品信息生成订单

    insert into t_orders (id,goods_id) values (null,1);

    //3.修改商品status为2

    update t_goods set status=2;

    //4.提交事务

    commit;/commit work;

    上面的查询语句中,我们使用了 select…for update 的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

    上面我们提到,使用 select…for update 会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。

    优点与不足

    悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数

    2乐观锁

    相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。

    数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。

    使用版本号实现乐观锁

    使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。

    1.查询出商品信息

    select (status,status,version) from t_goods where id=#{id}

    2.根据商品信息生成订单

    3.修改商品status为2

    update t_goods

    set status=2,version=version+1

    where id=#{id} and version=#{version};

    优点与不足

    乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。

    Mysql中有哪几种锁?

    1.表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

    2.行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

    3. 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

    展开全文
  • 在写入数据库的时候需要有锁,比如同时写入数据库的时候会出现丢数据,那么就需要锁机制。数据锁分为乐观锁和悲观锁它们使用的场景如下:乐观锁适用于写少读多的情景,因为这种乐观锁...
  • 数据锁分为乐观锁和悲观锁它们使用的场景如下:乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。悲观锁适用于写多读少的情景,这种情况也...
  • 数据锁分为乐观锁和悲观锁,那么它们使用的场景如下:1. 乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。2. 悲观锁适用于写多读少的情景,...
  • 在关系数据库管理系统里,乐观并发控制(又名”乐观锁”,Optimistic Concurrency Control,缩写”OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下...
  • 1.乐观锁:乐观地认为每次读数据别人都不会修改,所以不需要用数据库锁 //查出商店中id=1的商品的版本号 select version currentVersion from shop where id=1; //把id=1的商品库存减1,并把版本号自增1 update shop...
  • 数据锁分为乐观锁和悲观锁,那么它们使用的场景如下: 1. 乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。 2. 悲观锁适用于写多读少的情景...
  • 悲观锁 悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会 修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block 直到它拿到锁。 悲观锁:假定会发生并发冲突,...
  • 恼骚最近在搞并发的问题,订单的异步通知主动查询会存在并发的问题,用到了Mysql数据库的for update 在TP5直接通过lock(true),用于数据库机制Db::name('pay_order')->where('order_no',‘S...
  • 浅谈实现数据库乐观锁和悲观锁

    千次阅读 2018-10-11 09:42:21
    但是在分布式环境中,上述方法却不能在跨jvm场景中用于处理并发问题,当业务场景需要对分布式环境中的并发问题进行处理时,需要使用其他方式来实现,如数据库锁机制、缓存数据库如redis以及zookeeper分布式等。...
  • 悲观锁(Pessimistic Locking):   悲观锁,正如其名,它... 悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能 真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无...
  • 数据库乐观锁和悲观锁的理解和实现(转载&总结)

    万次阅读 热门讨论 2018-05-11 10:36:08
    理解:1. 乐观锁是一种思想,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。...这种是数据库乐观锁优点程序实现,不会存在死锁等问题。他的适用场景也相对乐观。阻止不了除了程序之外...
  • 悲观锁相对比较谨慎, 设想现实情况应该很容易就发生冲突,所以我还是独占数据资源吧。 乐观锁就想得开而且非常聪明,应该是不会有什么冲突的,我对表使用一个时间戳或者版本号,每次读、更新操作都对这个字段进行...
  • 数据锁分为乐观锁和悲观锁它们使用的场景如下:乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。悲观锁适用于写多读少的情景,这种情况也...
  • 今天去面试了,面试官问了个问题,高并发情况下,怎么防止库存超卖? 回去之后搜了一下解决方法,其中的一...数据的锁定分为两种,第一种叫作悲观锁,第二种叫作乐观锁。 1、悲观锁,就是对数据的冲突采取一种悲...

空空如也

空空如也

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

数据库乐观锁和悲观锁