锁及事务

悲观锁 、乐观锁

悲观锁认为每次拿数据时都会被别人修改,因此悲观锁采用每次取数据时都对数据上锁的原则。别人读取这个数据(不是修改)时就会阻塞直到拿到锁。悲观锁很安全但会大大降低数据库的性能。

乐观锁认为每次拿数据时不会被人修改,一次乐观锁存取数据时不对数据上锁 ,但是更新的时候会使用版本号机制去判断数据有没有被别人更新过,更新过的话则返回上层应用进行retry。乐观锁可以提高多度少写类的应用的吞吐率。

版本号机制:一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

  

数据库锁机制

http://blog.csdn.net/samjustin1/article/details/52210125

http://www.crazyant.net/1763.html有了事务为什么还需要锁

锁分类

共享锁(S锁):多个事务可封锁一个共享页(用于不更新数据的读操作如select)

排他锁(X锁):仅允许一个事物封锁此页(用于更新数据的操作,update,delete)

更新锁(U锁):可以避免死锁。其实是S锁和X锁的结合。

意向锁:

多个共享锁可以同时存在于同一资源上,但同一资源上不能同时共存共享锁和排他锁。共享锁不阻止其他事物读取已锁定的资源,但阻止其他事物更新已锁定的资源。

锁的粒度

何时加锁

何时加锁,加什么锁可以通过hint手动强行指定,但多数时候是数据库自动加锁的。

锁与事物隔离级别的优先级

手工指定的锁优先

如何提高并发效率?

不论是数据库系统本身的锁机制,还是乐观锁这种业务数据级别上的锁机制,本质都是对状态位的读,写,判断。

(死锁的发生1)
----------------------------------------
T1:
begin tran
select * from table (holdlock) (holdlock意思是加共享锁,直到事物结束才释放)
update table set column1=‘hello‘

T2:
begin tran
select * from table(holdlock)
update table set column1=‘world‘

假设T1和T2同时达到select,T1对table加共享锁,T2也对加共享锁,当
T1的select执行完,准备执行update时,根据锁机制,T1的共享锁需要升
级到排他锁才能执行接下来的update.在升级排他锁前,必须等table上的
其它共享锁释放,但因为holdlock这样的共享锁只有等其所在的事务结束后才释放,
所以因为T2的共享锁不释放而导致T1等(等T2释放共享锁,自己好升级成排
他锁),同理,也因为T1的共享锁不释放而导致T2等。死锁产生了。
(死锁的发生2)
----------------------------------------
T1:
begin tran
update table set column1=‘hello‘ where id=10

T2:
begin tran
update table set column1=‘world‘ where id=20

这种语句虽然最为常见,很多人觉得它有机会产生死锁,但实际上要看情
况,如果id是主键上面有索引,那么T1会一下子找到该条记录(id=10的记
录),然后对该条记录加排他锁,T2,同样,一下子通过索引定位到记录,
然后对id=20的记录加排他锁,这样T1和T2各更新各的,互不影响。T2也不
需要等。

但如果id是普通的一列,没有索引。那么当T1对id=10这一行加排他锁后,
T2为了找到id=20,需要对全表扫描,那么就会预先对表加上共享锁或更新
锁或排他锁(依赖于数据库执行策略和方式,比如第一次执行和第二次执行
数据库执行策略就会不同)。但因为T1已经为一条记录加了排他锁,导致
T2的全表扫描进行不下去,就导致T2等待。

另一种解释:
Update过程中对表进行扫描依此对每行记录下U锁,若满足条件则转换为X锁更新,若不满足条件则释放U锁。

对于两个查询而言, 查询一的第一个更新扫描所有记录,扫描过程会对扫描的每一条记录下U锁, 如果满足更新条件,则转化为X锁更新;如果不满足更新条件,则释放U锁。更新一完成后,第一个更新的记录保持X锁(因为事务没有完成),查询一等待第二个更新操作

