• 缓存一致性问题(七)


    前面已经在代码中把缓存的增删改操作写好,但是这样写法有一个问题,缓存变更的触发时机是在Mapper方法被调用时才更新的,这个时候问题就出来了,问题就是我们现在把缓存操作都是植入在我们业务逻辑当中的,这个操作导致了耦合性太强,从设计层面来讲其实不太推荐使用这个方法。而且还有一个问题,如果有天业务发生变更,这块业务不需要再存缓存了,那你是不是还得改代码。如果别的业务操作会影响到这一张表数据变动,那也需要对缓存进行处理。再一个不是很可能的情况,如果有人操作机房手动删除了一条数据呢。这就导致了数据一至性的问题。

    一、Canal

    阿里巴巴的技术解决方案Canal,通过Canal监听数据库变更,并实时消费变更数据,并更新缓存。主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费;网址:https://github.com/alibaba/canal早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。基于日志增量订阅和消费的业务包括
    • 数据库镜像
    • 数据库实时备份
    • 索引构建和实时维护(拆分异构索引、倒排索引等)
    • 业务 cache 刷新
    • 带业务逻辑的增量数据处理

    二、Canal原理讲解

    canal的原理是基于MYSQL的主从复制,这个东西我在之前的MYSQL文章中有写到过,这里就不再详细说明了。
    MySQL主备复制原理
    • MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
    • MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
    • MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
    Canal 工作原理
    • canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump协议
    • MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
    • canal 解析 binary log 对象(原始为 byte 流)

    三、canal安装

      1、MySQL开启binlog,对于MySQL , 需要先开启 Binlog 写入功能,配置 binlog-format 为 ROW 模式,my.cnf 中配置如下

    docker exec -it mysql /bin/bash 
    cd /etc/mysql/mysql.conf.d
    vi mysqld.cnf
    在最文件尾部添加如下配置:
    log-bin=mysql-bin # 开启 binlog 
    binlog-format=ROW # 选择 ROW 模式 
    server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
    上面配置完后记得重启下,重启命令是
    docker  restart mysql

    2、授权 canal 连接 MySQL 账号具有作为 MySQL slave 的权限, 如果已有账户可直接 grant:

    CREATE USER canal IDENTIFIED BY 'canal';
    GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; 
    -- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
    FLUSH PRIVILEGES;

    3、重启mysql容器

    docker restart canal

    4、查看是否开启binlog:如果查出的是ON代表开启了

    show variables like 'log_bin';

    5、Canal安装

    docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server

    6、进入容器,修改核心配置canal.properties 和instance.properties,canal.properties 是canal自身的配置,instance.properties是需要同步数据的数据库连接配置。

    进入canal.properties文件中,如下图添加canal.id=123,123是一个唯一标识,可以随便取

     改完这块就进入instance.properties

     将文件中的canal.instance.master.address后面的ip改成自己数据库的IP,这块内容意思是连接的数据库

    canal.instance.master.address=127.0.0.1:3306

    数据库连接上了下面就是要改需要监听的表了,shop_goods表示是数据库名,ad_items是指表名
    #监听配置
    canal.instance.filter.regex=shop_goods.ad_items

    配置文件中其它配置解说:

    # table regex 
    #这一句意思是监听任意数据库任意张表 #canal.instance.filter.regex
    =.*\..*
    配置完成后,重启 canal 容器
    docker restart canal

    四、缓存同步的实现

     有了上面的知识点,下面在spring-cloud-service服务下建立子服务spring-cloud-canal-service来实现缓存同步

    POM文件导的包

     <dependencies>
            <!--springboot-canal快速构建依赖包-->
            <dependency>
                <groupId>top.javatool</groupId>
                <artifactId>canal-spring-boot-starter</artifactId>
                <version>1.2.1-RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>com.ghy</groupId>
                <artifactId>spring-cloud-goods-api</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
        </dependencies>

    yml配置

    server:
      port: 8083
    spring:
      application:
        name: spring-cloud-canal-service
      cloud:
        nacos:
          config:
            file-extension: yaml
            server-addr: 192.168.32.135:8848
          discovery:
            #Nacos的注册地址
            server-addr: 192.168.32.135:8848
    #Canal配置
    canal:
      server: 192.168.32.135:11111
      destination: example
    #日志配置
    logging:
      pattern:
        console: "%msg%n"
      level:
        root: error
    创建监听类,实现EntryHandler后当有人操作数据库改变数据后就可以马上收到修改数据,这里面调用了SkuFeign是因为数据发生改动后要通知更改缓存内容
    @Component//交给容器管理
    @CanalTable(value = "ad_items")//需要监听的表
    public class Monitor implements EntryHandler<AdItems> {
    
        @Resource
        private SkuFeign skuFeign;
    
        /****
         * 数据库增加数据,执行该方法
         * @param adItems
         */
        @Override
        public void insert(AdItems adItems) {
            //重新加载缓存
            skuFeign.updateTypeItems(adItems.getType());
        }
    
        /****
         * 数据库修改数据,执行该方法
         */
        @Override
        public void update(AdItems before, AdItems after) {
            if(before.getType().intValue()!=after.getType().intValue()){
                //重新加载变更前分类的ID对应的推广产品
                skuFeign.updateTypeItems(before.getType());
            }
    
            //重新加载缓存
            skuFeign.updateTypeItems(after.getType());
        }
    
        /****
         * 数据库删除数据,执行该方法
         */
        @Override
        public void delete(AdItems adItems) {
            skuFeign.delTypeItems(adItems.getType());
        }
    }

     然后我人为在数据库插入一条数据,后台控制台就可以马上收到消息

      git源码:https://gitee.com/TongHuaShuShuoWoDeJieJu/spring-cloud-alibaba1.git

    这短短的一生我们最终都会失去,不妨大胆一点,爱一个人,攀一座山,追一个梦
  • 相关阅读:
    Kubernetes 架构(上)【转】
    部署 k8s Cluster(下)【转】
    部署 k8s Cluster(上)[转]
    k8s 重要概念[转]
    k8s 核心功能[转]
    5 秒创建 k8s 集群[转]
    内置函数——format
    基础数据类型(set集合)
    Oracle 传参错误
    .NET参数化Oracle查询参数
  • 原文地址:https://www.cnblogs.com/xing1/p/14939564.html
Copyright © 2020-2023  润新知