持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
我有个朋友,最近做了一个功能,大概就是将一批号码发给用户,一个号只能发给一个用户,项目数据库使用读写分离的部署方案,最后出现一个号码发给多个人的问题。朋友直呼不可能,因为做号码分发的地方加锁了,不可能出现问题的。号码保存在一个表中,通过修改状态的方式表示号码的发放状态。先读出来未发放的号码,后将该条号码修改成已发放即完成号码的发放逻辑。原来是读写分离问题导致一个号码多次发送,当一个号码被发放后修改到主库,但是由于延迟从库没修改成已发放,那么读取未发放号码的时候就会同时读出来刚刚已经发放的号码导致一个号码多次发送。何为主从延迟,如何解决?
主从同步过程
MySQL主从同步由主节点dump线程、从节点 I/O 线程、从节点SQL线程三个线程配合完成。
1、从节点上的 I/O 进程主从去连接主节点,并带上同步的开始位置即指定日志文件的位置之后的日志内容
2、主节点接收到来自从节点的 I/O 请求后,通过主节点dump线程读取指定日志位置之后的日志信息,返回给从节点。同时也返回binlog file的以及binlog position
3、从节点的 I/O 线程接收到内容后,将接收到的日志内容写到本地的relay log中,并将读取到的binlog文件名和位置保存到master-info文件中
4、从节点的SQL线程检测到relay log中新增加了内容后,将relay log回放写入到数据库中
主从复制模式
1、异步模式
主库写入binlog完成后提交事务。
2、半同步模式
保证至少有从库将日志写入relay log后应答成功才提交事务。
3、全同步模式
MySQL 主从复制默认是异步的模式
主从延迟问题
从上文可知主从同步无论哪种模式都是先写入从库的relay日志后再有SQL线程回放写入从库,所以或多或少的都会出现主从延迟问题。所以会导致一定时间内主从的不一致问题,开发中常见的读写分离存在主从延迟问题就是这个原因导致。
主从延迟问题解决
1、mysql层面。可以增加机器性能、减少网络延迟、降低数据安全措施例如关闭从节点binlog日志或者刷盘策略放开等增加从库写入的速度、还可以配置多个SQL线程等。
2、业务层面。可以使用缓存后双写避免马上去读从库,也可以加延迟读取以减少延迟带来的业务问题,也可以直接读取主库。
开头问题解决方案
1、修改sql,增加一个判断条件兜底,保证不多发,update code set state=已领取 where state=未领取,这样可以确保一条号码不会被多次领取,但是需要处理领取失败的情况。可以在读取未领取号码时多读取几条,一条领取失败就领取下一条直到成功或者领完。
2、使用缓存,将数据预读到缓存中,读的时候从缓存中读取,这样就可以避免去读从库,但是需要处理缓存和主库一致性问题。将号码放到redis队列中,每次从队列里去一条,用完后再读取到redis。