您的位置:首页 > 数据库

[数据库]事务、并发、数据库锁

2017-07-15 17:32 120 查看
一 事务
事务的定义
事务的特性

二 并发带来的问题
1 问题
脏读
虚读幻读
不可重复读

2 隔离级别
可读取未确认Read uncommitted
可读取确认Read committed
可重复读Repeatable read
可串行化Serializable

三 数据库锁
共享锁
排他锁
更新锁
锁的粒度
乐观锁与悲观锁


一 、事务


1 事务的定义

  事务(Transaction)是并发控制的基本单位。事务是构成单一逻辑工作单元的操作集合,要么完整的执行,要么完全不执行。不论任何情况,DBS必须保证事务能正确完整的执行。


2 事务的特性

● Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。

● Consistency(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。

● Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。

● Durability(持久性):事务结束后,事务处理的结果必须能够得到固化。


二、 并发带来的问题


1、 问题


1 脏读

  脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。 

  当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。


2 虚读(幻读)

幻读 : 一个事务读到另一个事务已提交的新插入的数据。 

例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时第二个事务向表中插入一行新数据。那么第一个事务发现表中还有没有修改的数据行,就好象发生了幻觉一样。


3 不可重复读

  不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。 

  在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……


2、 隔离级别




1 可读取未确认(Read uncommitted)

  写事务阻止其他写事务,避免了更新遗失。但是没有阻止其他读事务。 
存在的问题:脏读。即读取到不正确的数据,因为另一个事务可能还没提交最终数据,这个读事务就读取了中途的数据,这个数据可能是不正确的。 

解决办法就是下面的可读取确认。


2 可读取确认(Read committed)

  写事务会阻止其他读写事务。读事务不会阻止其他任何事务。 
存在的问题:不可重复读。即在一次事务之间,进行了两次读取,但是结果不一样,可能第一次id为1的人叫“李三”,第二次读id为1的人就叫了“李四”。因为读取操作不会阻止其他事务。 

解决办法就是下面的“可重复读”。


3 可重复读(Repeatable read)

  读事务会阻止其他写事务,但是不会阻止其他读事务。 

  存在的问题:幻读。可重复读阻止的写事务包括update和delete(只给存在的表加上了锁),但是不包括insert(新行不存在,所以没有办法加锁),所以一个事务第一次读取可能读取到了10条记录,但是第二次可能读取到11条,这就是幻读。 

  解决办法就是下面的“串行化”。


4 可串行化(Serializable)

  读加共享锁,写加排他锁。这样读取事务可以并发,但是读写,写写事务之间都是互斥的,基本上就是一个个执行事务,所以叫串行化。


三、 数据库锁

  数据库锁出现的原因是为了处理并发问题,因为数据库是一个多用户共享的资源,当出现并发的时候,就会导致出现各种各样奇怪的问题,就像程序代码一样,出现多线程并发的时候,如果不做特殊控制的话,就会出现意外的事情,比如“脏“数据、修改丢失等问题。所以数据库并发需要使用事务来控制,事务并发问题需要数据库锁来控制,所以数据库锁是跟并发控制和事务联系在一起的。


1 共享锁

共享(S)锁:多个事务可封锁一个共享页;任何事务都不能修改该页; 通常是该页被读取完毕,S锁立即被释放。


2 排他锁

  排它(X)锁:仅允许一个事务封锁此页;其他任何事务必须等到X锁被释放才能对该页进行访问;X锁一直到事务结束才能被释放。


3 更新锁

  更新(U)锁:更新锁在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排它锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个事务申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这时,这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就可以避免死锁。 

  



4 锁的粒度

  所谓粒度,即细化的程度。锁的粒度越大,则并发性越低且开销大;锁的粒度越小,则并发性高且开销小。

锁的粒度主要有以下几种类型:

● 行锁,行锁是粒度中最小的资源。行锁就是指事务在操作数据的过程中,锁定一行或多行的数据,其他事务不能同时处理这些行的数据。行级锁占用的数据资源最小,所以在事务的处理过程中,允许其它事务操作同一表的其他数据。

● 页锁,一次锁定一页。25个行锁可升级为一个页锁。

● 表锁,锁定整个表。当整个数据表被锁定后,其他事务就不能够使用此表中的其他数据。使用表锁可以使事务处理的数据量大,并且使用较少的系统资源。但是在使用表锁时,会延迟其他事务的等待时间,降低系统并发性。

● 数据库锁,防止任何事务和用户对此数据库进行访问。可控制整个数据库的操作。

用锁效率会降低,可通过使用表锁来减少锁的使用从而保证效率。


5 乐观锁与悲观锁

● 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。 

● 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息