数据库锁的种类一般分为两种:一种是悲观锁,一种乐观锁。
一、悲观锁
1.1、悲观锁(Pessimistic Lock)具有强烈的独占和排他特性,它指的是对数据被外界修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制。传统的关系数据库里用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在操作之前先上锁。
悲观锁的隔离级别可以看做可重复读。
悲观锁按使用性质分类
1.2、共享锁(读锁、S锁)
事务A对对象T加共享锁,其他事务也只能对T加共享锁,多个事务可以同时读,但不能有写操作,直到A释放共享锁。
特点:多个事务可封锁同一个共享页;任何事务都不能修改该页;该页被读取完毕,共享锁立即被释放。
1.3、互斥锁(排它锁、独占锁、写锁、X锁)
事务A对对象T加互斥锁以后,其他事务不能对T加任何锁(即其他事物进入阻塞状态),只有事务A可以读写对象T直到A释放互斥锁。
特点:仅允许一个事务封锁此页;其他任何事务必须等到互斥锁被释放才能对该页进行访问;互斥锁一直到事务结束才能被释放。
1.4、更新锁(U锁)
更新锁用来预定要对此对象施加互斥锁,它允许其他事务读,但不允许再施加更新锁或互斥锁,当被读取的对象将要被更新时,则升级为互斥锁。
更新锁主要是用来防止死锁的,因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为互斥锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个对象申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为互斥锁。这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请为更新锁,在数据修改的时候再升级为互斥锁,就可以避免死锁。
特点:用来预定要对此页施加互斥锁,它允许其他事务读,但不允许再施加更新锁或互斥锁;当被读取的页要被更新时,则升级为互斥锁。更新锁一直到事务结束时才能被释放。
1.5、自旋锁
自旋锁和互斥锁很像,一次只能有一个进程进入临界区,唯一不同的是自旋锁访问加锁资源时,会一直循环的查看是否释放锁。这样要比互斥锁效率高很多,但是仍然需要占用CPU。
特点:自旋锁需要设定一个自旋等待的最大时间,如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,就会导致其它争用锁的线程在最大等待时间内还是获取不到锁,这时争用线程会停止自旋进入阻塞状态。除此之外,当自旋锁递归调用的时候会造成死锁现象,所以慎重使用自旋锁。
悲观锁按作用范围分类(按锁的粒度分类)
数据库能够确定那些行需要锁的情况下使用行锁,如果不知道会影响哪些行的时候就会使用表锁。
举个例子,一个用户表user,有主键id和用户生日birthday。当你使用update … where id=?这样的语句时,数据库明确知道会影响哪一行,它就会使用行锁;当你使用update … where birthday=?这样的的语句时,因为事先不知道会影响哪些行就可能会使用表锁。
行锁
行锁的作用范围是行级别,数据库能够确定那些行需要锁的情况下使用行锁,如果不知道会影响哪些行的时候就会使用表锁。举个例子,一个用户表user,有主键id和用户生日birthday当你使用update … where id=?这样的语句数据库明确知道会影响哪一行,它就会使用行锁,当你使用update … where birthday=?这样的的语句的时候因为事先不知道会影响哪些行就可能会使用表锁。
表锁
表锁的作用范围是整张表。
二、乐观锁
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制,大多是基于版本号( Version )记录机制实现,而不需要借助数据库的锁机制。
乐观锁的实现
乐观锁的本质不是锁,其隔离级别可以看作为读未提交。乐观锁可以通过以下几种方式实现:
版本号
给数据增加一个版本标识,在数据库上就是表中增加一个version字段,每次更新把这个字段加1。读取数据的时候把version读出来,更新的时候比较一开始读取的version和现在的version,如果还是开始读取的version就可以更新了,如果现在的version比老的version大,说明有其他事务更新了该数据,并增加了版本号,这时候得到一个无法更新的通知,用户自行根据这个通知来决定怎么处理,比如重新开始一遍。
基于版本号实现乐观锁的关键是判断version和更新两个动作需要作为一个原子单元执行,否则在你判断可以更新以后正式更新之前有别的事务修改了version,这个时候你再去更新就可能会覆盖前一个事务做的更新。所以你可以使用update … where … and version=”old version”这样的语句,根据返回结果是0还是非0来得到通知,如果是0说明更新没有成功,因为version被改了,如果返回非0说明更新成功。
待更新字段
基于待更新字段实现乐观锁的方式和版本号方式相似,只是不增加额外字段,直接使用有效数据字段做版本控制信息,因为有时候我们可能无法改变旧系统的数据库表结构。假设有个待更新字段叫count,先去读取这个count,更新的时候去比较数据库中count的值是不是我期望的值(即开始读的值),如果是就把我修改的count的值更新到该字段,否则更新失败。
悲观锁的优缺点
悲观锁的优点是能避免冲突的发生。
悲观锁的缺点是开销较大,而且加锁时间较长,对于并发的访问性支持不好。
乐观锁的优缺点
乐观锁的优点是避免了长事务中的数据库加锁解锁开销,大大提升了大并发量下的系统整体性能表现。
乐观锁的缺点是只能在提交数据时才发现业务事务将要失败,如果系统的冲突非常的多,而且一旦冲突就要因为重新计算提交而造成较大的代价的话,乐观锁也会带来很大的问题。
乐观锁与悲观锁的选择
乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。
悲观锁适用于多写的场景,这种情况下一般会经常产生冲突。
行锁的实现方式
InnoDB 行锁是通过给索引上的索引项加锁来实现的。这种行锁意味着只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
MySQL 的 MyISAM 与 InnoDB 两种存储引擎在,事务、锁级别,各自的适用场景?
事务处理上方面
MyISAM:强调的是性能,每次查询具有原子性,其执行数度比 InnoDB 类型更快,但是
不提供事务支持。
InnoDB:提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚
(rollback)和 崩 溃 修 复 能 力 (crash recovery capabilities) 的 事 务 安 全 (transaction-safe (ACIDcompliant))型表。
锁级别
MyISAM:只支持表级锁,用户在操作 MyISAM 表时,select,update,delete,insert 语
句都会给表自动加锁,如果加锁以后的表满足 insert 并发的情况下,可以在表的尾部插入新
的数据。
InnoDB:支持事务和行级锁,是 innodb 的最大特色。行锁大幅度提高了多用户并发操
作的新能。但是 InnoDB 的行锁,只是在 WHERE 的主键是有效的,非主键的 WHERE 都会锁
全表的。
非关系型数据库和关系型数据库区别,优势比较
非关系型数据库的优势:
性能:NOSQL 是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要
经过 SQL 层的解析,所以性能非常高。
可扩展性:同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。 关
系型数据库的优势:
复杂查询:可以用 SQL 语句方便的在一个表以及多个表之间做非常复杂的数据查询。
事务支持:使得对于安全性能很高的数据访问要求得以实现。
MySql 的存储引擎有哪些,区别是什么
MySQL 常见的三种存储引擎为 InnoDB、MyISAM 和 MEMORY。
1、事务安全:
InnoDB 支持事务安全,MyISAM 和 MEMORY 两个不支持。
2、存储限制:
InnoDB 有 64TB 的存储限制,MyISAM 和 MEMORY 要是具体情况而定。
3、空间使用:
InnoDB 对空间使用程度较高,MyISAM 和 MEMORY 对空间使用程度较低。
4、内存使用:
InnoDB 和 MEMORY 对内存使用程度较高,MyISAM 对内存使用程度较低。
5、插入数据的速度:
InnoDB 插入数据的速度较低,MyISAM 和 MEMORY 插入数据的速度较高。
6、对外键的支持:
InnoDB 对外键支持情况较好,MyISAM 和 MEMORY 两个不支持外键。
你好呀,你已经看到了这里,非常希望你能点赞收藏,十分感谢!!!