mysql默认读写锁,
读操作实现悲观锁:
select * from user for update;
缺点是会将扫描过的行都锁上,因此务必确定用上了索引,不然行锁变表锁,将整个表锁住
写操作实现乐观锁(版本号和时间戳字段)
UPDATE user SET userName = 'userName', version = version + 1 WHERE userId = 'userId' AND version = 'version';
程序实现逻辑:
if(rowsUpdated == 0) {
throws new OptimisticLockingFailureException();
}
共享锁(IS锁)实现方式:
select * from user LOCK IN SHARE MODE;
行锁的劣势:开销大;加锁慢;会出现死锁
行锁的优势:锁的粒度小,发生锁冲突的概率低;处理并发的能力强
InnoDB 行级锁是通过给索引上的索引项加锁来实现的,InnoDB行级锁只有通过主键或唯一索引条件检索数据,才使用行级锁;否则,InnoDB使用表锁
(当“值重复率”低时,甚至接近主键或者唯一索引的效果,“普通索引”依然是行锁;当“值重复率”高时,MySQL 不会把这个“普通索引”当做索引,即造成了一个没有索引的 SQL,此时引发表锁。)
在不通过索引条件查询的时候,InnoDB是表锁而不是行锁。
导致行锁变表锁:
不通过索引查询
数据类型自动转换,MySql的优化器强制转化为匹配的类型;导致行锁升级为表锁
整张表的大部分数据更新时,一行一行地加锁会导致事务执行效率低,从而可能造成其他事务长时间锁等待和更多的锁冲突问题,性能严重下降。所以MySQL会将行锁升级为表锁,即实际上并没有使用索引。
mysql事务
并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
2、不可重复读:事务 A 多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
隔离级别
Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取已提交内容)
大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
Repeatable Read(可重读)
MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过还是有一个棘手的问题:幻读 (Phantom Read),幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。在这个级别,可能导致大量的超时现象和锁竞争。
不要看到select就说不会加锁了,在Serializable这个级别,读操作是会加锁的!