当业务系统在高并发和大数据量的情况下,读写分离成为常用的技术手段。读写分离的主要目的是将数据库的读和写操作分配给不同的服务器,以提高系统的性能和可伸缩性。
什么时候需要读写分离?
在互联网的大部分业务场景中,通常存在着读多写少的情况,特别是在电商等典型业务中。在这样的场景下,读操作的请求通常要比写操作的请求多得多。如果不采取相应的措施,数据库的读操作可能成为整个业务的瓶颈,并且写操作的成功率也可能受到影响。为了应对这个问题,通常会采用读写分离的技术来确保系统的稳定性和性能。读写分离顾名思义,就是分离读库和写库操作,从CRUD的角度,主数据库处理新增、修改、删除等事务性操作,而从数据库处理SELECT查询操作。具体的实现上,可以有一主一从,一个主库配置一个从库;也可以一主多从,也就是一个主库,但是配置多个从库,读操作通过多个从库进行,支撑更高的读并发压力。
读写分离的实现是将访问的压力从主库转移到从库,特别适用于业务中大部分请求为读操作的情况,以减轻主数据库的负载压力。然而,如果业务特点是写多读少,比如一些需要频繁进行动态更新的业务场景,读写分离可能就不是一个合适的选择。关系型数据库如MySQLInnoDB对于事务的支持确实会对写入性能产生一定的影响。此时,为了实现更高的写入性能,通常会考虑选择性能更高的NoSQL等非关系型存储来满足业务需求。NoSQL数据库通常以键值或文档为基础,可以提供更高的写入吞吐量和更低的写入延迟。
MySQL主从复制
读写分离是基于主从复制架构实现的,下面详细介绍MySQL中的主从复制技术。
MySQLInnoDB引擎的主从复制,是通过二进制日志binlog来实现。除了数据查询语句select以外,binlog日志记录了其他各类数据写入操作,包括DDL和DML语句。
binlog是MySQL中用于记录数据库更改操作的日志文件。在MySQL的复制和恢复过程中发挥着重要的作用。在binlog中,有三种常见的格式:Statement、Row和Mixed。Statement格式:基于SQL语句的复制,它记录了每一条修改数据的SQL操作。当从库获取到binlog后,可以通过执行相同的SQL语句来进行数据回放。Row格式:基于行信息的复制,它以行为单位记录了每一行数据修改的详细细节。相比于Statement格式,Row格式不记录执行SQL语句的上下文相关信息,而仅仅记录行数据的修改。对于批量更新操作等情况,Row格式会产生大量的日志内容。Mixed格式:混合模式复制,它是Statement格式和Row格式的结合。在Mixed格式下,不同的SQL操作会采用不同的记录方式。一般的数据操作会使用Row格式来保存,而一些表结构的变更语句则会使用Statement格式来记录。MySQL主从复制过程如下图所示:
主库将变更写入binlog日志,从库连接到主库之后,主库会创建一个logdump线程,用于发送binlog的内容。
从库开启同步以后,会创建一个IO线程用来连接主库,请求主库中更新的binlog,I/O线程接收到主库binlogdump进程发来的更新之后,保存在本地relay日志中。
接着从库中有一个SQL线程负责读取relaylog中的内容,同步到数据库存储中,也就是在自己本地进行回放,最终保证主从数据的一致性。
读写分离要注意的问题
在主从复制的过程中,由于主库和从库是两个不同的数据源,存在一个从主库写入数据到从库的同步延时问题。这是因为主从复制需要经过一系列的步骤来完成数据同步,包括主库写入binlog日志、从库读取binlog日志、解析日志并应用到从库数据等过程,这些操作本身都需要一定的时间。当主库接收到写入操作后,会将这个操作记录在binlog日志中。然后,从库会定期轮询binlog日志,读取新的写入操作,并将其应用到从库的数据中。由于需要进行额外的日志同步、解析和写入操作,这个过程会产生一定的延迟。在需要确保数据一致性的业务场景中,主从复制的延迟可能引起一些问题,例如订单支付场景中的情况。当订单支付已经完成并主库数据已经更新,但从库的数据尚未同步,此时若从库被用于读取数据,会出现订单未支付的情况,这在业务中是不可接受的。
为了解决主从同步延迟的问题,通常可以采取以下几个方法:1.敏感业务强制读主库:对于一些对数据实时性要求很高的业务场景,可以强制读取主库的数据,而不是从库。这样可以确保读取到的数据是最新的,避免了主从延迟带来的问题。2.关键业务不进行读写分离:对于一些对数据一致性要求较高的关键业务,如金融支付等,可以不进行读写分离。将读写操作都集中在主库上,避免了主从延迟导致的问题。3.使用半同步复制:MySQL支持半同步复制的方式,主库在执行写操作后,会等待至少一个从库完成同步才返回结果。这样可以提高数据的安全性,避免主库崩溃导致数据丢失的情况。4.使用全同步复制:如果对数据的一致性要求非常高,可以采用全同步复制的方式。在多从库的情况下,主库会等待所有从库都完成同步才返回结果。虽然全同步复制会带来一定的性能损耗,但可以确保数据的完全一致性。