个人随笔
目录
数据库连接超时案例分析(二)
2021-02-27 14:49:47

在上一篇文章中我的程序因为每个消费线程对应了两个事物,所以相当于一个消费线程就要占用两个数据库连接,但是数据库连接池配置的最大线程用的默认值20,不够,所以会报类似Could not open IDBC Connection for transaction。。。等错误,入下图

然后,我把数据库连接池调高一倍,就解决了问题,但是我这种解决方案其实是有问题的,问题如下:

1、在同一个线程中,应该先提交事务再开启新的事务

在一个线程中应该先提交事务,然后再开启一个新的事务,这样子才不会导致一个线程占用两个事务(两个数据库连接)。

有人可能会问,那我把数据库链接上限扩大一倍不就解决了么?

数据库链接扩大一倍是可以我解决,但是如果每个人写代码都是有嵌套事务,那么谁都不能预计那个代码嵌套了多少事务,每个线程会占用多少个数据库连接,也就不好配置程序的相关数据库连接参数,这种情况下很容易就会出现在开发环境由于并发量不高不会出现问题然后到生产环境访问量一上来了就爆炸了,只能狂烧脑细胞的处理。

所以我们一定要保证在同一个线程中,只有一个数据库连接,如果逼不得已有这种逻辑,那么也尽量少用,并且数据库连接池的上限要比线程数大。

2、非必要不要把无关的TCP链接操作纳入事务管理中

比如我上一个例子的程序,逻辑是这样子的业务逻辑执行完后,需要向RocketMQ登记一条消息,在消息登记失败后我会插入一条失败记录在数据库中。

  1. @Transaction()
  2. public void sendMsg(){
  3. try{
  4. sendRocketMsg();
  5. }catch(Exception){
  6. insertSQL();
  7. }
  8. }

逻辑本身没有毛病,但是我们不应该再方法上加上@Transaction()注解,这样子每次发送消息都会先开启事务,也就是会新建一个数据库链接,但是理论上我们的登记消息是跟数据库没有关系的,没有必要登记消息就去占用数据库连接,如果此时数据库连接有问题,那么消息也就再也发不出去,所以最好的逻辑是只有在抛出异常要进行登记消息的时候才需要去开启事务占用连接,伪代码如下

  1. ```
  2. public void sendMsg(){
  3. try{
  4. sendRocketMsg();
  5. }catch(Exception){
  6. @Transaction()
  7. insertSQL();
  8. }
  9. }

3、有对数据库多次修改操作的逻辑必须要加入同一个事务中

怎么说呢,我们有一个消费程序,然后我发现积压了几十万条消息,检查发现service方法没有加上@Transaction,真是人才啊,看下逻辑进行了多次数据库修改操作,这种情况会导致一个线程会占用多个数据库连接,那么数据库连接配置就更加不够了,所以解决办法就是加上事务注解,都放在同一个事务里就好了。(但是就算这样单个线程也是顺序执行的,提交事务后就会释放连接重新获取连接?搞不懂为啥)

总结

1、在同一个线程中,应该先提交事务再开启新的事务
2、非必要不要把无关的TCP链接操作纳入事务管理中
3、有对数据库多次修改操作的逻辑必须要加入同一个事务中
4、一定要主意程序数据库连接池的最大连接数是否足够
5、数据库连接池的最大值一定要大于等于线程池的最大值

 39

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号