首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  程序员

读写分离到底是大招还是人云亦云 ?真正做过的人有多少?

  •  3
     
  •   huadi · 187 天前 · 5318 次点击
    这是一个创建于 187 天前的主题,其中的信息可能已经有所发展或是发生改变。
    大部分所谓架构文章都会先介绍分库分表,再扛不住就读写分离对吧。

    但没人往下讲细节了。
    如果是异步同步,可以保证写主库成功之后返回,但保证不了延迟。比如写完马上读就会有问题。
    如果等待同步从库成功后再返回,实际就是双写。那么故障几率就更大了,可用性就会降低。

    好吧我知道还有所谓“具体业务具体分析”,但实际上哪有那么多具体业务能忍受可用性降低或者同步延迟。就比如订单,钱不能算错,库存也不能错,但没听说过谁说用可用性来做 trade off 的。

    我始终认为,提升系统容量,要有一套能撑得住的运维体系,然后一直分库分表就好了。所谓主从同步,是一个“高可用”方案,在主库挂掉的时候从库顶上。而不是 scale out 方案。

    那,真正做过读写分离方案的,到底有多少?
    第 1 条附言  ·  187 天前

    补充几点:
    我的疑问是原文的两个如果。

    分库分表可以多机做多实例,每个实例再包含多库和多表。所以也是可以做到负载均衡的。
    主从同步的延迟很低是没错,但关键是无法保证延迟。网络抖动一下,延迟就上来了。一主多从的情况下,每个从库的延迟可能都不相同。所以我想知道读写分离的方案处理这个无法保证的延迟是什么思路。主动监控吗?还是靠其他中间件?或者认为延迟超过某个阈值就是故障,不去考虑高延迟?
    我想了下,多实例分库分表也拉低了系统的可用性,但这个可用性和读写db双写还不一样。首先可以用主从来做高可用(我赞同用主从做高可用,而不是负载均衡),其次挂掉的是一部分数据,线上故障肯定是会有了,但不会全网挂。

    44 回复  |  直到 2019-04-20 20:52:41 +08:00
        1
    LinInV2   187 天前
    互联网公司标配吧。
    保证高可用性的啊
        2
    wentaoliang   187 天前 via iPhone
    这个理解就不对了。主从除了高可用,另外一个作用就是负载均衡,你想想如果访问到数据库的 qps 有几 w,你再怎么分库分表,单机都不可能承受的住
        3
    tiedan   187 天前
    读写的同步延迟间隙可以用缓存覆盖掉
        4
    guyujiezi   187 天前
    你用过 memcache 或 redis 缓存吗?缓存就是一种读写分离
        5
    jimrok   187 天前
    大部分的查询请求,其实不用那么实时,例如订单完成之后,可以不用变化。而且 mysql 的复制还是非常快的,100 毫秒上下,这样主库就可以把 cpu 剩下来提高写入的 ops。
        6
    huadi   187 天前
    @wentaoliang 分库又不是只能在单台机器上分,跑在多台机器上的多个实例也叫分库。
    关键是纸上谈兵的文章太多了,深入细节就没人讲。
    那么,我原文问的的两个如果怎么破?
        7
    expkzb   187 天前 via iPhone
    虽然我不是搞后端的,但我知道磁盘也是有 io 瓶颈的。一般业务都是读多写少的。
        8
    zhengyongtao   187 天前
    读写分离是为了负载均衡,单机性能同时做读写撑不了多少 qps,另外一般订单这种都会使用缓存和队列,而不是直接入库,什么都等入库黄花菜都凉了。
        9
    gaius   187 天前
    多数据源,具体方法上加自定义注解结合 aop 选折具体数据源,这样讲是不是细节了一点。
        10
    tiedan   187 天前
    读写分离的情况下,无实时性要求的业务读从,高实时性的业务强制读主。
        11
    wentaoliang   187 天前 via iPhone
    @huadi 比如你是一个库的某个表请求几 w,你就只能读写分离了,对于读写同步延迟的。就不应该在同一个请求中写完立马就读,如果非得这么做,让第二次强制读主库
        12
    huadi   187 天前
    @guyujiezi 缓存和两个 db 双写还是不一样的。
    缓存很快。
    缓存不是持久化方案,可以采用 db+缓存双写而不降低可用性。
    缓存和 db 的一致性,仍然是问题。
        13
    huadi   187 天前
    @wentaoliang 分库分表最大的问题是数据倾斜和运维水平。比如按 userId 分,可能有某个 user 的请求很多。或者 db 特别多。否则无论如何我都可以降低 QPS。
    强制读主库确实是个好方法。但我不知道怎么控制这个时间阈值,因为同步延迟很难保证。网络抖动这个天灾就受不了。
        14
    vmskipper   187 天前
    根据自己的 qps tps 而定 链接过多或者活动事务过多 就弄呗 读压力过大就加缓存 写压力过大就分片 每天 2000 万的记录 消息几亿 总量百亿 现在就这么弄的
        15
    guyujiezi   187 天前
    @huadi 主从架构不能叫“双写”,这样说法会有歧义的,两主才叫双写。

    主从同步一般都是异步的,这叫最终一致性,同步写入那就变成两阶段提( 2PC )交了
        16
    whileFalse   187 天前   ♥ 3
    “写完马上读就会有问题”

    没错。所以要从业务层弥补。
    当如果写和读属于两个无关业务,比如用户 A 刚更新了自己的头像,用户 B 立即去拿结果拿到了旧的;这种情况通常是可忍受的。因为如果用户 B 的这个请求早发送 1s,那么他看到的肯定就是旧的;两个业务之前不存在因果关系。

    如果写读属于同一个业务,比如更改用户单张借记卡的余额,然后显示用户所有卡的总余额;这种情况要不将两个数据库操作合并为同一个操作,要不从业务层将两个独立的请求合并为一个请求,要不强制读主库;要不告诉用户”更新可能延迟“之类的。

    总之,上读写分离是和业务相关的,无法做到对开发透明。在业务理解并做针对性优化的情况下,可用性不会是问题。
        17
    huadi   187 天前
    @guyujiezi 我的原始问题是原文的两个如果。
    如果不双写,做异步同步,这没问题的。那我的疑问是怎么处理延迟。
    主从延迟很低是没错,但关键是这个延迟是无法保证的。也就是说平常 100ms 之内,但网络一抖动,分钟级别的延迟都有可能发生。这个时候从库是没办法提供服务的,怎么处理这种问题呢?
        18
    huadi   187 天前
    @whileFalse 想请问下,合并两个请求有什么思路么?现在大部分应用都是 HTTP,有什么工具或者链接能分享下?
        19
    glfpes   187 天前
    你举的例子就是那种“读写分离不太合适”的情况。订单系统这种强一致性的场景不多且一般都是关键场景,花更多的资源有价值。
    实际上很多场景,不需要强一致性。当读写不均衡的情况下,比如读的 qps 比写高几个数量级,读写分离还是很常见的做法。比如用户画像。其实用到本地缓存的地方都可以搞读写分离。
        20
    lhx2008   187 天前   ♥ 1
    分布式系统最大的问题就是很难保证强一致性,对于普通业务,主要通过逻辑弥补。对于敏感的业务,主写从读会出问题。

    逻辑弥补有很多方法 (来自李运华的文章)
    1. 业务可以接受,系统不优化
    2. 写操作后的 n 秒内读操作指定发给数据库主服务器
    3. 读从机失败后再读一次主机
    4. 关键业务只走主机,可接受延迟的业务走从机
    5. 走缓存,先更新缓存,缓存过期肯定已经刷入从机了

    楼主看的“所谓架构文章”,怕不是 CSDN 上面的?
        21
    wentaoliang   187 天前
    @huadi 理解一下 cap 理论就知道,按目前分区无法完全保证的情况下,强一致性和高可用只能满足一个。所有从理论层面告诉你,你的只能在业务层面控制
        22
    yanaraika   187 天前
    首先,CAP 定理了解一下,要 C+P 必然会牺牲可用。实际中是要检测延时的,这也是不推荐在公有云上自己搭数据库集群的原因:网络没有专有网络稳定。

    现在也有一些利用 RDMA 提升 RSM 复制的方案。

    另一点是:吞吐和延时也是不可兼得的,要使用 batching 等方式提升 replicate 的吞吐就会牺牲延时。

    一种折中方案是 写 + 读确认节点 > N。一般写少读多的话成功写入 3/4 N 节点返回,成功读到 1/4N + 1 节点返回。

    顺带一提,当你需要读写分离的时候再折腾 MySQL 读写分离主从之类的基本就是收效最小的方案,之后主不可用、一致性太弱等问题会让你很头疼,最后层层 patch 后会发现自己造了一个已有的轮子。建议一步到位上无中心化的方案如 Cassandra/NewSQL 等。

    @lhx2008 这几个方案都有很多缺陷,最简单的就是客户端时钟不可靠
        23
    lhx2008   187 天前
    @yanaraika 这个确实是一个折中的方法,但是实现的复杂度也非常高了。当然 NoSQL 的经过验证的分布式的主从架构更加可靠。至于时钟问题,确实是一个存在的因素,我没有过多研究,不过 Redis 作者在说 RedLock 的时候,没有认为这是一个主要问题。"well, you can assume your clock is mostly correct though!"
        24
    guyujiezi   187 天前
    @huadi 如果你的业务代码写得正确,那是不会发生你所说的问题的。因为这个分布式模型是可以实现最终一致性的

    我猜你的意思可能是:往从库里取得商品库存,由于数据没有同步,所以从库中的库存数量多余实际数量,然后程序因此创建了本不应该创建的订单。是这样吧?

    然而你这个代码本身就是错误的呀
        25
    alvin666   187 天前 via Android
    我们做的主从是直接走的云服务商的内网,内网要是再波动了,就算不做主从也会受影响
        26
    yanaraika   187 天前 via Android
    @lhx2008 http://antirez.com/news/101 这个问题非常复杂。我个人是不喜欢 redlock 这种对时间源一个外界因素有依赖的方案的
        27
    yanaraika   187 天前 via Android
    @alvin666 主要是延时没 SoA 保证…而且延时低到一定程度后 gc 等因素也无法忽略
        28
    calpes   187 天前
    @huadi 最简单的例子。。。你确认写入成功后不再次读库,直接将你写入的数据返回给用户,这就是一种很常见的操作,主从分离这个事某种意义上是无法做到对开发者透明的,而且这里你有一个钻牛角尖的思路,就是主从之间的网络可能一抖抖个一分钟啥的,需要业务代码来容错,如果把这些东西加到研发成本里,基本上是没法接受的。OP 角度上来讲每个服务都是有他的可用性指标的,五个 9 以上我觉得这种抖动很难感知到,而如果你的业务在主从分离的场景下由于 db 的可用性指标不够导致业务上大面积的出问题,我建议你们换个更专业的 DB 和 OP,毕竟服务可用性他是一个系统性工程。
        29
    idblife   187 天前
    楼主土鳖而已
    哪家互联网公司不做读写分离能扛得住?
        30
    wangxiaoaer   187 天前
    @jimrok #5 即使多个实例机器,那么同一个数据库的某张表你打算放到几台机器呢?如果 1 台,势必会有瓶颈,如果多台,不还是主从复制吗
        31
    passerbytiny   187 天前
    读写分离是抽象,分库分表是技术实现,啥时候被当成解决问题的两种方式了。人家文章的意思其实是:一般你用分库分表这种简单实现方式凑合以下就行了,要是扛不住,你得从根本的抽象上去寻找解决方案,比如说读写分离;至于具体是什么,限于篇幅无法细说(其实我也只知道个标题)。

    读写分离这种标题很容易理解但内容很复杂的高级抽象,网上你就只能找到标题,想系统的学习,还是要买书。推荐你一本书《实现领域驱动设计》,里面“架构”、“事件”等章节有关于读写分离架构的描述。
        32
    jimrok   186 天前
    @wangxiaoaer 你这个问题,要结合业务场景来讲。就说订单这个东西,如果你的用户少,可以不做拆表处理的。当用户购买商品后,去查询订单,这个请求是个读请求,而且查询可能根据时间,状态过滤。这个列表数据是不容易做 memcahced 缓存的,所以,必须将这部分查询请求放在从库上执行。这样主库的压力就会降下来,放几台要根据你的 qps 量进行估算。如果每秒有 2w 笔查询,那你单台能支撑的是 5000qps,你就需要准备至少 4 台服务器。如果你再能在应用层将请求分割开,那么每台的 mysql 可能只负责某个区域的用户查询请求,那么 mysql 的 buffcache 就可能非常高效。
        33
    jimrok   186 天前
    @huadi 主从复制不是解决强一致性问题的,通常情况下的应用,不用害怕这个延迟。在运维层面来讲,通常有一个读写分离的 proxy,有这个 proxy 来代理 sql 端的请求。这个代理也同事负责监控主从库的监控状况,如果从库失败,这个代理会把 select 的语句发给主库执行的。如果你用过阿里云的 RDS,他们会给你一个 readonly 的 mysql 地址,还有一个读写分离的地址,你不需要考虑后端是否可用,这个 proxy 已经管理了后端的群集。
        34
    CRVV   186 天前
    如果一个数据库 instance 搞不定所有的读写请求,那么你可以使用多个 instance 来搞
    有不同的方案来使用多个 instance,读写分离是其中的一个
    或者说你也不一样要完全读写分离,只要把一部分读请求分走,也许就能好了

    > 如果是异步同步,可以保证写主库成功之后返回,但保证不了延迟。比如写完马上读就会有问题

    是会有问题,但有的业务可以接受有这个问题,或者用另外的手段处理这个问题。
    或者你可以只把不需要强一致性的读请求分走。

    > 如果等待同步从库成功后再返回,实际就是双写。那么故障几率就更大了,可用性就会降低

    出故障的概率会变大,但是数据出错的概率会变小。某些情况下可以接受系统挂掉但是不能接受丢数据。

    而且这么干的前提是你用一个 instance 搞不定,搞不定的事情就没有可用性,现在能搞定了才有了可用性。
    这里不应该说可用性降低了。
        35
    Raymon111111   186 天前
    读写分离太多了

    我们目前单库写入 qps 3000 (分库分表 5 个库), 读写分离框架用的很顺利

    你说的主从延迟的问题, 绝大多数情况下都能控制在 100ms 内. 也就是说主库写入后从库很短时间内就可以读到, 能够满足绝大多数的使用场景

    有一些很特殊的场景会采用强制读主库的方案, 但是这种一般控制量, 主库读影响写入性能

    至于监控, 产品也很多, 最快的学习方法是进一个大公司, 一般这些东西很成熟, 你一看就明白了
        36
    tairan2006   186 天前 via Android
    喷了,用缓存本质上就是读写分离吧。网络分区下高可用和一致性是矛盾的,这不就是 CAP 定理么,楼主自己瞎琢磨啥呢。
        37
    zzzzzzZ   186 天前
    读写分离是标配,不是大招,不是人云亦云,是一个稍微水平正常的开发都能做的标配架构

    如果你还卡在你第一个"如果",那就是你代码写的烂,别玩架构了
    如果你出现了你第二个"如果",那就是你架构搭的烂,别玩架构了

    你要的所谓的“中间件”,数据库层面的是 Mycat,就是 @jimrok #32 做的事
    代码层面的纯代码就能解决,想不明白怎么敲代码,就别来纸上谈兵的学一些架构的东西,架构从来都是实践出来的
        38
    janxin   186 天前
    我觉得其实楼主你应该实操一下?空想很容易钻牛角尖。

    另外一个,基本上根据 CAP 去牺牲某一个,根据业务需求再去考虑牺牲哪一个。

    即便加上缓存,也是根据具体的业务需求,决定缓存 miss 和脏缓存的接受程度。

    毕竟,没有银弹嘛。
        39
    phx13ye   186 天前 via iPhone
    借楼问问各位大佬,多对多关系的的中间表一般怎么分库分表。是要双向查的,只能冗余两份各自 sharding 吗?。例如我给哪些文章点赞了,文章被谁点赞。学生选修哪些课程,课程有哪些学生选修。
        40
    bringyou   186 天前
    分库分表限制了一些业务实现,比如说表之间的 join。一般业务场景下,对于数据的读取是多于写入(更新),所以为了那些特性,很多场景下都只是先读写分离了。而且一般的规模下这个也足够了。至于主从同步的延迟,阿里云 rds 是 1 毫秒级别的,很少超过 10ms,动辄 100ms 甚至 1min 的延迟,我觉得还是先招些专门的网络运维人员,先优化网络链路来的实在一些,因为网络慢不只是影响数据库主从同步,正常的业务应用也会受到影响。

    如果对于 1ms 级别的延迟不能接受的话,可以向前面老哥提到的,再次查询写库,这个重复的工作可以通过 aop 或者中间件来做,自己不用重复的。

    同步模型可以看看 mysql 的异步同步、半同步和组同步。
        41
    bringyou   186 天前
    @phx13ye 之前我司是这样。如果不局限于数据库的话,可以试试一些 newsql 或者 elasticsearch 这样的搜索引擎
        42
    akira   186 天前
    之前做读写分离,是把一些报表类的,不太要求实时性的查询 放在只读库 ,
    大部分业务还是落在主库上面
        43
    curdoldman   186 天前
    SQL HINT 了解一下。
    HINT 是一种可选的 SQL 片段或注释,可用于在 SQL 语句里强制索引,强制使用主库,强制不使用缓存等等
        44
    fox0001   185 天前 via Android
    我们是采用 Solr + 数据库,不完全的读写分离。但是 Solr 抗住了绝大部分的复杂查询,效果不错
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4207 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 29ms · UTC 05:46 · PVG 13:46 · LAX 22:46 · JFK 01:46
    ♥ Do have faith in what you're doing.