Canal解决select count(*)执行慢的问题

 前言

count 的常用方式,使用 count(*)来统计数据条数,但是 innodb 没有存储数据总数,所以执行起来就会很慢。

  1. 可以使用 expalin sql 来返回预估行数,expalin select count(*)....., 通过预估的方式,统计数据条数。
  2. 可以使用 redis 存储记录总数,或者一个额外的表来存储数据条数。

用了 Redis 来维持总数,那么就会涉及数据一致性的问题:

1. 如果数据短时间不一致但是业务可以接受的话,那么就可以考虑异步刷新 Redis 上的总数。

2. 使用 Canal 之类的工具监听 MySQL binlog,然后刷新 Redis 上的总数。

 这篇文章就来说说Canal

Canal的应用 

使用 canal 工具监听 MySQL binlog 并实时刷新 Redis 是一个常见的需求,特别是在需要将数据库变化同步到缓存系统时。以下是详细的步骤和相关代码示例来实现这一目标。 

1. 安装 Canal

Canal 是阿里巴巴开源的一个高性能 MySQL binlog 增量订阅 & 消费组件。首先,需要下载并安装 Canal。

安装步骤:
  • 访问 Canal 官方 GitHub 仓库: GitHub - alibaba/canal: 阿里巴巴 MySQL binlog 增量订阅&消费组件
  • 下载并解压 Canal Server。
  • 修改 canal.propertiesinstance.properties 配置文件,根据你的 MySQL 配置进行修改。

2. 配置 Canal

instance.properties 文件中,设置 MySQL 连接信息,例如:

canal.instance.mysql.slaveId=1234
canal.instance.master.address=127.0.0.1:3306
canal.instance.dbUsername=root
canal.instance.dbPassword=your_password
canal.instance.connectionCharset=UTF-8
canal.instance.defaultDatabaseName=test_db
canal.instance.filter.regex=test_db\\..*

3. 启动 Canal Server

在命令行中,进入 Canal 目录并启动 Canal Server:

sh bin/startup.sh

4. 编写 Canal Client

Canal 提供了 Java 客户端 API,用于消费 binlog 数据。你需要编写一个 Canal Client 来监听 binlog 并将变化的数据写入 Redis。

引入依赖

在你的 pom.xml 文件中添加 Canal 和 Redis 的依赖:

<dependencies>
    <dependency>
        <groupId>com.alibaba.otter</groupId>
        <artifactId>canal.client</artifactId>
        <version>1.1.5</version>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>
编写 Canal Client 代码

以下是一个简单的 Canal Client 示例,它会将数据库变更同步到 Redis:

import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import redis.clients.jedis.Jedis;

import java.net.InetSocketAddress;
import java.util.List;

public class CanalClient {

    public static void main(String[] args) {
        // 创建 Canal 连接
        CanalConnector connector = CanalConnectors.newSingleConnector(
                new InetSocketAddress("127.0.0.1", 11111), "example", "", "");

        try (Jedis jedis = new Jedis("localhost", 6379)) {
            connector.connect();
            connector.subscribe(".*\\..*");
            connector.rollback();

            while (true) {
                Message message = connector.getWithoutAck(100);
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId != -1 && size > 0) {
                    processEntries(message.getEntries(), jedis);
                }
                connector.ack(batchId);
            }
        } finally {
            connector.disconnect();
        }
    }

    private static void processEntries(List<CanalEntry.Entry> entries, Jedis jedis) {
        for (CanalEntry.Entry entry : entries) {
            if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) {
                try {
                    CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
                    CanalEntry.EventType eventType = rowChange.getEventType();
                    for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
                        if (eventType == CanalEntry.EventType.DELETE) {
                            handleDelete(rowData.getBeforeColumnsList(), jedis);
                        } else if (eventType == CanalEntry.EventType.INSERT) {
                            handleInsert(rowData.getAfterColumnsList(), jedis);
                        } else {
                            handleUpdate(rowData.getBeforeColumnsList(), rowData.getAfterColumnsList(), jedis);
                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException("ERROR: " + entry.toString(), e);
                }
            }
        }
    }

    private static void handleInsert(List<CanalEntry.Column> columns, Jedis jedis) {
        // Example: Save the inserted row to Redis
        String key = null;
        for (CanalEntry.Column column : columns) {
            if ("id".equals(column.getName())) {
                key = "user:" + column.getValue();
                break;
            }
        }
        if (key != null) {
            for (CanalEntry.Column column : columns) {
                jedis.hset(key, column.getName(), column.getValue());
            }
        }
    }

    private static void handleDelete(List<CanalEntry.Column> columns, Jedis jedis) {
        // Example: Delete the row from Redis
        String key = null;
        for (CanalEntry.Column column : columns) {
            if ("id".equals(column.getName())) {
                key = "user:" + column.getValue();
                break;
            }
        }
        if (key != null) {
            jedis.del(key);
        }
    }

    private static void handleUpdate(List<CanalEntry.Column> beforeColumns, List<CanalEntry.Column> afterColumns, Jedis jedis) {
        // Example: Update the row in Redis
        String key = null;
        for (CanalEntry.Column column : afterColumns) {
            if ("id".equals(column.getName())) {
                key = "user:" + column.getValue();
                break;
            }
        }
        if (key != null) {
            for (CanalEntry.Column column : afterColumns) {
                jedis.hset(key, column.getName(), column.getValue());
            }
        }
    }
}

