您当前的位置:首页 > 电脑百科 > 数据库 > MYSQL

聊聊业务项目如何主动感知mysql是否存活

时间:2023-01-04 16:10:28  来源:今日头条  作者:小心程序猿QAQ

前言

先前写过一篇文章聊聊如何利用redis实现多级缓存同步,里面讲到业务部门因数据库宕机,有技术提出当数据库宕机,切换到redis,今天我们就来聊聊如何触发这个切换动作?

1、方案一:利用异常机制

伪代码如下:

 

首先这个方案是不可行的,因为每次请求,还是先走到数据库逻辑,然后等抛出异常,这个时间会挺长的,业务上是无法接受的

2、方案二:主动进行MySQL探活

实现思路: 可以利用数据库连接池检测有效连接的思路

实现方案

1、方式一:利用druid连接池的ValidConnectionChecker进行扩展

核心逻辑如下

@Slf4j
public class MysqlConnectionCheck extends MySqlValidConnectionChecker {

    private ApplicationContext applicationContext;

    public MysqlConnectionCheck(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }


    @Override
    public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
      return checkMySQLCommunications(conn,validateQuery,validationQueryTimeout);

    }

    private boolean checkMySQLCommunications (Connection conn, String validateQuery, int validationQueryTimeout) {
        boolean validConnection = false;
        try {
            validConnection = super.isValidConnection(conn, validateQuery, validationQueryTimeout);
        } catch (Exception e) {
        }
        if(validConnection){
            boolean b = MySQLCommunicationsHolder.isMySQLCommunicationsException.compareAndSet(true, false);
            if(b){
                CommunicationsHealthEvent event = CommunicationsHealthEvent.builder().conn(conn).build();
                applicationContext.publishEvent(event);
            }
        }


        return validConnection;
    }
}
复制代码

在yml配置我们自定义的检测器

spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: ${DRIVER_CALSS_NAME:com.mysql.cj.jdbc.Driver}
        url: ${DATASOURCE_URL:jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai}
        username: ${DATASOURCE_USERNAME:root}
        password: ${DATASOURCE_PWD:123456}
        druid:
            # 指定连接的有效检查类
            valid-connection-checker-class-name: com.github.lybgeek.db.check.test.user.check.MysqlConnectionCheck 
复制代码

这个方案也是不大行的,后面翻了一下druid源码。当数据库抛出不可恢复的异常时,比如网络抖动,异常断开,druid会触发exceptionSorter,抛弃连接。而CreateConnectionThread会检测是否需要创建连接,如果不需要,他就会进行等待。当连接不够时,会调用

com.alibaba.druid.pool.DruidAbstractDataSource#createPhysicalConnection()
复制代码

进行创建,同时isValidConnection也是在这个方法里面进行连接验证,但这边就有问题,就是当数据库宕机了,就创建不了连接,因此就进入异常流程,isValidConnection是没法执行的

2、方式二:参考druid的检测连接逻辑,额外编写定时器触发检测逻辑

核心代码块:

public class ValidConnectionCheckerAdapter implements ValidConnectionChecker {

    private DbCheckProperies dbCheckProperies;

    public ValidConnectionCheckerAdapter(DbCheckProperies dbCheckProperies) {
        this.dbCheckProperies = dbCheckProperies;
    }

    @Override
    public boolean isValidConnection(Connection conn, String query, int validationQueryTimeout) throws SQLException {
        boolean valid = checkConnection(conn, query, validationQueryTimeout);

        // unexcepted branch
        if (valid && isMysql()) {
            long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
            if (lastPacketReceivedTimeMs > 0) {
                long mysqlIdleMillis = System.currentTimeMillis() - lastPacketReceivedTimeMs;
                if (lastPacketReceivedTimeMs > 0
                        && mysqlIdleMillis >= dbCheckProperies.getTimeBetweenEvictionRunsMillis()) {
                    return false;
                }
            }
        }
        return valid;

    }


    private boolean checkConnection(Connection conn, String query, int validationQueryTimeout) throws SQLException {
        if (query == null || query.length() == 0) {
            return true;
        }

        if(conn == null){
            return false;
        }

        Statement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.createStatement();
            if (validationQueryTimeout > 0) {
                stmt.setQueryTimeout(validationQueryTimeout);
            }
            rs = stmt.executeQuery(query);
            return true;
        } finally {
            JdbcUtils.close(rs);
            JdbcUtils.close(stmt);
        }
    }