对于查询二的更新,与查询一的更新过程相同,如果更新的记录在查询一第一个更新的记录前,那么查询二所更新的记录也会持有X锁,但在扫描记录进行到查询一条一个更新的记录的时候,需要等待查询一完成(已经有X锁的记录无法下U锁),这个时候查询二被查询一Block
对于查询一, 第二个更新进行时,它也扫描所有记录,进行到更新二所在的记录的时候,它无法取得U锁(因为已经被查询二下了X锁), 这个时候查询一等待查询二完成。 在这种情况下,查询一和查询二就是互相等待了,符合死锁条件

如果查询二更新的记录在查询一第一个更新的记录之后,那么查询二的U扫描行到查询一第一次更新记录的时候,就会因为锁冲突导致无法进行下去,必须等待查询一完成, 这个时候查询二没有会导致查询二第一个更新无法进行的锁, 也就不会导致死锁了

另一种相似解释:

首先我运行事务一(Process Node1),我们看看它的代码:首先要进行这一步:update table1  set A=‘aa1‘ where B=‘b3‘ ,系统运行这一步的时候,是从第一条数据开始加上U锁的,当检查到第二条数据的时候,U锁发现,符合B=‘b3‘时,将U锁升级为X锁,这个时候,我们就在第一条蓝线这里表示为X,接下来,继续对第3条记录进行U锁,然后为第4条,第5条,到了第11条的时候,又存在了符合B=‘b3‘时,将U锁升级为X锁,也就是我表示的第二条蓝线,继续加U锁,发现,整张表都已经所扫描完了,没有存在符合条件的了,这个时候,如果Process Node1到这里就运行完了,我们应该释放X锁,但是Process Node1,后面还有代码:waitfor  delay ‘00:00:10‘  ,也就是等待了10秒钟,这个时候,只要事务不执行完成,X锁不会释放。

而此时,我已经运行了事务二(Process Node2),我们看到它的代码是: update table1  set A=‘aa3‘  where B=‘b1‘,通过上面说明,我们同样分析一下该update语句的执行过程,Process Node2将table1中的数据从第一条开始加U锁,这个时候,第一条数据就符合 B=‘b1‘,这个时候,U锁升级为X锁,继续往下执行的时候,发现第二条数据已经被Process  Node1的X锁占有,X锁为排它锁的原因为不与其他锁兼容,也就是说,不能加U锁,这个时候,Process Node2只能等待Process Node1将X锁释放,而Process Node1 没有执行完成,是不会释放X锁的,所以Process Node2 等待Process Node1释放第二条记录的X锁。

这个时候,我们发现Process Node1中waitfor  delay ‘00:00:10‘ ,代码已经运行完成,之后,进行update table1 set A=‘aa2‘ where B=‘b8‘代码,这条语句从第一条数据开始加U锁,但是,我们发现第一条数据已经被Process Node2的X锁占用,也就是说,Process Node1需要等待Process Node2 的X锁释放才行,所以Process Node1 在第一条记录这里等待。

我们最后看到的应该是这样的:

因此出现了上面的现象,就是Process Node2 想要对Resource Node1(第二条记录)请求U锁,但是,Resource Node1 被Process Node1 的X锁占用,而Process Node2 想要对Resource Node2(第一条记录)请求U锁,但是Resource Node2被Process Node2 的X锁占用。

解决方案:

我们的解决方案是这样的,在B的字段加上非聚集索引,就可以了,为什么呢?应为聚集索引和非聚集索引加U锁的时候,都不是整张表进行扫描的,而是直接就可以根据索引找到这条记录进行升级锁,所以,不会出现上面的死锁的问题。

为解决死锁,引入更新锁。

----------------------------------------
T1:
begin tran
select * from table(updlock) (加更新锁)
update table set column1=‘hello‘
T2:
begin tran
select * from table(updlock)
update table set column1=‘world‘