5. 运行 Canal Client

编译并运行 Canal Client,它会监听 MySQL binlog 变更,并将变更实时同步到 Redis。

 

总结

通过上述步骤,你可以实现使用 Canal 工具监听 MySQL binlog,并实时刷新 Redis 的功能。这种方式可以确保数据库和缓存的一致性,适用于高性能、高实时性的数据同步场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/627592.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每日5题Day1 - LeetCode 1-5

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int[] twoSum(int[] nums, int target) {//返回值为Int[]数组&#xff0c;所以先初…

【计算机网络】数据链路层 差错控制 循环冗余码CRC及FCS 习题5

在计算机网络中&#xff0c;关于差错控制&#xff0c;我们主要要比特控制。 比特控制&#xff0c;简单理解&#xff0c;即在传输过程中&#xff0c;0变为1&#xff0c;1变为0。 差错控制主要有两类 自动重传请求ARQ——检错编码 &#xff08;接收方检测出差错&#xff0c;就设…

数字社交的先锋:探索Facebook的未来发展

在当今数字化时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。而在众多社交平台中&#xff0c;Facebook一直处于引领地位&#xff0c;不断探索和创新&#xff0c;塑造着数字社交的未来。本文将深入探讨Facebook作为数字社交的先锋&#xff0c;探索其未来发展…

LabVIEW天然气压缩因子软件设计

LabVIEW天然气压缩因子软件设计 项目背景 天然气作为一种重要的能源&#xff0c;其压缩因子的准确计算对于流量的计量和输送过程的优化具有关键意义。传统的计算方法不仅步骤繁琐&#xff0c;而且难以满足现场快速响应的需求。因此&#xff0c;开发一款既能保证计算精度又便于…

BUUCTF靶场[MISC]wireshark、被嗅探的流量、神秘龙卷风、另一个世界

[misc]wireshark 考点&#xff1a;流量、追踪流 工具&#xff1a;wireshark 先看题目&#xff0c;管理员密码 将下载的文件用wireshark打开&#xff0c;查找flag 点击追踪tcp流&#xff0c;开始挨个查看flag [misc]被嗅探的流量 考点&#xff1a;流量、追踪流 工具&#xf…

010.理解异步性

异步消息传递是响应式系统的一个关键特性。但到底是什么异步性&#xff0c;为什么它对响应式应用程序如此重要?我们的人生注定在许多异步任务中。你可能没有意识到&#xff0c;但你的日常活动如果它们本质上不是异步的&#xff0c;那就太烦人了。要理解什么是异步&#xff0c;…

批量提取指定目录下的所有文件名

::批量提取指定目录下的所有文件名&#xff1a; echo off chcp 65001 > nul setlocal enabledelayedexpansion set "folder目录路径" for /r "%folder%" %%F in (*) do ( set "filename%%~nxF" echo !filename! ) endlocal pause…

ASIL详解

概念 随着汽车新四化的发展&#xff0c;整车E/E系统的复杂性也不断增加&#xff0c;功能安全正成为一种更主流的要求。汽车安全完整性等级&#xff08;ASIL&#xff09;分解为实现更高水平的诊断覆盖度提供了可靠而稳健的途径&#xff0c;并在开发具有更高ASIL等级的安全关键系…

Edge浏览器自动翻译功能按钮不见了

前言&#xff1a; 平时偶尔会用到Edge的页面翻译功能&#xff0c;使用挺方便。突然发现Edge浏览器的翻译功能不见 了。如下图所示&#xff1a; 解决思路&#xff1a; 1、从网上找各种解决方案也没有解决&#xff0c;其中有一个说到点右上角的三个点 2、点击设置…

专业矢量绘图软件Sketch for mac v100中文激活版

Sketch for Mac 是一款专业的矢量图形设计工具&#xff0c;主要用于 UI/UX 设计、网页设计、图标设计等领域。它的界面简洁、易用&#xff0c;功能强大&#xff0c;可以帮助设计师快速创建高质量的设计作品。 Sketch for Mac 可以轻松地创建矢量图形、图标、网页布局、移动应用…

利用关系感知一致性和虚拟特征补偿解决医学分类中的长尾问题

文章目录 Combat Long-Tails in Medical Classification with Relation-Aware Consistency and Virtual Features Compensation摘要方法实验结果 Combat Long-Tails in Medical Classification with Relation-Aware Consistency and Virtual Features Compensation 摘要 由于…

SpringBoot接收参数的19种方式

https://juejin.cn/post/7343243744479625267?share_token6D3AD82C-0404-47A7-949C-CA71F9BC9583

业务系统加固和安全设备加固

业务系统加固 业务系统包含哪些系统? 业务系统漏洞面临的风险 1web风险 2漏洞扫描&#xff0c;端口扫描 3系统漏洞 4逻辑漏洞 5 信息泄露 6拒绝服务 7口令爆破 加固方式&#xff1a; 在风险加上修复 1web漏洞&#xff1a; 包括csrf,xss&#xff0c;口令破解等等 修…

仿C#或Java基础类型自定义

class Int{ private:int _value 0; public:operator int() const{ // 隐式转换return _value;}// 显式转换explicit operator int*() const { return nullptr; }operator(const int page){_value page;}operator float() const{return static_cast<float>(_value);}ope…

如何利用AI生成答辩PPT?笔灵AI答辩PPT,智能识别关键点

很多快要毕业的同学在做答辩PPT的时候总是感觉毫无思路&#xff0c;一窍不通。但这并不是你们的错&#xff0c;对于平时没接触过相关方面&#xff0c;第一次搞答辩PPT的人来说&#xff0c;这是很正常的一件事。一个好的答辩PPT可以根据以下分为以下几部分来写。 1.研究的背景和…

Java后端的字符串相等、集合非空判定

一.字符串相等判定 使用工具类的判定 使用原始方法 在调用equals()之前显式地检查对象是否为null&#xff1a; 将常量字符串放在前面调用equals()&#xff0c;这样即使roleCode为null也不会抛出NullPointerException&#xff1a; 二.集合非空判定 使用工具类 原始&#xff0…

Python-VBA函数之旅-vars函数

目录 一、vars函数的常见应用场景 二、vars函数使用注意事项 三、如何用好vars函数&#xff1f; 1、vars函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a;https://myelsa1024.blog.csdn.net/ 一、vars函数…

数据结构--AVL树

一、什么是AVL树 1、AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查 找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在…

YOLOv5独家改进:backbone改进 | 微软新作StarNet:超强轻量级Backbone | CVPR 2024

💡💡💡创新点:star operation(元素乘法)在无需加宽网络下,将输入映射到高维非线性特征空间的能力,这就是StarNet的核心创新,在紧凑的网络结构和较低的能耗下展示了令人印象深刻的性能和低延迟 💡💡💡如何跟YOLOv5结合:替代YOLOv5的backbone 收录 YOL…

一表捋清网络安全等级保护测评要求

三级网络安全等级保护测评指标&#xff1a; 对于中小企事业单位来说&#xff0c;网络安全建设是一个复杂且投入较高的过程&#xff0c;因此他们更倾向于寻找一种“省心省力”的等保建设方案&#xff0c;以及一种能够持续有效且具有较高性价比的网络安全建设投入方式。 此时&…