复制代码
 @Override
    public void afterPropertiesSet() throws Exception {
        executorService.scheduleWithFixedDelay(new DbCheckTask(),0,dbCheckProperies.getTimeBetweenEvictionRunsMillis(), TimeUnit.MILLISECONDS);
    }
    

    private class DbCheckTask implements Runnable{

        @Override
        public void run() {
            SQLException sqlException = null;
            Connection conn = dbConnManger.getConn();
            try {
                boolean validConnection = validConnectionChecker.isValidConnection(conn, dbCheckProperies.getValidationQuery(), dbCheckProperies.getValidationQueryTimeout());
                if(validConnection){
                    boolean b = MySQLCommunicationsHolder.isMySQLCommunicationsException.compareAndSet(true, false);
                    if(b){
                        CommunicationsHealthEvent event = CommunicationsHealthEvent.builder().conn(conn).build();
                        applicationContext.publishEvent(event);
                    }
                }else{
                   sqlException = new SQLException("connection is invalid","10040");
                }
            } catch (SQLException e) {
                 log.error("{}",e);
                 sqlException = e;
                 dbConnManger.closeConnection();
                 conn = null;
            }

            if(sqlException != null){
                MySQLCommunicationsHolder.isMySQLCommunicationsException.compareAndSet(false, true);
                CommunicationsUnHealthEvent event = CommunicationsUnHealthEvent.builder().sqlException(sqlException).build();
                applicationContext.publishEvent(event);
            }



        }
    } 
复制代码

总结

其实mysql的探活实现方式有很多种,本文的实现检测逻辑是直接套用druid的检测连接逻辑,之前对druid的使用,基本上就是停留在配置上,没过多关注。

为了写这篇文章,特地翻了一下druid的源码,主要是因为之前以为扩展ValidConnectionChecker就行了,后面发现行不通。就看了一下源码,发现druid的设计思路挺好的,有些实现思想是我们在日常开发中,可以借鉴使用的。还有druid里面有些跟数据库相关的util,也是可以直接拿过来用的。


原文链接:
https://juejin.cn/post/7184240942918926396



