MySQL数据库锁类型
锁概念 : 当高并发访问同一个资源时,可能会导致数据不一致,需要一种机制将用户访问数据的顺序进行规范化,以保证数据库数据的一致性。锁就是其中的一种机制。
一个栗子 :以买火车票为例,火车票可面向广大消费者,每位用户都可以查询余票数量、购买火车票 ... 但最终购票成功的仅有一位用户,处于购票高峰期时会出现很多用户同时抢夺同一张票的现状,为了避免出现一张火车票被多个用户购买成功的情况,当第一位用户进入购票流程时,就将数据库锁定,让别的用户无法修改,只有当第一位用户购票成功或取消购票之后,才会解除数据库的锁定,此时别的用户就可继续进行操作。这样就保证了一张火车票只能被一个用户购买。
悲观锁: 一般代指数据库锁机制,类似于我们在多线程资源竞争时添加的互斥锁,较容易出现死锁现象。它对于数据被外界修改持保守态度,认为数据随时会修改,所以整个数据处理中需要将数据加锁。悲观锁一般都是依靠关系数据库提供的锁机制实现。
悲观锁按使用性质划分:
数据库的操作可归纳为两种:读和写。当多个事务同时读取一个对象时,不会产生有冲突。但同时读和写,或者同时写会产生冲突。因此为提高数据库的并发性能,定义三种锁
共享锁(Share locks简记为S锁):也称读锁,事务A给对象T加S锁,其他事务也只能对T加S,多个事务可以同时读,但不能有写操作,直到A释放S锁。
地球语言 : 仅对数据进行读操作,因此多个事务可以同时为一个对象添加共享锁。(火车票人人都可随时查询)。
产生共享锁: select * from ad_plan lock in share mode;
排它锁(Exclusivelocks简记为X锁):也称写锁,事务A给对象T加X锁以后,其他事务不能对T加任何锁,只有事务A可以读写对象T,直到A释放X锁。
地球语言: 对数据仅需写/读写操作,只有一个事务可以为当前对象添加排他锁,其余事务不可再为其上锁。(一个用户已经进入购票流程,其余客户不能再购票)
产生排他锁: select * from ad_plan for update;
更新锁(简记为U锁):用来预定要对此对象施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的对象将要被更新时,则升级为X锁,主要是用来防止死锁的。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排它锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个对象申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就可以避免死锁。
select * from information_schema.innodb_locks; 可以查看锁。
悲观锁按作用范围划分:
行锁:锁的作用范围是行级别,数据库能够确定那些行需要锁的情况下使用行锁,如果不知道会影响哪些行的时候就会使用表锁。举个栗子,现有一张学生表student,有主键id和学生名字name,假设现在需要使用 update ... where id=xxx 语句修改数据库数据,因为主键字段id在创建时已经默认建立了索引,所以数据库能够明确知道你需要修改的是哪一条记录,此时仅会使用行锁。但当使用 update ... where name=xxx 语句修改数据库数据时,数据库实现并不知道会影响哪些行,此时可能会使用表锁。
表锁:表锁的作用范围是整张表。
数据库死锁:通常如果需要“修改”一条数据,数据库管理系统会先在上面加锁,以保证在同一时间只有一个事务能进行修改操作。锁定(Locking)发生在一个事务获取到某一资源的“锁”时,其他的事务就不能更改这个资源了,这种机制的存在是为了保证数据一致性。多数情况下,可以认为如果一个资源被锁定,它总会在以后某个时间被释放。而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。简单的说,进程A等待进程B释放他的资源,B又等待A释放他的资源,这样就互相等待就形成死锁。
死锁产生条件:
- 按同一顺序访问对象:如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得 Supplier 表上的锁,然后获得 Part 表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在 Supplier 表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序。
- 避免事务中的用户交互:避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成。
- 保持事务简短并在一个批处理中:在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。
- 使用低隔离级别:确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺。
- 使用绑定连接:使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞。
数据库具体实现:
- 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务;
- 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;
- 所有的SP都要有错误处理(通过@error)
- 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁
- 优化程序,检查并避免死锁现象出现;
- 合理安排表访问顺序
- 在事务中尽量避免用户干预,尽量使一个事务处理的任务少些。
- 采用脏读技术。脏读由于不对被访问的表加锁,而避免了锁冲突。在客户机/服务器应用环境中,有些事务往往不允许读脏数据,但在特定的条件下,我们可以用脏读。
- 数据访问时域离散法。数据访问时域离散法是指在客户机/服务器结构中,采取各种控制手段控制对数据库或数据库中的对象访问时间段。主要通过以下方式实现: 合理安排后台事务的执行时间,采用工作流对后台事务进行统一管理。工作流在管理任务时,一方面限制同一类任务的线程数(往往限制为1个),防止资源过多占用; 另一方面合理安排不同任务执行时序、时间,尽量避免多个后台任务同时执行,另外,避免在前台交易高峰时间运行后台任务
- 数据存储空间离散法。数据存储空间离散法是指采取各种手段,将逻辑上在一个表中的数据分散到若干离散的空间上去,以便改善对表的访问性能。主要通过以下方法实现: 第一,将大表按行或列分解为若干小表; 第二,按不同的用户群分解。
- 使用尽可能低的隔离性级别。隔离性级别是指为保证数据库数据的完整性和一致性而使多用户事务隔离的程度,SQL92定义了4种隔离性级别:未提交读、提交读、可重复读和可串行。如果选择过高的隔离性级别,如可串行,虽然系统可以因实现更好隔离性而更大程度上保证数据的完整性和一致性,但各事务间冲突而死锁的机会大大增加,大大影响了系统性能。
- 使用Bound Connections。Bound connections 允许两个或多个事务连接共享事务和锁,而且任何一个事务连接要申请锁如同另外一个事务要申请锁一样,因此可以允许这些事务共享数据而不会有加锁的冲突。
- 考虑使用乐观锁定或使事务首先获得一个独占锁定。
乐观锁:一般是指用户自己实现的一种锁机制,并不是真实存在的锁。它对于数据被外界修改持乐观态度,认为数据不会修改,所以数据处理时数据库不再为其加锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。可以在数据表中添加一个冗余字段,比如时间戳,在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。比如更新数据时,拿着之前相同的查询条件再一次查询数据库,若仍能够得到数据证明此条记录无人修改,即可继续操作,否则表示当前有用户正在抢夺资源,就放弃更新操作。
乐观锁实现方式:
a. 版本号(记为version):在表中新增一个version字段,作为版本标识的记号,当数据每次更新时就将此字段加1,每次读取数据时一并将version字段读出,更新数据之前比较version字段值。举个栗子,若此次读取的 新version值 比 旧version值 大,说明有其他事务在此之前修改过这条记录,并为版本号字段增加了数量,此时就无法得到这条记录,需要重新开始一遍。此字段存在的意义是作为一个标志位,准备修改数据时将version读出,真正修改数据前再查询一次version,比较上一次得到的version值和现在version是否一致,相同继续操作,不同重新开始。可使用类似 update … where … and version=”old version” 语句进行比较。根据返回结果是否为0执行下一步的操作。
b. 时间戳(timestamp):和版本号基本一样,只是通过时间戳来判断而已,注意时间戳要使用数据库服务器的时间戳,而不能是业务系统的时间。
c. 待更新字段:和版本号方式相似,只是不增加额外字段,直接使用表中现有做版本控制信息的标志位,因为有时我们可能无法改变旧系统的数据库表结构。假设现在需要保存一个订单记录,有库存stock字段: 首先需要查询数据库,得到这个商品的库存数量,再判断库存数量是否大于用户购买数量,经历一系列判断逻辑都能够通过的话,保存这个订单数据之前,需要拿着当初查询数据库时的库存字段再查询一次这个商品,若通过原始库存值能够得到商品对象,那么就进行订单的修改操作,否则就是别的用户正在抢夺资源,应放弃操作重新再来。
d. 所有字段:和待更新字段类似,只是使用所有字段做版本控制信息,只有所有字段都没有变化才会执行更新。
锁的级别:页级、表级、行级。
MySQL的锁机制比较简单,最显著的特点是不同的存储引擎支持不同的锁机制。
MyISAM & MEMORY: 表级锁(table-level locking), BDB: 页面锁(page-level locking)&表级锁, InnoDB: 行级锁(row-level locking)&表级锁,默认采用行级锁。
3种锁特性:
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。对整个表加锁,影响标准的所有记录。通常用在DDL语句中,如DELETE TABLE,ALTER TABLE等。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。对一行记录加锁,只影响一条记录。通常用在DML语句中,如INSERT, UPDATE, DELETE等。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
数据库引擎通常必须获取多级别上的锁才能完整地保护资源。
锁的应用场景:
乐观锁适用于高并发、读多写少的场景,发生冲突时能够减少开销.
如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁
如果冲突频率非常高,建议采用悲观锁保证成功率,如果冲突频率大,乐观锁会需要多次重试才能成功,代价比较大
如果重试代价大,建议采用悲观锁
锁的优缺点:
乐观锁不会发生死锁情况,不会过多占用系统资源,无法阻止除数据库之外的操作
悲观锁写入数据时能够确保数据的准确性
MySQL数据库锁类型的更多相关文章
- MYSQL数据库索引类型及使用
MYSQL数据库索引类型包括普通索引,唯一索引,主键索引与组合索引,这里对这些索引的做一些简单描述: (1)普通索引 这是最基本的MySQL数据库索引,它没有任何限制.它有以下几种创建方式: 创建索引 ...
- MYSQL数据库约束类型
07.14自我总结 MYSQL数据库约束类型 一.主键约束(primary key) 主键约束要求主键列的数据唯一,并且不能为空.主键分为两种类型:单字段主键和多字段联合主键. 1.单字段主键 写法 ...
- Mysql数据库日志类型查询与配置详解
在mysql中日志分为很多种,下面小编来给大家介绍Mysql数据库日志类型查询与使用,希望对各位同学会有所帮助 mysql常见的日志类型有五种:错误日志.二进制日志.查询日志.慢查日志和中继日志. 一 ...
- mysql数据库索引类型和原理
索引初识: 最普通的情况,是为出现在where子句的字段建一个索引.为方便讲述,我们先建立一个如下的表. CREATE TABLE mytable ( id serial primary key, c ...
- mysql数据库查找类型不匹配
无意中看到10级学长的博客,提到了mysql数据库类型查找不匹配的问题,博客地址是:卢俊达 . 数据库中建表中会对每个属性进行类型划分,然后在查找数据库select时: MySQL 的文档 (Type ...
- mysql数据库锁的机制-one
锁概念 : 当高并发访问同一个资源时,可能会导致数据不一致,需要一种机制将用户访问数据的顺序进行规范化,以保证数据库数据的一致性.锁就是其中的一种机制. 举例 :以买火车票为例,火车票可面向广大消费者 ...
- MySQL数据库索引类型、MySQL索引的优化及MySQL索引案例
关于MySQL索引的好处,如果正确合理设计并且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车.对于没有索引的表,单表查询可能几十万数据就是瓶颈,而通常大型 ...
- mysql数据库事务类型
出自:https://blog.csdn.net/u014439239/article/details/78086729 数据库事务有不同的隔离级别,不同的隔离级别对锁的使用是不同的,锁的应用最终导致 ...
- oracle数据库date类型和mysql数据库datetime类型匹配
oracle数据库有date类型,但是没有datetime类型 mysql数据库既有date类型也有datetime类型. Oracle数据库的date类型和mysql的date类型是不一样的,Ora ...
随机推荐
- PackageManager整理
一.PackageManager的功能 1.安装.卸载应用.2.查询permission相关信息.3.查询Application相关信息(application,activity,receiver,s ...
- TX-LCN分布式事务Demo实战
1. TX-LCN分布式事务Demo实战 1.1. 原理介绍 1.1.1. 事务控制原理 TX-LCN由两大模块组成, TxClient.TxManager,TxClient作为模块的依赖框架,提供T ...
- Oracle SQL调优记录
目录 一.前言 二.注意点 三.Oracle执行计划 四.调优记录 @ 一.前言 本博客只记录工作中的一次oracle sql调优记录,因为数据量过多导致的查询缓慢,一方面是因为业务太过繁杂,关联了太 ...
- Xamarin.Android 嵌入web端界面
在程序中嵌入Web端界面. 首先在前台界面上创建一个webview <android.webkit.WebView android:layout_width="match_parent ...
- Java核心技术及面试指南 IO部分的面试题归纳以及答案
4.6.1 java中有几种类型的流? Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStream,OutputStr ...
- SkyWalking-netcore
详细安装步骤:https://www.jianshu.com/p/3ddd986c7581?from=groupmessage SkyWalking-netcore 官网:https://github ...
- python 分享一个通过 (key1.key2.key3) 形式获取嵌套字典值的方法
最近在做接口自动化测试,响应的内容大多数是多层嵌套的json数据,如果一层层的去剥,效率不高,脚本繁重,所以写了一个可以通过(key1.key2.key3)形式获取嵌套字典值的方法,如有不对或者需要优 ...
- Ioc及Bean容器(三)
专题一 IoC 接口及面向接口编程 什么是 IoC Spring 的Bean配置 Bean 的初始化 Spring 的常用注入方式 接口 用于沟通的中介物的抽象化 实体把自己提供给外界的一种抽象化说明 ...
- Python快速学习02:基本数据类型 & 序列
前言 系列文章:[传送门] 也就每点一点点的开始咯,“还有两年时间,两年可以学很多东西的” Python ['paɪθən] n. 巨蛇,大蟒 基本数据类型 变量不需要声明 a=10 # int 整 ...
- 【EF6学习笔记】(八)更新关联数据
上一篇链接:EF学习笔记(七):读取关联数据 本篇原文链接:Updating Related Data 本篇主要考虑对于有关联的数据进行新增.删除.更新操作:比如Course .Instructor: ...