缓存方案主要用于缓存用户定义的热点数据,用户可以通过缓存来获取数据,从而降低数据库的压力。
内存访问速度远大于磁盘访问速度,mysql缓存的数据跟业务无关,缓存的是最近操作的数据。mysql作为项目的主要数据库,缓存数据库只是辅助功能。
缓存一致性
- mysql有,redis无,正常
- mysql无,redis有,不正常
- 都有,数据不一致,不正常
- 都有,数据一致,正常
- 都没有,正常
读写策略
- 读策略:先读缓存,缓存存在直接返回,缓存不存在,去访问Mysql获取,若存在,再写缓存
- 写策略:
- 安全:先删除缓存中数据,再写mysql,再同步到redis.这样mysql的压力会大
- 效率:先写缓存,设置过期时间(防止mysql写失败),再写mysql,最后将mysql中的数据同步到redis中
同步方案
- 触发器 + UDF(user defined function,c++扩展代码),每次表变更都需要与redis重新建立连接
go-mysql-transfer、canal(java,考虑分布式))伪装成mysql的从数据库,bin log->i/o thread->relay log,既要连接mysql,也要连接redis
vim /etc/mysql/my.cnf1
2
3
4[mysqld]
log-bin = mysql-bin
binlog-format = ROW
server_id = 1 # 主数据库id1
2
3
4
5
6
7
8
9
10
11CREATE TABLE `user` {
`id` BIGINT,
`nick` VARCHAR(100),
`height` INT8,
`sex` VARCHAR(1),
`age` INT8,
} ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user` values(1001, 'nephen', 180, 1, 20);
update `user` set age = age + 1 WHERE id = 1001
delete from `user` where id = 1001;
show master status;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# mysql配置
addr: 192.168.130.120:3306
user: root
pass: 123456
charset: utf8
slave_id: 1001
flavor: mysql
# 规则配置
rules:
schema: test # 数据库名
table: user # 表名
order_by_column: id # 排序字段
column_underscore_to_camel: true # 列名称下划线转驼峰
lua_file_path: lua/t_user.lua1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21local ops = require("redisOps")
local row = ops.rawRow()
local action = ops.rawAction()
if action == "insert" or action == "update" then
local id = row["id"]
local key = "user:" .. id
local name = row["nick"]
local sex = row["sex"]
local height = row["height"]
local age = row["age"]
ops.HSET(key, "id", id)
ops.HSET(key, "nick", name)
ops.HSET(key, "sex", sex)
ops.HSET(key, "height", height)
ops.HSET(key, "age", age)
elseif action == "delete" then
local id = row["id"]
local key = "user:" .. id
ops.DEL(key)
end1
2keys *
hgetall user:1001
异常处理
以效率为主的读写策略时。
- 缓存穿透,mysql和redis都没有该数据,用户又一直读取不存在的数据,造成mysql访问吞吐量降低。
- 在redis中设置key为nil,标记不存在的数据,以后访问该key不需要去访问mysql,少量数据可以使用这种方法。
- reids中部署布隆过滤器,可以确定某个key一定不存在,不要放在server,因为可能有多个server
- 缓存击穿,redis没有,mysql有,此时有大量并发请求,造成mysql访问吞吐量降低
- 过热的数据(这个数据不被删除)不过期
- 分布式锁,【先读缓存,缓存存在直接返回,缓存不存在,去访问Mysql获取,若存在,再写缓存】加锁,会影响并发性能
- 缓存雪崩,大量缓存数据集中失效(过期时间没有错开),但是mysql数据存在
- 间隔设置过期时间
- 重启时,预先导入数据到缓存(预热)
弊端
- 不支持多语句事务, 同一个key必须经过同一个连接,hash(key) % n选择固定的连接,避免多线程
- 不支持回滚