Tags:mysql   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
我们在操作数据库的时候,锁的冲突是影响数据库并发访问性能的一个非常重要因素,从这一角度来说,锁对于数据库而言就显得尤为重要。今天分享MySQL相关的最全锁@mikechen01MySQ...【详细内容】
2023-01-05  Tags: mysql  点击:(0)  评论:(0)  加入收藏
前言先前写过一篇文章聊聊如何利用redis实现多级缓存同步,里面讲到业务部门因数据库宕机,有技术提出当数据库宕机,切换到redis,今天我们就来聊聊如何触发这个切换动作?1、方案一...【详细内容】
2023-01-04  Tags: mysql  点击:(0)  评论:(0)  加入收藏
历史数据迁移 项目地址:https://gitee.com/xl-echo/dataMigration历史迁移解决方案。微服务的架构为基础,使用多种设计模式,如:单利、桥接、工厂、模板、策略等。其中涉及的核心...【详细内容】
2023-01-04  Tags: mysql  点击:(7)  评论:(0)  加入收藏
mysql锁可能是数据库知识篇幅中普遍比较难理解的一个知识点!以前对锁理解的也是停留在八股文的的阶段,经历了这次生产问题之后重新学习了问题表现:早上刚到公司还没进入状态,就...【详细内容】
2023-01-03  Tags: mysql  点击:(2)  评论:(0)  加入收藏
今天在工作中写项目的时候,遇到了一个让我感到几乎无解的问题,在转换了思路后,想出了一个折中的解决方案,记录如下。其实,问题的场景,非常简单: 就是需要查询出上图的数据,红框是从...【详细内容】
2022-12-24  Tags: mysql  点击:(16)  评论:(0)  加入收藏
这篇文章主要介绍了navicat打开sql的方法,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获。下面让小编带着大家一起了解一下。navicat怎么打开sql...【详细内容】
2022-12-22  Tags: mysql  点击:(0)  评论:(0)  加入收藏
最近,某同事在生产上遇到一个 mysql 死锁的问题,于是在帮忙解决问题后,特意花了一周的时间,把 MySQL 所有的锁都整理了一遍,今天就来一起聊聊 MySQL锁。 申明:本文基于 MySQL 8.0....【详细内容】
2022-12-18  Tags: mysql  点击:(13)  评论:(0)  加入收藏
Insert Intention Lock,中文我们也称之为插入意向锁。这个可以算是对我们之前所讲的 Gap Lock 的一个补充,关于 Gap Lock,如果还有小伙伴不懂,可以参考:记录锁、间隙锁与 Next-Ke...【详细内容】
2022-12-18  Tags: mysql  点击:(23)  评论:(0)  加入收藏
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不...【详细内容】
2022-11-30  Tags: mysql  点击:(0)  评论:(0)  加入收藏
Adminer (以前叫phpMinAdmin),是一个用 PHP 编写的前端数据库管理工具。与 phpMyAdmin 不同的是,它只包含一个 PHP 文件,可以在安装 Adminer 的目标服务器上下载。与 phpMyAdmi...【详细内容】
2022-11-23  Tags: mysql  点击:(42)  评论:(0)  加入收藏
▌简易百科推荐
我们在操作数据库的时候,锁的冲突是影响数据库并发访问性能的一个非常重要因素,从这一角度来说,锁对于数据库而言就显得尤为重要。今天分享MySQL相关的最全锁@mikechen01MySQ...【详细内容】
2023-01-05  互联网资讯看板     Tags:MySQL锁   点击:(0)  评论:(0)  加入收藏
前言先前写过一篇文章聊聊如何利用redis实现多级缓存同步,里面讲到业务部门因数据库宕机,有技术提出当数据库宕机,切换到redis,今天我们就来聊聊如何触发这个切换动作?1、方案一...【详细内容】
2023-01-04  小心程序猿QAQ  今日头条  Tags:mysql   点击:(0)  评论:(0)  加入收藏
历史数据迁移 项目地址:https://gitee.com/xl-echo/dataMigration历史迁移解决方案。微服务的架构为基础,使用多种设计模式,如:单利、桥接、工厂、模板、策略等。其中涉及的核心...【详细内容】
2023-01-04  Java机械师  今日头条  Tags:MySQL   点击:(7)  评论:(0)  加入收藏
mysql锁可能是数据库知识篇幅中普遍比较难理解的一个知识点!以前对锁理解的也是停留在八股文的的阶段,经历了这次生产问题之后重新学习了问题表现:早上刚到公司还没进入状态,就...【详细内容】
2023-01-03  打篮球的程序员  今日头条  Tags:mysql锁   点击:(2)  评论:(0)  加入收藏
今天在工作中写项目的时候,遇到了一个让我感到几乎无解的问题,在转换了思路后,想出了一个折中的解决方案,记录如下。其实,问题的场景,非常简单: 就是需要查询出上图的数据,红框是从...【详细内容】
2022-12-24  Java架构师修炼之道  今日头条  Tags:MySQL   点击:(16)  评论:(0)  加入收藏
这篇文章主要介绍了navicat打开sql的方法,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获。下面让小编带着大家一起了解一下。navicat怎么打开sql...【详细内容】
2022-12-22  崔永亮  搜狐号  Tags:mysql   点击:(0)  评论:(0)  加入收藏
最近,某同事在生产上遇到一个 mysql 死锁的问题,于是在帮忙解决问题后,特意花了一周的时间,把 MySQL 所有的锁都整理了一遍,今天就来一起聊聊 MySQL锁。 申明:本文基于 MySQL 8.0....【详细内容】
2022-12-18  猿Java  知乎  Tags:MySQL   点击:(13)  评论:(0)  加入收藏
Insert Intention Lock,中文我们也称之为插入意向锁。这个可以算是对我们之前所讲的 Gap Lock 的一个补充,关于 Gap Lock,如果还有小伙伴不懂,可以参考:记录锁、间隙锁与 Next-Ke...【详细内容】
2022-12-18  江南一点雨  今日头条  Tags:MySQL   点击:(23)  评论:(0)  加入收藏
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不...【详细内容】
2022-11-30  京东云企业管家    Tags:mysql   点击:(0)  评论:(0)  加入收藏
Adminer (以前叫phpMinAdmin),是一个用 PHP 编写的前端数据库管理工具。与 phpMyAdmin 不同的是,它只包含一个 PHP 文件,可以在安装 Adminer 的目标服务器上下载。与 phpMyAdmi...【详细内容】
2022-11-23  程序猿阿嘴  今日头条  Tags:MySQL   点击:(42)  评论:(0)  加入收藏
站内最新
站内热门
站内头条