1 缓存一致性
Redis缓存与Mysql数据库的一致性问题解决:https://blog.csdn.net/ZGL_cyy/article/details/112065489
用户每次抢完红包,要查看自己抢红包记录,此时需要查询数据库表 money_log ,如果每次都查询 money_log 就会占用大量数据库资源。此时我们应该将数据存储到缓存中,每次查询直接从缓存获取即可。
但现在面临的问题是如果用户抢到了不同的红包,缓存没法及时更新,因此我们需要实现抢红包数据库数据和Redis缓存中的数据同步。
2 缓存一致性解决方案
用户每次操作数据库的时候,使用Canal监听数据库指定表的增量变化,在Java程序中消费Canal监听到的增量变化,并在Java程序中实现对Redis缓存或者Nginx缓存的更新。
用户查询的时候,先通过Lua查询Nginx的缓存,如果Nginx缓存没有数据,则查询Redis缓存,Redis缓存如果也没有数据,可以去数据库查询。
3 Canal介绍
基础知识:
java利用canal监听数据库
https://blog.csdn.net/ZGL_cyy/article/details/116563680
大数据同步工具Canal
https://blog.csdn.net/ZGL_cyy/article/details/114799026
Canal主要用途是基于 MySQL 数据库增量日志解析,并能提供增量数据订阅和消费,应用场景十分丰富。
github地址:https://github.com/alibaba/canal
版本下载地址:https://github.com/alibaba/canal/releases
文档地址:https://github.com/alibaba/canal/wiki/Docker-QuickStart
3.1 Canal应用场景
1.电商场景下商品实时更新同步到至Elasticsearch、solr等搜索引擎;
2.价格、库存发生变更实时同步到redis;
3.数据库异地备份、数据同步;
4.代替使用轮询数据库方式来监控数据库变更,有效改善轮询耗费数据库资源。
3.2 MySQL主从复制原理
MySQL master 将数据变更写入二进制日志( binary log , 其中记录叫做二进制日志事件 binary log events ,
可以通过 show binlog events 进行查看) 2. MySQL slave 将 master 的 binary log events 拷贝到它的中继日
志( relay log ) 3. MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
3.3 Canal工作原理
1.canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议 2. MySQL
master 收到 dump 请求,开始推送 binary log 给 slave (即 canal ) 3.canal 解析 binary log 对象(原始为 byte
流)
3.4 Canal配置
1)开启MySQL的bin-log
开启MySQL的binlog日志功能:
cd /etc/mysql/mysql.conf.d 在mysqld.cnf最下面添加如下配置 # 开启 binlog log-bin=/var/lib/mysql/mysql-bin # 选择 ROW 模式 binlog-format=ROW # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复 server-id=12345
2)Canal安装
这里采用容器安装
docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server
配置CanalServer
修改 /home/admin/canal-server/conf/canal.properties ,将它的id属性修改成和mysql数据库中server-id不同的
值,如下图:
修改 /home/admin/canal-server/conf/example/instance.properties ,配置要监听的数据库服务地址和监听数
据变化的数据库以及表,修改如下:
指定监听数据库表的配置如下 canal.instance.filter.regex :
mysql 数据解析关注的表,Perl正则表达式.
多个正则之间以逗号(,)分隔,转义符需要双斜杠(\)
常见例子:
- 所有表:.* or .\…
- canal schema下所有表: canal\…*
- canal下的以canal打头的表:canal\.canal.*
- canal schema下的一张表:canal.test1
- 多个规则组合使用:canal\…*,mysql.test1,mysql.test2 (逗号分隔)
注意:此过滤条件只针对row模式的数据有效(ps. mixed/statement因为不解析sql,所以无法准确提取tableName进行
过滤)
重启canal:
docker restart canal
不要忘了MySQL创建账号并授权:
create user canal@'%' IDENTIFIED by 'canal'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%'; FLUSH PRIVILEGES;
5 同步更新Redis缓存
修改 com.oldlu.service.impl.MoneyLogServiceImpl ,添加方法 list(String username) ,代码如下:
创建类 com.oldlu.canal.MoneyLogSync ,实现消费Canal中监听到的增量数据,代码如下:
@CanalTable(value = "money_log") @Component public class MoneyLogSync implements EntryHandler<MoneyLog> { @Autowired private MoneyLogService moneyLogService; @Autowired private RedisTemplate redisTemplate; /*** * 数据增加变更 * @param moneyLog */ @Override public void insert(MoneyLog moneyLog) { //查询用户的抢红包列表 List<MoneyLog> moneyLogs = moneyLogService.list(moneyLog.getUsername()); //将数据存入到Redis redisTemplate.boundHashOps("UserMoneyLog").put(moneyLog.getUsername(),moneyLogs); } }
application.yml中配置Canal的地址:
#Canal配置 canal: server: 192.168.211.141:11111 destination: example
关于微服务中如何消费Canal监听到的数据,参考参考地址: https://github.com/NormanGyllenhaal/canal-client
我们可以测试实现抢单,抢单后,数据会自动同步到Redis缓存中。