java中mysql数据库锁的介绍以及运用

本文详细介绍了MySQL中的事务特性、隔离级别,以及MyISAM和InnoDB表锁的区别,重点讲解了InnoDB的行锁、并发事务处理的问题和优化建议。
摘要由CSDN通过智能技术生成

能够了解关于mysql中锁的那些事,并能够帮助自己怎么能在合适的场景去使用,怎么去选择


前言

  • MySQL 不仅用于小型数据库,还可以扩展到大型企业级应用场景。它在许多高流量的网站和大型企业系统中得到了广泛应用。
  • 虽然 Oracle 收购了 MySQL,但开源版本仍然存在且免费。MySQL 有多个版本(如社区版、企业版等),其中一些版本收费,但免费的社区版仍在维护和更新。

一、mysql事务?

说到mysql的锁的那些问题,咱得先聊聊mysql的事务。

1.事务特性

原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行

一致性:事务的执行使得数据库从一种正确状态转换成另一种正确状态

隔离性:在事务正确提交之前,不允许把该事物对数据的任何改变提供给任何其他事务

持久性:事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存

2.隔离级别

(1)读未提交(read Uncommited):

在该隔离级别,所有的事务都可以读取到别的事务中未提交的数据,会产生脏读问题,在项目中基本不怎么用, 安全性太差;(可能会产生脏读、不可重复读、幻读)

(2) 读已提交(read commited):

这是大多数数据库默认的隔离级别,但是不是MySQL的默认隔离级别;这个隔离级别满足了简单的隔离要求:一个事务只能看见已经提交事务所做的改变,所以会避免脏读问题;(可能会产生不可重复读和幻读)

(3 ) 可重复读(Repeatable read):

这是MySQL的默认隔离级别,它确保了一个事务中多个实例在并发读取数据的时候会读取到一样的数据;不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。(可能会产生幻读)

(4) 可串行化(serializable):

事物的最高级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争,一般为了提升程序的吞吐量不会采用这个;(只有串行化解决了全部的问题,但也意味着这种隔离级别 的性能是最低的)

二、MySQL锁问题

1.锁概述

锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中除传统的计算资源的争用外,数据也是一种供许多用户共享的资源,如何保证数据并发访问的一致性,有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素,从这个角度来说,锁对于数据库而言显得尤其重要.

2.锁分类

1.主要是针对锁粒度划分的,一般分为:行锁、表锁、库锁

行锁:访问数据库的时候,锁定整个行数据,防止并发错误。(开销小,加锁快,不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低)

表锁:访问数据库的时候,锁定整个表数据,防止并发错误。(开销大,加锁慢,会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高)

2.从对数据库操作的类型 一般分为读锁和写锁

读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会相互影响

写锁(排它锁):当前曹锁没有完成之前,他会阻断其他写锁和读锁

存储引擎表级锁行级锁页面锁
MyISAM支持不支持不支持
InnoDB支持支持不支持
MEMORY支持不支持不支持
BOB支持不支持支持

从上述的特点可见,很难笼统的说哪种锁更好,只能就具体的特点来说哪种更合适,仅从锁的角度来说:表级锁更适合于查阅为主,只有少量耳朵按索引条件更新数据的应用,比如web应用;而行级锁则更适合于有大量按索引条件并发更新少量不同的数据,同时还又有并查询的应用 比如一些在线事务处理系统

3.MyISAM表锁

MyISAM存储引擎支持表锁,这也是mysql开始的几个版本中唯一支持的锁类型

1.如何加表锁

显示加表锁的语法:

加读锁:  lock TABLE table_name READ;

加写锁: lock TABLE table_name WRITE;

如果对一张表加了读锁他并不会影响其他线程对数据的读操作,但是会限制对数据的写操作

 如果对一张表加了写锁 会影响其他线程 在当前线程没有释放锁( UNLOCK TABLES)其他线程不得读和写

这正是因为以上原因,MyISAM的读写锁调度是写有限,这也是MyISAM不适合做为写为主的表的存储引擎的原因,因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞

4.查看锁的争用情况

-- 查看所有的表
SHOW open TABLES
-- 查询表争用情况
show STATUS like '%Table_Locks%'

Table_locks_immediate:值得是能够立即获得表锁的次数,每立即获取锁,值加一

