谈谈关于开发中的"锁"

来源:互联网 发布:microsoft mac 编辑:程序博客网 时间:2024/06/12 01:06

《笔记 – 独占锁/共享锁/Lock-free – 20141029》来谈谈锁,文中谈到可以抛出以下方面也是关于锁的:

  • 锁的类型
    • 从数据库系统的角度
      • 独占锁(Exclusive Lock)
      • 共享锁(Shared Lock)
      • 更新锁(Update Lock)
    • 从开发的角度
      • 悲观锁(Pessimistic Lock)
      • 乐观锁(Optimistic Lock)
  • MySQL隔离级别
    • 从锁的问题引发出脏读、不可重复读、幻读
    • 事务的五种设置
  • 非阻塞型算法
    • Wait-free
    • Lock-free
    • Obstruction-free

锁的类型

从数据库系统的角度来看,锁分为以下三种类型:

独占锁(Exclusive Lock)

  独占锁锁定的资源只允许进行锁定操作的程序使用,其它任何对它的操作均不会被接受。执行数据更新命令,即INSERT、 UPDATE 或DELETE 命令时,SQL Server 会自动使用独占锁。但当对象上有其它锁存在时,无法对其加独占锁。独占锁一直到事务结束才能被释放。

共享锁(Shared Lock)

  共享锁锁定的资源可以被其它用户读取,但其它用户不能修改它。在SELECT 命令执行时,SQL Server 通常会对对象进行共享锁锁定。通常加共享锁的数据页被读取完毕后,共享锁就会立即被释放。

更新锁(Update Lock)

  更新锁是为了防止死锁而设立的。当SQL Server 准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server 确定要进行更新数据操作时,它会自动将更新锁换为独占锁。但当对象上有其它锁存在时,无法对其作更新锁锁定。

从程序员的角度看,锁分为以下两种类型:

悲观锁(Pessimistic Lock)

  每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock)

  相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。  而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

MySQL隔离级别

脏读

脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
例如:
  张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。
  与此同时,
  事务B正在读取张三的工资,读取到张三的工资为8000。
  随后,
  事务A发生异常,而回滚了事务。张三的工资又回滚为5000。
  最后,
  事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。

不可重复读

是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
例如:
  在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。
  与此同时,
  事务B把张三的工资改为8000,并提交了事务。
  随后,
  在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。

幻读

是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
例如:
  目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。
  此时,
  事务B插入一条工资也为5000的记录。

事务五种隔离级别

(1)DEFAULT

使用数据库设置的隔离级别(默认),由DBA 默认的设置来决定隔离级别。

(2)READ_UNCOMMITTED

  这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。会出现脏读、不可重复读、幻读 (隔离级别最低,并发性能高)。

(3)READ_COMMITTED

保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。可以避免脏读,但会出现不可重复读、幻读问题(锁定正在读取的行)。

(4)REPEATABLE_READ

可以防止脏读、不可重复读,但会出幻读(锁定所读取的所有行)。

(5)SERIALIZABLE

这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。保证所有的情况不会发生(锁表)。

提醒

不可重复读的重点是修改:同样的条件,你读取过的数据,再次读取出来发现值不一样了.幻读的重点在于新增或者删除:同样的条件,第 1 次和第 2 次读出来的记录数不一样.

非阻塞型算法(引用无锁和无等待的定义和例子)

Wait-free(无等待)

假如一个方法是无等待的,那么它保证了每一次调用都可以在有限的步骤内结束。这篇论文给出了一个无等待(有界无等待)算法的例子。http://www.cs.technion.ac.il/~moran/r/PS/bm94.ps

Lock-free(无锁)

如果一个方法是lock-free的,它保证线程无限次调用这个方法都能够在有限步内完成。

例子:

AtomicInteger atomicVar = new AtomicInteger(0);public void funcLockFree() {    int localVar = atomicVar.get();    while (!atomicVar.compareAndSet(localVar, localVar+1)) {        localVar = atomicVar.get();    }}

Obstruction-free(无干扰)

如果一个方法满足无干扰性质,那么这个方法从任意一点开始它的执行都是隔离的,并且能够在有限步内完成。

综上所述,不难得出 Obstruction-free 是 Non-blocking synchronization 中性能最差的,而 Wait-free 性能是最好的,但实现难度也是最大的,因此 Lock-free 算法开始被重视,并广泛运用于当今正在运行的程序中,比如 linux 内核。

0 0