mysql缓存方案用来干什么
比如直接和mysql交互会造成mysql很大的压力,现在在mysql前面增加redis充当缓存;也可以把redis替换为memcached;因为mysql做增删改操作的时候必须要落盘,访问redis的效率要远远高于mysql;
mysql中有change buffer缓存,那么为什么还要增加redis缓存中间件呢?因为change buffer缓存针对的是lru淘汰策略,主要是针对最近访问的数据,但是没法缓存用户访问的热点数据;
还有哪些方案可以提升mysql访问性能
读写分离,分担读写压力,因为读的操作较多,所以直接往从数据库中读,写的操作较少,所以直接操作主数据库;
redolog用于事务持久化/确保本地数据一致;
binlog用于数据备份/主从复制,确保主从数据一致;
mysql主从复制中,从数据库会主动和主数据库建立连接,建立连接后会产生log dump thread线程,该线程从binlog读数据并发送到从数据库;从数据库读到binlog后会通过io thread写到relay log中;sql thread线程会从relay log读取日志后进行重放replay,即重新执行sql语句,就可以应用到从数据库;可以看到这是一种异步复制;说明同一时刻主从数据库的数据可能不一致,这是读写分离可能就有问题,可能读到的不是最新数据;所以读写分离保证的是最终一致性,而不是实时一致性;如果要求是强一致性的,即要求确保读到的数据一定是最新的数据,我们可以从主数据库中去读;
连接池
异步连接
缓存方案是怎么解决的?
热点数据放到redis中,服务器读数据的时候从redis中读,如果不是热点数据依然从mysql读;mysql和redis是独立的进程;现在将mysql和redis作为整体看作db;这种情况下可能出现几种状态;
1)mysql有数据,redis中没有;通过同步可以一致;
2)mysql没有数据,redis中有;
3)都有数据·,但是数据不一致;
4)都有数据,且数据一致;
5)都没有;
这里只有2,3情况需要制定读写策略来避免;
读策略,在server层写逻辑,如果是热点数据则从redis中读数据,如果不是热点数据则从mysql中读数据;如果mysql中可以读到数据则同步到redis,如果读不到数据则返回没有;
写策略,一定要确保是4;
1)安全优先;先删除缓存,然后写mysql,接着mysql把数据同步到redis;同一个key必须要走同一个mysql连接,同一个redis连接,通过hash实现;确保同一个key没有并发问题;有一条连接在访问某个key,不可能有另一条连接也在访问这个key;安全优先效率较低;
2)效率优先;先写redis,若是插入和更新,把key设置过期时间,再写mysql,最后mysql同步到redis;过期时间设置为server把数据发送到mysql然后同步到redis这个过程所花的时间;工作当中用到的是效率优先的策略;虽然有安全性的问题,但是只会影响设置的过期时间这段时长;
如何把mysql数据同步到redis?有很多开源方案;
1)触发器+udf,触发器对表进行增删改操作,会触发行为把mysql数据同步到redis中;udf即user define function,我们可以用c/c++实现udf,在实现中与redis建立连接并把数据进行同步;把热点数据表设置触发器,然后在触发器中调用udf,udf与redis建立连接并把数据同步到redis;触发器中有有种数据,一种是new即新增的数据,一种是old即原来的数据,old存储在undolog中;
2)伪装从数据库,一种是阿里开源的canal,一种是go-mysql-transfer;
阿里开源的canal,canal主动与mysql建立连接,往mysql拉取binlog,canal client不断的从canal中拉数据并将数据写到redis;这种方案的有点是解决了可用性的问题,有可替换的节点,分布式考虑的更多;
go-mysql-transfer伪装成从数据库,并从mysql拉取数据,拉完数据后直接写redis;但是没有解决分布式的问题;
代码路径
git clone https://gitee.com/josinli/mysql_redis.git
git clone https://gitee.com/mirrors/go-mysql-transfer.git
缓存方案的故障问题及解决
缓存方案的弊端是不能处理多语句的事务,因为redis不支持回滚,造成redis和mysql数据不一致;