redis缓存双写一致性问题:
缓存双写一致性的理解
-
redis中有数据 需要和数据库的值相同
-
redis中无数据 数据库的值要是最新的,需要回写redis
-
缓存细分两种 只读缓存和
-
读写缓存 1.同步直写策略 写数据库后也同步写redis缓存,缓存和数据库中的数据一致(会对性能有一定的影响)
-
2.异步缓写策略 mysql数据变动,业务允许一段时间后作用于redis,异常情况出现的话,需要人工修补,或者借助kafka,rabbitMQ等消息中间件实现重写
-
双检加锁策略
-
数据库和缓存一致性的几种策略
双检加锁策略:
使用场景 redis中无数据时需要去mysql查数据,查到之后回写到redis,多线程会导致,一个线程正在回写,另一个线程也去查mysql然后也回写数据
从而mysql运动量飙升,可能打爆mysql 也就是 缓存击穿,所以再第一时间redis查不到数据时,就要立刻加锁,加锁之后再查一次,从而保证单线程操作回写,
这样其他线程拿到锁进来的时候就可以在redis查到值,从而跳过步骤
延时双删策略:
更新操作在更新mysql之前删一次redis数据,更新之后再删一次redis数据然后再回写最新的数据到redis
数据库和缓存一致性的几种策略:
- 可以停止redis的情况下:挂牌报错,系统维护
- 先更新mysql再更新redis
- 先更新redis后更新数据库 同上错误
- 先删除缓存,再更新数据库 延时双删策略:
- 先更新数据库再删除redis再回写redis (建议选择)
- 如何保证数据的最终一致性? redis和mysql不可能百分之百一致,但可以通过binlog记录数据库更新操作,然后将数数据写入mq,然后如果后续操作缓存失败可以读取mq进行反复操作,从而保证数据一致性
如何搭建缓存双写一致性的工程?
canal工作原理:
canal模拟 MySQL slave的交互协议,伪装自己为MySQL slave,
向MySQLmaster发送dump协议 MySQL master 收到dump请求,
开始推送binarylog给slave(即canal) canal 解析 binary log 对象(原始为byte流)
Canal是一个开源的数据库复制和实时数据捕获(CDC)解决方案,用于与MySQL一起使用。它可以捕获MySQL数据库的变更日志,并将其传送给其他位置或系统进行进一步处理。
使用Canal可以实现以下功能:
-
数据同步和数据集成:Canal可以将MySQL数据库的变更日志传送到其他位置或系统,以实时保持数据的同步和一致性。这对于构建数据仓库、数据分析、缓存刷新等场景非常有用。
-
实时数据监控和分析:Canal可以订阅MySQL数据库的变更事件,并将其传送给实时数据分析或监控系统。这样可以实时监测数据库的变化,并及时做出相应的处理。
-
数据库解耦和架构升级:通过使用Canal,可以将数据库的写操作与读操作进行解耦,从而实现更高的扩展性和可用性。同时,在进行数据库架构升级时,可以先将变更日志传送给新的数据库实例,以确保数据的一致性和平滑过渡。
-
数据库镜像和备份:Canal可以实时复制MySQL数据库的变更日志,并将其传送给其他数据库实例,从而实现数据库的镜像和备份。这对于容灾和故障恢复非常重要。
总之,Canal提供了一种可靠而高效的方式来捕获和传送MySQL数据库的变更日志,为数据同步、实时监控和分析、解耦架构等场景提供了支持。
配置mysql:
[mysqld]
log-bin=mysql-bin #开启 binlog
binlog-format=ROW #选择ROW模式
server_id=1 #配置MySQL replaction,不要和canal的 slaveId重复
启动mysql:
docker run -d -p 3306:3306 --privileged=true \
-v /xtstudy/mysql/log:/var/log/mysql \
-v /xtstudy/mysql/data:/var/lib/mysql \
-v /xtstudy/mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql mysql:5.7
通过以上配置,MySQL 将使用指定的字符集和校对规则运行。
-
将容器内部的MySQL服务端口(3306)映射到主机的同一端口(3306)上。
-
将主机上的目录 /xtstudy/mysql/log 映射到容器内部的 MySQL 日志目录 /var/log/mysql。
-
将主机上的目录 /xtstudy/mysql/data 映射到容器内部的 MySQL 数据目录 /var/lib/mysql。
-
将主机上的目录 /xtstudy/mysql/conf 映射到容器内部的 MySQL 配置文件目录 /etc/mysql/conf.d。
-
设置 MySQL 的 root 用户密码为 "123456"。
mysql> CREATE USER 'canal'@'%' IDENTIFIED BY '123456';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON . TO 'canal'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.01 sec)
mysql> SHOW GRANTS FOR 'canal'@'%';
启动canal:
docker run -p 11111:11111 --name canal -d canal/canal-server:v1.1.6
创建canal宿主机挂载目录
mkdir -p /docker/canal/conf
查看docker运行情况,复制容器id
docker ps
拷贝配置文件
docker cp canal:/home/admin/canal-server/conf/example/instance.properties /docker/canal/conf
这里密码不是canal是123456 CREATE USER 'canal'@'%' IDENTIFIED BY '123456';
删除容器重启canal
docker rm -f canal
docker run -p 11111:11111 --name canal -v /docker/canal/conf/instance.properties:/home/admin/canal-server/conf/example/instance.properties -v/docker/canal/logs/:/home/admin/canal-server/logs/example/ -d canal/canal-server:v1.1.6
-- 创建user表
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
age INT,
email VARCHAR(100) UNIQUE,
address VARCHAR(255)
);
-- 插入示例值
INSERT INTO user (username, age, email, address) VALUES
('张三', 25, 'zhangsan@example.com', '北京市朝阳区'),
('李四', 30, 'lisi@example.com', '上海市浦东新区'),
('王五', 28, 'wangwu@example.com', '广州市天河区');
手动修改数据库,查看canal监听效果,控制台打印监听:
通过id查询redis回写成功
java代码可以参考canal官网,也可以私信博主!!