更新锁的意思是:“我现在只想读,你们别人也可以读,但我将来可能会做更新操作,我已经获取了从共享锁(用来读)到排他锁
(用来更新)的资格”。一个事物只能有一个更新锁获此资格。

T1执行select,加更新锁。
T2运行,准备加更新锁,但发现已经有一个更新锁在那儿了,只好等。

当后来有user3、user4...需要查询table表中的数据时,并不会因为T1的select在执行就被阻塞,照样能查询,相比起例6,这提高
了效率。

数据库事物

通过将一组相关操作组合成一个要么全部执行成功,要么全部执行失败的单元,极大地简化了错误恢复并使应用程序更加可靠。事务既可以在存储过程中写,也可以在应用程序中写。

事物的三个操作:

开始,提交,回滚。

保存点:设置保存点后,事物回滚可以回滚到设置的保存点,而不必全部回滚。

java事务的类型有三种;JDBC事务,JTA事务(分布式多数据源),容器事务。

在jdbc api中,默认的情况为自动提交事务,也就是说,每一条对数据库的更新的sql语句代表一项事务,操作成功后,系统自动调用commit()来提交,否则将调用rollback()来撤消事务。 
在jdbc api中,可以通过调用setAutoCommit(false) 来禁止自动提交事务。然后就可以把多条更新数据库的sql语句做为一个事务,在所有操作完成之后,调用commit()来进行整体提交。倘若其中一项 sql操作失败,就不会执行commit()方法,而是产生相应的sqlexception,此时就可以捕获异常代码块中调用rollback()方法撤消事务。

spring的事务管理实践和JTA理论代码:

存储过程

存储过程是大型数据库系统中一组为了完成特定功能的SQL语句集,存储在数据库中。经过第一次编译后再次调用不用再编译。用户通过指定存储过程的名字并给出参数(如果该存储过程有名字)来执行它。

时间: 2024-10-05 00:23:06

锁及事务的相关文章

《高性能MySQL》读书笔记--锁、事务、隔离级别 转

