Java开发 - 数据库中的基本数据结构(二)

简介: Java开发 - 数据库中的基本数据结构

数据库中的事务


数据库中的事务提交默认开启,可通过命令查询状态,关闭或者开启:


查询命令


show variables like 'autocommit'


关闭命令

set autocommit=off;


开启命令

set autocommit=on;


事务的管理


开启事务:beigin

提交事务:commit

回滚事务:rollback


尤其需要注意的是,增删改默认开启数据库事务,select不涉及任何事务。


在微服务中,当涉及到多步增删改的操作步骤时,一般我们使用dubbo调用其他模块数据库操作时会用到事务,以保证所有的操作要么一起成功,要么一起失败。此时就会为整个业务实现方法增加事务的注解。


数据库中的死锁


死锁在数据访问安全时时有发生,数据库也是数据访问的一种,虽然数据库提供了一些锁机制来保证线程安全,但,有人的地方就有江湖,数据库也无可避免的就会出现死锁的现象。


下面,我们举个例子来说明数据库中的死锁:


用户表:user1

id name age
1 张三 20
2 李四 30

事务A
1.修改user1的张三为张二
2.删除user2的id为1的王五

<------------------------->

事务B
1.修改user2的王五的年龄为20
2.删除user1的id为1的张三

用户表:user2

id   name age
1 王五 15
2 周六 18

我们可以想象一下,这俩操作会怎么样,上面我们说过,增删改都会增加排他锁,所以第一步操作都可以执行完成,且添加了排他锁,那么在第二步操作的时候,由于排他锁的存在,需要等待排他锁的释放,会导致事务A和事务B相互等待,无法继续执行,死锁产生。


那么出现死锁之后,数据库是怎么解决的呢?


在实际操作用会出现两种情况,一种是提示死锁,另一种是提示锁等待超时,这是因为数据库版本不一致的问题。


在mariadb中,对死锁是这么处理的:当检测到死锁后,让一端的事务回滚,并提示死锁DeadLock,接着让另一端的事务执行成功,一般是让后执行的事务进行回滚,回滚之后的事务执行失败,并且不会再次主动发起自旋执行。


数据库中的视图


什么是视图?


数据库中的视图是一张虚拟表,用于展示结果集,它并不保存数据,而是从已经存在的表中调取数据,后期如果要执行相同的sql,可以直接调用视图名称。听起来有点像我们对方法的封装,这里是对数据库sql语句的封装,只不过数据库没有方法的概念。


使用场景:我们要多次展示数据库中某张表或几张表中相同的数据,一样的sql语句需要多次使用,这时候,就可以为这个sql创建视图,在需要的地方直接调用视图,从视图中获取数据,简化sql语句。


操作视图:


创建视图:


create view view_name as select xx from xxxx_tb where xxxxxx;


也可以进行连表操作。


调用视图:


select xxx from view_name;


删除视图:


drop view view_name;


你会发现视图的操作和数据库的操作是一样的。


视图的注意事项


视图是对sql语句的封装,不是对查询结果集的封装,视图的存在并不存提高任何查询的效率,只是简化了sql语句;

视图一般只用于查询,不对数据进行写操作,所以不应该对视图进行写操作,但是数据库允许这么做。

视图来源于单表

insert操作     成功

delete操作    成功

update操作   成功


视图来源于多表连查

insert操作     失败

delete操作    失败


update操作

修改一张表中数据    成功

修改多张表中数据    失败


视图中不保存真是的数据,而是来源于真实的表中,所以,当真是表中的数据发生变化,视图数据也会发生变化。


事务隔离级别


事务的隔离级别有四种:


read uncommitted  读未提交

read committed   读已提交

repeatable read  可重复读

serializable    可串行化


读未提交


事务的结束表现为两种情况,一种是事务提交,另一种是事务的回滚,读未提交即表现在事务可以读取这两种状态下的数据,从而产生脏读。


什么是脏读?


脏读就是事务读取到了其他事务未提交或未回滚之后的内容,导致最终读取到的数据不存在,这就叫脏读。


我们想想这种情况怎么发生?

时刻 事务A 事务B
t1 begin begin
t2 查询user.name='张三'
t3 修改user.name='李四'
t4 查询到user.name='李四'

t5

由于某些原因导致事务B失败,rollback
t6

commit,此时表中user.name实际为'张三',读到的为'李四'

...

很明显,脏读这种情况不应该出现,所以读未提交这种隔离级别不应该被使用,事实上,也没有数据库使用读未提交的隔离级别。要解决这种情况,产生了另一种隔离级别:读已提交。


读已提交


读已提交的出现就是为了解决读未提交的情况,事务职能读取到其他事务提交后或回滚后的内容,解决了脏读问题,这里应该有掌声👏👏。


不过问题来了,这又产生了新的问题:不可重复读!!!

不可重复读是什么?看下表