Table_locks_waited:指的是不能立即获取表级锁而需要等待的次数,每等待一次改值加一,此值高说明存在着较为严重的表级锁争用情况

4.InnoDB行锁

1.行锁介绍

行锁的特点:偏向于InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定的粒度最小,发生锁冲突的概率最低,并发度也最高,InnoDB和MyISAM最大的不同在于:一是对事物的志成,二是采用了行级锁

2.ACID

ACID属性含义
原子性事务是一个原子操作单元,其对数据的修改,幺妹全部成功,要么全部失败
一致性在事务开始和完成时,数据都必须保持一致状态
隔离性数据系统提供一定隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
持久性事务完成之后,对于数据的修改是永久的

3.并发事务处理带来的问题

问题含义
丢失更新当两个或多个事务选择同一行,最初的事务修改的值,会被后面的事务修改的值覆盖
脏读当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没提交到数据库中.这时另外一个事务也访问这个数据,然后使用了这个数据
不可重复读一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现和以前读取的数据不一致
幻读一个事务按照相同的查询条件重新读取以前查询过的数据,却发现其他事务插入了满足器查询条件的新数据

4.InnoDB的行锁模式

共享锁(S):又称为凌锁,简称S锁,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能凌不能修改。

·排他锁(X):又称为写锁,简称X锁,排他锁就是不能与其他锁并存,如一个事务获取了一个数据行 的排他锁,其他事务就不能再获取该行的其他锁,包括共字锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

共享锁:SELECT FROM tab1e_name WHERE...LOCK IN SHARE MODE
排它锁:SELECT FROM table_name WHERE ...FOR UPDATE

但是如果索引实效 这个时候InnoDB的行锁会升级成表锁

5.间隙锁

当我们用范围条件,而不是使用相等条件检索数据,并请求共字或排他锁时,InnoDB会给符合条件的已有数据进行加锁;对于键值在条件范围内但并不存在的记录,叫做"间隙(GAP)”,InnoDB也会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。


总结

InnoDB存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面带来了星梦损耗可能比表锁会更高一些,但是在整体并发处理能力方面要远远高于MyISAM 的表锁的,当系统并发量较高的时候,InnoDB的整体性能和MyISAM相比就会有比较明显的优势

但是,InnoDB的行级锁同样也有脆弱的意面,当我们使用不当的时候,可能会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至可能会更差

优化建议:

·尽可能让所有数据检索都能通过索引来完成,避免无索引行锁升级为表锁。

·合理设计索引,尽显缩小锁的范围·尽可能减少索引条件,及索引范围,避免间隙锁

·尽量控制事务大小,减少锁定资源量和时间长度

·尽可使用低级别事务隔离(但是需要业务层面满足需求)

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 项目使用 MySQL 悲观主要是通过 SQL 语句的 `SELECT ... FOR UPDATE` 或 `SELECT ... LOCK IN SHARE MODE` 实现的。 `SELECT ... FOR UPDATE` 语句会对查询结果的每一行加上排它(Exclusive Lock),其它事务无法对这些行进行修改或者加排它,直到当前事务结束,这种方式适用于需要修改的场景。 `SELECT ... LOCK IN SHARE MODE` 语句会对查询结果的每一行加上共享(Shared Lock),其它事务可以对这些行进行查询但是无法修改或者加排它,直到当前事务结束,这种方式适用于只读场景。 下面是使用 `SELECT ... FOR UPDATE` 的示例代码: ```java Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = dataSource.getConnection(); pstmt = conn.prepareStatement("SELECT * FROM my_table WHERE id = ? FOR UPDATE"); pstmt.setLong(1, 1L); rs = pstmt.executeQuery(); // 对查询结果进行修改 } catch (SQLException e) { // 异常处理 } finally { // 关闭连接和释放资源 try { rs.close(); } catch (Exception e) {} try { pstmt.close(); } catch (Exception e) {} try { conn.close(); } catch (Exception e) {} } ``` 在以上示例代码,通过 `SELECT ... FOR UPDATE` 查询 id 为 1 的记录并加上排它,然后对查询结果进行修改。 需要注意的是,使用悲观会对数据库性能有影响,因为在定期间其它事务无法访问被定的数据。因此,在使用悲观时需要谨慎权衡性能和数据一致性的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值