双写一致性问题

一个数据同时存在于持久性存储(数据库)及缓存(redis)中,任何一方的更新都必须保证另一方的同步更新,以保持双方数据的一致性,如:

  1. 写数据库后忘记更新缓存:后续请求可能仍从缓存中读到旧数据;
  2. 先删除缓存以保证数据新鲜度,但是后续更新数据库失败了
  3. 并发情况下,多个读写请求交替进行,可能导致多个请求操作同一份数据,比如删缓更库时,新读请求读到旧数据并把它放到缓存里
  4. 主从复制存在时延,若在此期间缓存失效,可能从未同步的从库读到旧数据

双写策略

旁路缓存

即读请求到来先在缓存里面找,如果没有命中则去数据库查找,找到后加载到缓存并返回数据;

写请求到来时先更新数据库,再删除缓存;

为什么是删除缓存不是更新缓存?

并发情况下,两个写请求到来可能出现a更新数据库->b更新数据库->b更新缓存->a更新缓存的情况,导致数据库缓存数据不一致

为什么先操作数据库后操作缓存?

若先删除缓存,a请求删除缓存并更新数据库,b请求到来直接从未更新完的数据库读到旧数据并加载到缓存里,导致出现脏数据

读写穿透

读穿透类似旁路缓存策略,只是未命中缓存的话,由缓存系统去隐式地自动地查询数据库并加载到缓存而非由应用程序显示地访问数据库

写穿透则是更新缓存,同时更新数据库,只有当数据库更新完成,这个写操作才算完成

异步缓存写入

更新缓存后并非立即更新数据库,而是通过缓存系统记录下此次更新,放到一个队列中(日志文件或者内存队列),后台独立存在一个线程定期或者当队列达到一定数量去从队列中取更新操作,批量写入数据库

因为更新数据库操作并不及时,所以很忌讳缓存或系统故障导致丢数据,适用容忍一定延迟的数据一致性场景

用一定一致性换性能和扩展

延时双删

更新数据库时,先删除一遍缓存,确保后续请求会从数据库读新的,但更新数据库和删除缓存可能存在时间窗口,期间可能读到旧数据,所以设定一定时间后再删除一遍缓存

删除缓存重试

删除缓存失效执行时,设定重试策略以确保正确删除,如指数退避、固定间隔重试等策略多次尝试

监听并读取binlog异步删除缓存

更新数据库时,将对应操作记录在类似binlog的事务日志里面

用专门的异步服务或监听器订阅binlog变化,若有数据更新

根据binlog中操作信息定位到受影响的缓存项

将需要更新的数据发送到消息队列,由消费者处理队列中的事件,以一个异步的方式删除或更新缓存

将更新缓存和主业务解耦,避免主线程阻塞

总结

读的时候旁路缓存;

写的时候,先写数据库,异步处理缓存的更新,即监听并读取binlog事件,异步删除缓存.

一主多从的情况下,还要等所有从库binlog事件都被处理后才删除缓存

一条SQL查询语句的过程

  1. 连接器:连接器负责和客户端连接,登陆校验,获取权限,维持和管理连接
  2. 查看缓存:会先去看之前有没有执行过相同的语句,如果有的话,语句和相对应的结果会以键值对的方式被存在内存里面,直接返回,如果没有就到分析器流程
  3. 分析器:主要进行语法分析语义分析,比如知道语句中字符串的含义
  4. 优化器,负责索引的选择,如果是多表查询的话还负责多表间的加载顺序,生成执行计划
  5. 执行器(执行引擎)通过分析器知道了做什么,通过优化器知道了怎么做,先作一次权限校验,接下来按照执行计划去存储引擎获取数据,接着处理数据,可能设计排序连接过滤等,最后返回结果

事务隔离级别

  1. 读未提交:允许一个事务读取另一个事务尚未提交的数据修改.存在脏读、不可重复读、幻读现象
  2. 读已提交:一个事务只能读取已提交的数据操作
  3. 可重复读:事务执行期间,多次读取同一数据结果相同,即一个事务执行时,其他事务所作修改对他而言是不可见的
  4. 序列化:最高级别的,确保事务间并发执行与串行相同

事务四大特性ACID

原子性:事务的所有操作不应该出现部分成功的情况

一致性:事务执行前后,数据库从一个一致性状态到另一个一致性状态

隔离性:多个事务并发执行时,一个事务不会影响其他事务执行

持久性:一旦事务被提交,那么对数据库的操作就是永久的,即便系统故障或崩溃

数据库的存储引擎

存储引擎主要负责执行查询、数据存储

  1. MyISAM:早期默认的存储引擎,支持全文索引、表级锁;适合快速读取和数据量不大的场景
  2. InnoDB:目前主流,支持事务ACID、行级锁、外键、崩溃恢复(事务日志实现,确保一致性),适合需要事务和高并发的场景
  3. Memory:将表数据存在内存里,查询速度飞快,适用于临时数据的存储(一旦server崩溃或重启容易丢失数据)

MySQL为什么使用B+树作索引

大并发服务器基本框架

基本服务器框架都是c/s的,请求和响应的流程是:客户端与应用服务器建立连接,应用服务器与数据库服务器进行交互

! 当大量并发到来的时候,服务器会进行大量数据库操作,但数据库的最大连接数量是有限的,未连接的需等待前面已连接的请求处理后在访问数据库

可以在两者间增设中间层DAL,设计缓冲队列和连接池设计.缓冲队列用来存储等待的请求,当连接池有空闲连接就从队列中取一个请求处理.但仅仅减轻服务器压力,未提升处理速度(受限于数据库并发数)

增设一层缓存,将常用数据加载到缓存,命中缓存

**!**缓存足够多时,需要进行缓存换页

缓存可以部署在和应用服务器同一台机器上,也可以单独服务器,推荐单独服务器,不然不同应用服务器间很难访问彼此的缓存

**!**如果有大量并发请求到来,虽有多台服务器+缓存服务器+DAL中间层,数据库服务器仍会出现瓶颈,比如大量写操作阻塞了很多读请求.

将数据库实现读写分离.由于读一般比写多,执行负载均衡replication,中间层将写操作投递到主库中,读操作从从库中读取.当主库被修改,通过replication机制同步给从库

同样,应用服务器也实现负载均衡,架设多台服务器,不同请求派发给不同的服务器.由单独的任务服务器派发.这种方式时任务服务器主动分发,一个用服务器被动接受,在请求类型相近的情况下,比如,都是读请求,服务器a2个读请求,服务器b3个读请求,,没什么问题,但如果请求处理的复杂程度不同.