时刻 事务A 事务B
t1 begin begin
t2 查询user.name='张三'
t3 修改user.name='李四'
t4 commit

t5

再次查询user.name='李四'
t6

commit

...

两次查询发现查询结果不一样?那么是不是有问题,可能我们看着觉得没错,假设两次分别查询的是银行卡余额,第一次100w,第二次0,我只是查了两次,什么都没干,钱没了,那么你觉得问题大不大? 要不要报警?


在Java中,这就是并发线程的安全问题,所以我们也要避免事务访问期间,其他事务对我们访问的数据进行修改。


根据我们前面学到的内容,我们可以给这条数据加上排他锁,这就解决了这个问题,相信大家已经学会了,此时将产生新的隔离级别:可重复读!


可重复读


可重复读这个隔离级别完美吗?看到这里显然没有结束,所以它一定是不完美的,没错,新的问题又产生了:幻读!


幻读是什么?我们用下面这张表来解读下:

时刻 事务A 事务B
t1 begin begin
t2 查询user表所有数据=2条
t3 添加一个新用户
t4 commit

t5

再次查询useruser表中的数据=3条
t6

commit

...

还以查询银行卡为例,第一次查100w,第二次查0,这是不正常的,因为事务A没有做任何处理。这和不可重复读很相似,但两者也有一定的区别,不可重复读是对数据进行修改,幻读是对数据库进行添加操作,两者都是前后查询到的结果不一致导致的。


我们考虑下幻读应不应该存在? 这种情况可以存在,我认为合法,但是我就需要两次读取到的数据保持一致,该怎么办?给表加排他锁!此时,两次查询结果一致。幻读问题解决!


加了锁,新的隔离级别也就产生了:可串行化!


可串行化


可串行化解决了幻读的问题,但是由于需要给整张表加锁,这样,数据的访问效率就大大降低,现在做开发,谁不使用并发的?所以实际开发中可串行化并不常用,只有在执行一些安全性要求极高的操作时才会使用这一等级。


用一张表来表示他们之间的关系:

隔离等级 产生脏读 产生不可重复读 产生幻读
读未提交 true true true
读已提交 false true true
可重复读 false false true
可串行化 false false false


隔离等级的优先级


和我上面给出的顺序是一致的,优先级由低到高分别是:读未提交-->读已提交-->可重复读-->可串行化。就像道生一,一生二,二生三,三生万物。


数据库使用的隔离级别

oracle和sql server 默认的隔离级别为读已提交。

mysql的 默认隔离级别为可重复读。


MVCC


MVCC是多版本并发控制,是用来实现可重复读的。它解决了并发安全的问题,大大提高了并发执行效率,优于加锁。


MVCC的实现分为三部分:


undolog  ,用于记录一些信息,我们下面会说到;

mysql中的每张表里隐藏的三个字段

row_id,Innodb引擎提供的隐藏主键,表中没有主键时自动创建,从1开始,自增;

DB_trx_id,表示最后操作这条数据的事务的id;

DB_roll_ptr,表示回滚指针,当前事务操作变更数据时,如果失败,需要回滚到之前的那条数据,这里保存的就是操作前数据的地址。

ReadView

目录
相关文章
|
2天前
|
XML Java 数据库连接
性能提升秘籍:如何高效使用Java连接池管理数据库连接
在Java应用中,数据库连接管理至关重要。随着访问量增加,频繁创建和关闭连接会影响性能。为此,Java连接池技术应运而生,如HikariCP。本文通过代码示例介绍如何引入HikariCP依赖、配置连接池参数及使用连接池高效管理数据库连接,提升系统性能。
15 5
|
4天前
|
存储 SQL API
探索后端开发:构建高效API与数据库交互
【10月更文挑战第36天】在数字化时代,后端开发是连接用户界面和数据存储的桥梁。本文深入探讨如何设计高效的API以及如何实现API与数据库之间的无缝交互,确保数据的一致性和高性能。我们将从基础概念出发,逐步深入到实战技巧,为读者提供一个清晰的后端开发路线图。
|
2天前
|
存储 缓存 NoSQL
2款使用.NET开发的数据库系统
2款使用.NET开发的数据库系统
|
6天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
21 6
|
5天前
|
存储 SQL 数据库
深入浅出后端开发之数据库优化实战
【10月更文挑战第35天】在软件开发的世界里,数据库性能直接关系到应用的响应速度和用户体验。本文将带你了解如何通过合理的索引设计、查询优化以及恰当的数据存储策略来提升数据库性能。我们将一起探索这些技巧背后的原理,并通过实际案例感受优化带来的显著效果。
18 4
|
6天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
18 4
|
8天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
29 4
|
7天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
14 0
|
8天前
|
Java API Android开发
kotlin和java开发优缺点
kotlin和java开发优缺点
21 0
|
6天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
20 4