MySQL连接是所有database共享吗

版本信息

MySQL版本:5.5.61

mybatis-plus的starter版本:3.1.2

druid的starter版本:1.2.9

准备工作

因为需要验证MySQL连接池被打满,那我们势必需要将连接数调小,或者指定一个已知的数。这样我们才好做具体的模拟操作。

我们可以执行以下命令查看当前数据库的最大连接数

show variables like '%max_connections%';

我们当前验证的数据库最大连接数如下,100也是当前数据库的默认值


最大连接数

为了方便做验证,我们执行以下命令把数据库最大连接数设置为10。此处设置为10只是为了我们方便验证,生产上应该根据具体的并发数量与硬件资源做相应的设置。同时以下设置也是暂时的,重启MySQL服务后将被重置,如果需要永久设置可以设置my.ini配置文件,这个不在本文讨论范围。

set global max_connections=10;

将数据库最大连接数设置好后,我们来做如下两个验证,一个是同database但不同表,再一个是不同database。

同database不同表(简称实验①)

我们准备以下database


创建名为coffe的database

表结构我们还是使用上篇文章所创建t_order_info和t_user_info的,有兴趣的可以去看看:

慢SQL引发的重大生产事故

我们在工程①准备如下测试代码(简称代码①):

@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrder(OrderInfo orderInfo) throws Exception{
  System.out.println("第几次:" + orderInfo.getOrderId());
  orderInfoDao.save(orderInfo);
  //测试不释放连接
  Thread.sleep(10000000);
}

这里我们开启事务,然后向表t_order_info写入数据,但是我们添加一句Thread.sleep(10000000);将事务阻塞住不提交,从而阻止连接的释放。

紧接着我们在单元测试中编写如下代码(简称代码②):

    @Test
    public void testMaxConnection() throws Exception{
      for (int i = 0; i < 10; i++) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderId((long)(i));
        orderInfo.setUid((long)(i));
        orderInfo.setProductId((long)(i));
        new Thread(()->{
          try {
          orderInfoService.saveOrder(orderInfo);
      } catch (Exception exception) {
        exception.printStackTrace();
      }
    }).start();
}
UserInfo userInfo = userInfoDao.lambdaQuery().eq(UserInfo::getUid, 1).one();
System.out.println("userInfo:" + JsonUtils.silentObject2String(userInfo));
Thread.sleep(10000000);
}

我们在单元测试中开启十个线程执行代码①,我们知道代码①会阻塞连接释放,当我们开启十个线程后,数据库连接立马被打满,于是当我们去执行 UserInfo userInfo = userInfoDao.lambdaQuery().eq(UserInfo::getUid, 1).one(); 这句代码时,因为数据库连接已经被代码①全部打满了。所以数据库立马报了如下错误:

数据库连接打满报错截图

经过实践发现,数据库最大连接数与表没有任何关系,任何一个表操作都有可能打满数据库连接。其他任何表操作将无法执行。

在实验中我们还发现一个很有意思的事情就是我们设置max_connections只对客户端连接生效,系统连接则不受限制,验证如下:

实验组①:我们单把数据库最大连接数设置为10,但是不使用MySQL命令行进行登录也不使用数据库操作工具进行连接。我们的代码①(代码②中暂时屏蔽 userInfoDao.lambdaQuery().eq(UserInfo::getUid, 1).one())输出结果如下:

第几次:6

第几次:4

第几次:8

第几次:3

第几次:7

第几次:1

第几次:2

第几次:0

第几次:5

第几次:9

正常输出十次结果,没有报任何的错误。符合预期。


对照组①:我们还是把数据库最大连接数设置为10,并且使用MySQL命令行进行登录,但是不使用数据库操作工具进行连接。我们的代码①依然正常输出十次。说明系统连接并没有占用最大连接的数量。


对照组②:我们依然把数据库最大连接数设置为10,并且使用MySQL命令行进行登录,再使用数据库操作工具占用一个连接。我们的代码①输出结果如下:

第几次:8

第几次:9

第几次:2

第几次:3

第几次:6

第几次:7

第几次:5

第几次:1

第几次:4

只输出了九次,并且控制台报了如下错误:

com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:
Data source rejected establishment of connection, 
message from server: "Too many connections"

不同database(简称实验②)

我们再新增一个database如下:


对比database

准备表结构如下:

create table t_compare
(
id bigint not null comment '主键ID'
primary key,
name varchar(64) not null comment '名称'
);

我们在工程②准备如下测试代码(简称代码③):

@Test
    public void testMaxConnection() throws Exception{
        new Thread(()->{
            Compare first = compareMapper.selectById(1);
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("first: " + JsonUtils.silentObject2String(first));
        }).start();

        new Thread(()->{
            Compare second = compareMapper.selectById(2);
            System.out.println("second: " + JsonUtils.silentObject2String(second));
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        Thread.sleep(100000);
    }

我们在工程①执行如下代码(特别注意这份代码与实验①区别我们把第十六行代码注释了,并且我们只循环了九次,也就是我们工程①只占用了九个连接,因为工程②的druid启动时会去尝试获取链接,所以为了工程②能够正常启动,我们为工程②保留一个连接。其实这里已经能够得出结论MySQL连接是全数据库共享的,并不是针对某个database的,但是为了更贴近实战,我们将实验②继续完成):

@Test
    public void test() throws Exception{
        for (int i = 0; i < 9; i++) {
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setOrderId((long)(i + 10));
            orderInfo.setUid((long)(i + 2));
            orderInfo.setProductId((long)(i + 2));
            new Thread(()->{
                try {
                    orderInfoService.saveOrder(orderInfo);
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            }).start();
        }
//        UserInfo userInfo = userInfoDao.lambdaQuery().eq(UserInfo::getUid, 1).one();
//        System.out.println("userInfo:" + JsonUtils.silentObject2String(userInfo));
        Thread.sleep(10000000);
    }

当我们启动工程①之后,正常输出九次,按照代码即占用九个连接,此时我们启动工程②,会发现代码③的first与second永远只会正常输出一个。另外一个永远会报"Too many connections"。


经过实验①和②得出,MySQL的最大连接数是对整个数据库的,并且只对客户端连接生效,并不是针对某个表,也不是针对某个database的。所以为了让不同领域的表不互相影响,必须进行物理隔离,即在不同的物理机上部署不同领域的database以存储不同领域的表结构。

如何避免数据库连接打满

避免数据库连接被打满,我们可以从以下几个方面着手:

  • 避免出现慢SQL。
  • 系统并发数不要超过数据库连接数,如有必要database一定要进行物理隔离。
  • 代码中避免出现死循环或Thread.sleep等代码,避免获取数据库连接不释放。

总结

以上就是关于数据库连接数维度的分析与实践,如果我们有数据库连接被打满的风险,使用不同的database是解决不了根本问题的,我们只有使用真正的物理隔离,即不同的库在不同的物理机上,使用不同的物理连接才能保证系统高可用。如果各位同学觉得对你有所帮助,请关注、点赞、评论、收藏来支持我,手头宽裕的话也可以赞赏来表达各位的认可,各位同学的支持是对我最大的鼓励。未来为大家带来更好的创作。 


分享一句非常喜欢的话:把根牢牢扎深,再等春风一来,便会春暖花开。

版权声明:以上引用信息以及图片均来自网络公开信息,如有侵权,请留言或联系

504401503@qq.com,立马删除。

  • 33
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖啡攻城狮Alex

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值