当前位置: 首页 > 科技观察

数据库读写分离如何保证主从一致性

时间:2023-03-16 22:25:12 科技观察

读写分离当我们数据库的主键压力变大的时候,我们会尝试增加一些从节点来分担主节点的查询压力。一般来说,我们采用一主多从的结构作为读写分离的基本结构。一般来说,我们常用的实现读写分离架构的方法有两种:客户端直接分离这种方法,客户端或者我们的微服务直接进行数据库的读写选择。读库选择路由到主库,查询路由到从主库。这种方式的好处是因为是直连,所以性能比较高,但是业务团队需要了解数据库实例的细节,而且当数据库调整的时候,业务端需要同步重构。使用数据中间件代理是一种通过代理层分发数据读写的方式,业务层通过代理实现所有的请求。这种方式的优点是业务层不需要感知数据库的存在,但问题是对数据中间件的性能要求高,需要专人进行优化和维护,而且整体架构相对复杂。但是我们发现,虽然这两种方法各有优缺点。但是核心是通过查询请求的数据写入和路由来实现的,所以这会导致题目的问题:主备同步存在延迟,所以在延迟时间内查询插入的内容无法查询最新提交事务。那么如何保证主从一致性的问题实际上就变成了如何处理主从延迟的问题。低廉的改造成本取决于项目规模、团队规模和主机的部署模型。我们处理问题的方式也有很多。强制读主库最简单也是最难的方法就是强制读主库。一般来说,我们在不同的查询中会有不同程度的一致性要求。我们可以将需要保证数据一致性的请求配置到主库查询,对于没有强依赖的查询请求配置到备库。这种方案虽然不是很优雅,但是实现起来是最简单的,而且在Spring等框架的支持下,一般只需要加一个注解即可。但是,这种方法的问题也很明显。如果有大量强一致性要求的查询语句,相当于不进行读写分离和扩容。那么这种方式就会导致系统在数据库层面没有有效的扩展手段。等待时间由于问题的根源是主从延迟,所以在下次查询时等待一段时间就可以弥补这个延迟。所以在主库插入数据后,让数据库数据连接或者对应的执行线程等待一段时间再返回。主从备份的延迟时间是通过等待时间来消化的。但是这种方法也存在一些问题。比如:等待时间一般是固定的,即使主从没有延时,也会一直等到时间结束;仍然会有一致性问题。但是这个方法的优雅之处在于它可以结合业务来实现。比如用户下单后,通过下单发送优惠券或者下单抽奖等方式将用户从前端拖拽过来。在查询自己的订单时,中间要有一定的间隔,保证再次查询数据时数据的一致性。一主一备的情况上面的两种解决方案可能看起来没有那么“技术性”,感觉有点投机取巧。那么我们可以讨论在两种情况下如何用更多的技术方法来实现一致性。对于主从复制,查询延迟方案是当主库完成一个事务时通知从库,当从库收到事务后,主库完成返回给客户端。因此,当主库完成事务时,只能保证从库已经收到,而不能保证从库执行完成,从而导致主从备份的延迟。但是从数据库执行数据是有进度的,这个进度可以通过showslavestatus语句中的seconds_behind_master来描述。该参数描述的是从库数据落后于主库数据多少秒。当这个参数为0时,我们可以认为从库和主库之间基本没有延迟,所以此时可以查询请求。不过seconds_behind_master是秒级别的,只能粗略判断。由于精度低,仍然可能存在不一致。如果需要精确执行,我们可以对比同步文件的执行记录,具体为:Master_Log_File和Read_Master_Log_Pos,代表主库读取的最新位置;Relay_Master_Log_File和Exec_Master_Log_Pos,代表备库执行点的最新位置。所以当Relay_Master_Log_File和Exec_Master_Log_Pos与其一致时,说明从库的执行数据赶上了主库,那么可以说保证了主从一致性。半同步复制方案但是比较同步文件的执行记录方法的问题是如果当前事务的binlog还没有传输到从库,也就是Master_Log_File和Read_Master_Log_Pos没有更新,不能保证从库包含最新的主库事务。为了保证在一主一备的情况下,必须从库接收数据,即Master_Log_File和Read_Master_Log_Pos中的数据与主库一致,我们可以开启半同步复制。半同步复制的原理是在主库提交事务之前先将binlog发送到从库,当从库接受后返回响应。主库收到响应后才返回事务执行完成。这样就可以保证从库的Master_Log_File和Read_Master_Log_Pos与主库一致,从而解决主从一致性问题。一主多备情况下的半同步复制可以解决一主多备的情况,但是一主多备时,只要主库收到从库的响应,就会返回交易执行完成。这时候当请求打到还没有完成同步的从库时,就会出现主从延迟。等待主库记录方案所以对于一主多备的情况,我们可以把重点放在执行查询的从库上,即保证我们要查询的备库已经执行了我们预期的事务。那么我们的问题就变成了两部分:1.确认主库交易,2.查询数据情况。确认主库交易当我们提交一个交易后,我们可以通过执行showmasterstatus来获取主库中的数据交易文件(File)和持仓记录(Position)。查询数据条件当我们想从数据库中查询数据时,可以使用语句selectmaster_pos_wait(File,Position,1);查询该条记录当前是否执行过(当返回值>=0时表示执行过)。最后一个数字1表示阻塞持续时间。通过先确认主库的事务记录,再确认备库是否执行过主库对应的事务。但是可以发现,这种方式在查询的时候需要知道主库的交易信息,对场景有很大的限制。最后,主从一致性的问题来自于主从延迟,所以我们从如何消除延迟来解决这个问题。对于更简单的解决方案,我们可以忽略延迟的影响而不去备库,或者等待一段时间。在一主一备的情况下,我们可以大致用seconds_behind_master来判断或者用Relay_Master_Log_File和Exec_Master_Log_Pos来判断。在一主多从的情况下,我们需要在查询前传入主库执行的事务记录,以保证数据的一致性。可以看到,当数据规模和部署方式发生变化时,好的解决方案会越来越多。我认为最重要的是根据实际业务情况选择最合适的方法。

猜你喜欢