mysql 悲观锁与乐观锁的理解及应用分析

本文实例讲述了mysql 悲观锁与乐观锁。分享给大家供大家参考,具体如下:

悲观锁与乐观锁是人们定义出来的概念,你可以理解为一种思想,是处理并发资源的常用手段。

不要把他们与mysql中提供的锁机制(表锁,行锁,排他锁,共享锁)混为一谈。

一、悲观锁

顾名思义,就是对于数据的处理持悲观态度,总认为会发生并发冲突,获取和修改数据时,别人会修改数据。所以在整个数据处理过程中,需要将数据锁定。

悲观锁的实现,通常依靠数据库提供的锁机制实现,比如mysql的排他锁,select .... for update来实现悲观锁。

例子:商品秒杀过程中,库存数量的减少,避免出现超卖的情况。

CREATE TABLE `tb_goods_stock` (
 `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
 `goods_id` bigint(20) unsigned DEFAULT '0' COMMENT '商品ID',
 `nums` int(11) unsigned DEFAULT '0' COMMENT '商品库存数量',
 `create_time` datetime DEFAULT NULL COMMENT '创建时间',
 `modify_time` datetime DEFAULT NULL COMMENT '更新时间',
 PRIMARY KEY (`id`),
 UNIQUE KEY `goods_id` (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品库存表';

将商品库存数量nums字段类型设为unsigned,保证在数据库层面不会发生负数的情况。

注意,使用悲观锁,需要关闭mysql的自动提交功能,将 set autocommit = 0;

注意,mysql中的行级锁是基于索引的,如果sql没有走索引,那将使用表级锁把整张表锁住。

1、开启事务,查询要卖的商品,并对该记录加锁。

begin;
select nums from tb_goods_stock where goods_id = {$goods_id} for update;

2、判断商品数量是否大于购买数量。如果不满足,就回滚事务。

3、如果满足条件,则减少库存,并提交事务。

update tb_goods_stock set nums = nums - {$num}
where goods_id = {$goods_id} and nums >= {$num};
commit;

事务提交时会释放事务过程中的锁。

悲观锁在并发控制上采取的是先上锁然后再处理数据的保守策略,虽然保证了数据处理的安全性,但也降低了效率。

二、乐观锁

顾名思义,就是对数据的处理持乐观态度,乐观的认为数据一般情况下不会发生冲突,只有提交数据更新时,才会对数据是否冲突进行检测。

如果发现冲突了,则返回错误信息给用户,让用户自已决定如何操作。

乐观锁的实现不依靠数据库提供的锁机制,需要我们自已实现,实现方式一般是记录数据版本,一种是通过版本号,一种是通过时间戳。

给表加一个版本号或时间戳的字段,读取数据时,将版本号一同读出,数据更新时,将版本号加1。

当我们提交数据更新时,判断当前的版本号与第一次读取出来的版本号是否相等。如果相等,则予以更新,否则认为数据过期,拒绝更新,让用户重新操作。

CREATE TABLE `tb_goods_stock` (
 `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
 `goods_id` bigint(20) unsigned DEFAULT '0' COMMENT '商品ID',
 `nums` int(11) unsigned DEFAULT '0' COMMENT '商品库存数量',
 `create_time` datetime DEFAULT NULL COMMENT '创建时间',
 `modify_time` datetime DEFAULT NULL COMMENT '更新时间',
 `version` bigint(20) unsigned DEFAULT '0' COMMENT '版本号',
 PRIMARY KEY (`id`),
 UNIQUE KEY `goods_id` (`goods_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='商品库存表';

1、查询要卖的商品,并获取版本号。

begin;
select nums, version from tb_goods_stock where goods_id = {$goods_id};

2、判断商品数量是否大于购买数量。如果不满足,就回滚事务。

3、如果满足条件,则减少库存。(更新时判断当前version与第1步中获取的version是否相同)

update tb_goods_stock set nums = nums - {$num}, version = version + 1
where goods_id = {$goods_id}
and version = {$version} and nums >= {$num};

4、判断更新操作是否成功执行,如果成功,则提交,否则就回滚。

乐观锁是基于程序实现的,所以不存在死锁的情况,适用于读多的应用场景。如果经常发生冲突,上层应用不断的让用户进行重新操作,这反而降低了性能,这种情况下悲观锁就比较适用。

更多关于MySQL相关内容感兴趣的读者可查看本站专题:《MySQL数据库锁相关技巧汇总》、《MySQL存储过程技巧大全》、《MySQL常用函数大汇总》、《MySQL日志操作技巧大全》及《MySQL事务操作技巧汇总》

希望本文所述对大家MySQL数据库计有所帮助。

(0)

相关推荐

  • Mysql数据库锁定机制详细介绍

    前言 为了保证数据的一致完整性,任何一个数据库都存在锁定机制.锁定机制的优劣直接应想到一个数据库系统的并发处理能力和性能,所以锁定机制的实现也就成为了各种数据库的核心技术之一.本章将对MySQL中两种使用最为频繁的存储引擎MyISAM和Innodb各自的锁定机制进行较为详细的分析. MySQL锁定机制简介 数据库锁定机制简单来说就是数据库为了保证数据的一致性而使各种共享资源在被并发访问访问变得有序所设计的一种规则.对于任何一种数据库来说都需要有相应的锁定机制,所以MySQL自然也不能例外.MyS

  • mysql锁表和解锁语句分享

    页级的典型代表引擎为BDB. 表级的典型代表引擎为MyISAM,MEMORY以及很久以前的ISAM. 行级的典型代表引擎为INNODB. -我们实际应用中用的最多的就是行锁. 行级锁的优点如下: 1).当很多连接分别进行不同的查询时减小LOCK状态. 2).如果出现异常,可以减少数据的丢失.因为一次可以只回滚一行或者几行少量的数据. 行级锁的缺点如下: 1).比页级锁和表级锁要占用更多的内存. 2).进行查询时比页级锁和表级锁需要的I/O要多,所以我们经常把行级锁用在写操作而不是读操作. 3).

  • mysql数据库锁的产生原因及解决办法

    数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性.加锁是实现数据库并 发控制的一个非常重要的技术.在实际应用中经常会遇到的与锁相关的异常情况,当两个事务需要一组有冲突的锁,而不能将事务继续下去的话,就会出现死锁,严 重影响应用的正常执行. 在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Lock

  • MySQL行级锁、表级锁、页级锁详细介绍

    页级:引擎 BDB.表级:引擎 MyISAM , 理解为锁住整个表,可以同时读,写不行行级:引擎 INNODB , 单独的一行记录加锁 表级,直接锁定整张表,在你锁定期间,其它进程无法对该表进行写操作.如果你是写锁,则其它进程则读也不允许行级,,仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作.页级,表级锁速度快,但冲突多,行级冲突少,但速度慢.所以取了折衷的页级,一次锁定相邻的一组记录. MySQL 5.1支持对MyISAM和MEMORY表进行表级锁定,对BDB表进行

  • MYSQL锁表问题的解决方法

    本文实例讲述了MYSQL锁表问题的解决方法.分享给大家供大家参考,具体如下: 很多时候!一不小心就锁表!这里讲解决锁表终极方法! 案例一 mysql>show processlist; 参看sql语句 一般少的话 mysql>kill thread_id; 就可以解决了 kill掉第一个锁表的进程, 依然没有改善. 既然不改善, 咱们就想办法将所有锁表的进程kill掉吧, 简单的脚本如下. #!/bin/bash mysql - u root - e " show processli

  • Mysql(MyISAM)的读写互斥锁问题的解决方法

    由于没办法在短期内增加读的服务器,所以采取对Mysql进行了一些配置,以牺牲数据实时性为代价,来换取所有服务器的生命安全.呵呵,具体相关调整以及思路如下: MyISAM在读操作占主导的情况下是很高效的.可一旦出现大量的读写并发,同InnoDB相比,MyISAM的效率就会直线下降,而且,MyISAM和 InnoDB的数据存储方式也有显著不同:通常,在MyISAM里,新数据会被附加到数据文件的结尾,可如果时常做一些UPDATE,DELETE操作之后,数据文件就不再是连续的,形象一点来说,就是数据文件

  • 一次Mysql死锁排查过程的全纪录

    前言 之前接触到的数据库死锁,都是批量更新时加锁顺序不一致而导致的死锁,但是上周却遇到了一个很难理解的死锁.借着这个机会又重新学习了一下mysql的死锁知识以及常见的死锁场景.在多方调研以及和同事们的讨论下终于发现了这个死锁问题的成因,收获颇多.虽然是后端程序员,我们不需要像DBA一样深入地去分析与锁相关的源码,但是如果我们能够掌握基本的死锁排查方法,对我们的日常开发还是大有裨益的. PS:本文不会介绍死锁的基本知识,mysql的加锁原理可以参考本文的参考资料提供的链接. 死锁起因 先介绍一下数

  • Mysql悲观锁和乐观锁的使用示例

    悲观锁 悲观锁,认为数据是悲观的.当我们查询数据的时候加上锁.防止其他线程篡改,直到对方拿到锁,才能修改. 比如,有如下的表.status=1表示可以下单,status=2表示不可以下订单.假如在并发的过程中有两个用户同时查到status=1,那么从逻辑上来说都可以去新增订单,但是会造成商品超卖. 如下例子 CREATE TABLE `goods` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL,

  • mysql 数据库死锁原因及解决办法

    死锁(Deadlock) 所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程.由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁. 一种情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源.例如,如果线程A锁住了记

  • mysql锁定单个表的方法

    mysql锁定单个表的方法 复制代码 代码如下: mysql>lock table userstat read; mysql>unlock tables; 页级的典型代表引擎为BDB. 表级的典型代表引擎为MyISAM,MEMORY以及很久以前的ISAM. 行级的典型代表引擎为INNODB. -我们实际应用中用的最多的就是行锁. 行级锁的优点如下: 1).当很多连接分别进行不同的查询时减小LOCK状态. 2).如果出现异常,可以减少数据的丢失.因为一次可以只回滚一行或者几行少量的数据. 行级锁

  • 实例讲解MySQL中乐观锁和悲观锁

    数据库管理系统中并发控制的任务是确保在多个事务同时存取数据库中同一数据不破坏事务的隔离性和统一性以及数据库的统一性 乐观锁和悲观锁式并发控制主要采用的技术手段 悲观锁 在关系数据库管理系统中,悲观并发控制(悲观锁,PCC)是一种并发控制的方法.它可以阻止一个事务以影响其他用户的方式来修改数据.如果一个事务执行的操作的每行数据应用了锁,那只有当这个事务锁释放,其他事务才能够执行与该锁冲突的操作 悲观并发控制主要应用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本环境

随机推荐