java手动提交事务_Java架构-猜你不知道MyBatis这几道常见的面试题(附答案详解)...

84b1d94e7c5f611f816e144faee21842.png

1 . 经常碰到这样的面试题目:#{}和${}的区别是什么?

正确的答案是:#{}是预编译处理,${}是字符串替换。

(1)mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值。

(2)mybatis在处理${}时,就是把${}替换成变量的值。

(3)使用#{}可以有效的防止SQL注入,提高系统安全性。原因在于:预编译机制。预编译完成之后,SQL的结构已经固定,即便用户输入非法参数,也不会对SQL的结构产生影响,从而避免了潜在的安全风险。

(4)预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。

最后,补充一点:

$符号一般用来当作占位符,常使用Linux脚本的人应该对此有更深的体会吧。例如:$1,$2等等表示输入参数的占位符。知道了这点就能很容易区分$和#,从而不容易记错了。

2 .数据库链接中断如何处理

题目:

我们知道,数据库的访问底层是通过tcp实现的,如果数据库链接中断,那么应用程序是不知道的,是探测不出的,那么程序会卡住,一直在等待,会等待吓人的几十分钟,这种情况会把人郁闷死,真不如及时来个弹框,告诉用户系统暂时无法使用,让用户离开呢。所以,面对数据库连接中断的异常,该怎么设置mybatis呢?

答案:

要想吃透这个问题,要明白链接中断产生的原因。这里面会涉及到网络通信的问题。在数据库链接中,connection操作可不是计算1+1这样的形式,它低层是个循环处理过程,既然是循环处理过程那么自然就跟时间扯上关系了,跟时间有关的设置有:max_idle_time,connect_timeout。max_idle_time表明最大的空闲时间,超过这个时间socket就会关闭,这样操作系统会省心省力一些,毕竟操作系统维持一个socket也是花费不少精力的。connect_timeout表明链接的超时时间,我们知道,网络环境就是跟潮水一样,一波一波的,总是在波动,既是数据库服务器活的杠杠的,但是因为网络用塞,客户端仍然连不上服务器端,这个时候就要设置timeout,别一直傻等着。

3 . 数据库插入重复如何处理

问题:

在开发过程中,经常遇到插入重复的现象,这种情况该如何解决呢?

答案:

插入的过程一般都是分两步的:先判断是否存在记录,没有存在则插入否则不插入。如果存在并发操作,那么同时进行了第一步,然后大家都发现没有记录,然后都插入了数据从而造成数据的重复。解决插入重复的思路可以是这样的:

(1)判断数据库是否有数据,有的话则无所作为。没有数据的话,则进行下面第2步

(2)向redis set key,其中只有一个操作a会成功,其他并发的操作b和c会失败的

(3)上面set key 成功的操作a,开始执行插入数据操作,无论是否插入数据成功,都在最后del key。【注】插入不成功可以多尝试几次,增加成功的概率。

(4)上面set key 失败的操作b和c,sleep一下,然后再判断数据库是否有数据,有数据则无所做为,没有数据则重复上面的set key,此时是b和c在竞争,失败者则无所作为,成功者则开始插入数据,然后无论插入成功还是失败则都要del key。【注】既然是并发了,本身就是异常情况,就没有必要考虑用户体验了,就可以多sleep一会儿也无妨,不过对于单线程多事件处理的开发模式不要sleep太久。

总之,上面的过程就是:线程a 线程b 线程c,同时插入数据。如果线程a拿到锁之后,让它插入数据,它插入成功了,那么线程b 线程c啥也不用做;它插入失败了,线程b 线程c则抢锁,谁抢到了谁插入数据,不管最后是否成功,程序走到此步就可以了,已经完成了既定两个目标:执行插入,不重复插入。

4 . 事务执行过程中宕机的应对处理方式

问题:数据库插入百万级数据的时候,还没操作完,但是把服务器重启了,数据库会继续执行吗? 还是直接回滚了?

答案:不会自动继续执行,不会自动直接回滚,但是可以人工手动选择继续执行或者直接回滚,依据是事务日志。

事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是人们口中常说的“日志先行”(Write-Ahead Logging)。

日志分为两种类型:redo log和undo log

(1)redo log

在系统启动的时候,就已经为redo log分配了一块连续的存储空间,以顺序追加的方式记录redo log,通过顺序io来改善性能。所有的事务共享redo log的存储空间,它们的redo log按语句的执行顺序,依次交替的记录在一起。如下一个简单示例:

记录1:

记录2:

记录3:

记录4:

记录5:

此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据redo log中记录的日志,把数据库恢复到崩溃前的一个状态。未完成的事务,可以继续提交,也可以选择回滚,这基于恢复的策略而定。

(2)undo log

undo log主要为事务的回滚服务。在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。

以下是undo+redo事务的简化过程,假设有2个数值,分别为A和B,值为1,2

  1. start transaction;
  2. 记录 A=1 到undo log;
  3. update A = 3;
  4. 记录 A=3 到redo log;
  5. 记录 B=2 到undo log;
  6. update B = 4;
  7. 记录B = 4 到redo log;
  8. 将redo log刷新到磁盘
  9. commit

在1-8的任意一步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。如果在8-9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化。若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘。所以,redo log其实保障的是事务的持久性和一致性,而undo log则保障了事务的原子性。

5 . Java客户端中的一个Connection问题

问题:

Java客户端中的一个Connection是不是在MySQL中就对应一个线程来处理这个链接呢?

答案:

不是。凡是从线程思考问题的人,一般都是被Java技术的多线程思想所禁锢了,其实在高性能服务器端端开发底层往往靠io复用来处理,这种模式就是:单线程+事件处理机制。在MySQL里面往往有一个主线程,这是单线程(与Java中处处强调多线程的思想有点不同哦),它不断的循环查看是否有socket是否有读写事件,如果有读写事件,再从线程池里面找个工作线程处理这个socket的读写事件,完事之后工作线程会回到线程池。所以:Java客户端中的一个Connection不是在MySQL中就对应一个线程来处理这个链接,而是由监听socket的主线程+线程池里面固定数目的工作线程来处理的。

65b7fd4875f7242dee6369f7639c961e.png
444d858a05e903c9cb1ed72cf2bdbd78.png

最后

小编最近将收集的Java程序员进阶架构师的资料做了一些整理(如下图),免费分享给每一位学习Java的朋友。

关注后台私信回复【资料】即可获取

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值