1.锁 为什么需要锁?因为数据库要解决并发控制问题.在同一时刻,可能会有多个客户端对表中同一行记录进行操作,比如有的在读取该行数据,其他的尝试去删除它.为了保证数据的一致性,数据库就要对这种并发操作进行控制,因此就有了锁的概念. 1.1锁的分类 从对数据操作的类型(读\写)分 读锁(共享锁):针对同一块数据,多个读操作可以同时进行而不会互相影响. 写锁(排他锁):当前写操作没有完成前,它会阻断其他写锁和读锁. 大多数时候,MySQL锁的内部管理都是透明的. 1.2锁粒度(Lock granula

《高性能MySQL》读书笔记--锁、事务、隔离级别

1.锁 为什么需要锁?因为数据库要解决并发控制问题.在同一时刻,可能会有多个客户端对表中同一行记录进行操作,比如有的在读取该行数据,其他的尝试去删除它.为了保证数据的一致性,数据库就要对这种并发操作进行控制,因此就有了锁的概念. 1.1锁的分类 从对数据操作的类型(读\写)分 读锁(共享锁):针对同一块数据,多个读操作可以同时进行而不会互相影响. 写锁(排他锁):当前写操作没有完成前,它会阻断其他写锁和读锁. 大多数时候,MySQL锁的内部管理都是透明的. 1.2锁粒度(Lock granula

锁和事务

锁和事务 读写锁 共享锁 (只读) 排他锁 (可写) 颗粒锁 控制锁的范围 事务经典例子: 银行转账 A 转给 B 1000 块 1. 先从 A 的帐号余额 减去 1000 元 2. B 的帐号增加 1000 元 其中上面两个过程有一个失败就会滚, 退回原来的操作 特征 ACID: 原子性 atomicity 一致性 consistency 隔离性 isolation 持久性 durability

MySQL锁和事务(一):InnoDB锁(MySQL 官方文档粗翻)

// 写在前面,实际上,数据库加锁的类型和范围受到多种因素的影响,例如数据库隔离等级,SQL语句,是否使用主键.索引等等.可以查看博文: http://www.cnblogs.com/zhaoyl/p/4121010.html 了解 这一章节讲述了InnoDB使用的锁类型. 共享锁(S)和独占锁(X) 意向锁 行锁(record lock,不知道叫 记录锁是不是更好,百度了一下有人叫行锁) 间隙锁(gap lock) Next-Key锁 插入意向锁 AUTO-INC(自增长)锁 空间锁(Pred

InnoDB锁与事务模型

一.locking 锁 1.Shared and exclusive locks innodb实现了两种类型的行级锁(锁粒度)Shared(s)locks 共享锁:允许持有锁的事务去读取行记录.Exclusive(x)locks 排它锁:允许持有锁的事务更新.删除行记录.如果事务t1持有了行记录r的 s lock.当另一个事务t2想要对记录r持有一个锁的时候:T2请求一个s lock:T2立刻获得s lock,t1.t2同时持有记录r的共享锁T2请求一个x lock:t2 不能立刻获得 排它锁.

《高性能MySQL》读书笔记之 MySQL锁、事务、多版本并发控制的基础知识

1.2 并发控制 1.2.1 读写锁 在处理并发读或写时,通过实现一个由两种类型的锁组成的锁系统来解决问题.这两种类型的锁通常被称为 共享锁(shared lock) 和 排它锁(exclusive lock),也叫读锁(read lock)和写锁(write lock). 读锁是共享的,或者说是不互相阻塞的.多个客户端可以在同一时刻读取同一个资源,而互不干扰.写锁则是排他的,也就是说一个写锁会阻塞其他写锁和读锁. 1.2.2 锁粒度 为了提高共享资源的并发性,尽量只锁定需要修改的部分数据,而不

SQLserver锁和事务隔离级别的比较与使用(转)

SQLserver锁和事务隔离级别的比较与使用(转) http://www.cnblogs.com/chenlulouis/archive/2010/12/06/1898014.html http://www.cnblogs.com/CareySon/p/3509030.html

(转)SQL SERVER的锁机制(三)——概述(锁与事务隔离级别)

五.锁与事务隔离级别 事务隔离级别简单的说,就是当激活事务时,控制事务内因SQL语句产生的锁定需要保留多入,影响范围多大,以防止多人访问时,在事务内发生数据查询的错误.设置事务隔离级别将影响整条连接. SQL Server 数据库引擎支持所有这些隔离级别: · 未提交读(隔离事务的最低级别,只能保证不读取物理上损坏的数据) · 已提交读(数据库引擎的默认级别) · 可重复读 · 可序列化(隔离事务的最高级别,事务之间完全隔离) SQL Server 还支持使用行版本控制的两个事务隔离级别.一个是

数据库的数据类型、索引、锁、事务和视图

数据库的数据类型.索引.锁.事务和视图 数据的类型 1)数据类型: 数据长什么样? 数据需要多少空间来存放? 系统内置数据类型和用户定义数据类型 2)MySql 支持多种列类型: 数值类型 日期/时间类型 字符串(字符) 类型 3)选择正确的数据类型对于获得高性能至关重要,三大原则: 更小的通常更好,尽量使用可正确存储数据的最小数据类型 简单就好,简单数据类型的操作通常需要更少的CPU 周期 尽量避免NULL,包含为NULL的列,对MySQL更难优化 4)整型 tinyint(m) 1节个字节,

mysql锁,事务

什么是事务 事务定义了一个服务操作序列,由服务器保证这些操作序列在多个客户并发访问和服务器出现故障情况下的原子性事务的属性 A --redo&undo C --undo I --lock D --redo事务编程锁的概念lock与lath的区别--对象:事务/线程--保护:数据库对象/内存结构--持续时间:长/短--模式:表锁行锁/互斥--死锁:有/无InnoDB锁分析--IS,IX,S,X--record lock,gap lock,next-key lock MDL锁分析死锁原理与分析SEL