补充几点:
我的疑问是原文的两个如果。
分库分表可以多机做多实例,每个实例再包含多库和多表。所以也是可以做到负载均衡的。
主从同步的延迟很低是没错,但关键是无法保证延迟。网络抖动一下,延迟就上来了。一主多从的情况下,每个从库的延迟可能都不相同。所以我想知道读写分离的方案处理这个无法保证的延迟是什么思路。主动监控吗?还是靠其他中间件?或者认为延迟超过某个阈值就是故障,不去考虑高延迟?
我想了下,多实例分库分表也拉低了系统的可用性,但这个可用性和读写db双写还不一样。首先可以用主从来做高可用(我赞同用主从做高可用,而不是负载均衡),其次挂掉的是一部分数据,线上故障肯定是会有了,但不会全网挂。
1
LinInV2 2019-04-19 10:41:54 +08:00
互联网公司标配吧。
保证高可用性的啊 |
2
wentaoliang 2019-04-19 10:46:26 +08:00 via iPhone
这个理解就不对了。主从除了高可用,另外一个作用就是负载均衡,你想想如果访问到数据库的 qps 有几 w,你再怎么分库分表,单机都不可能承受的住
|
3
tiedan 2019-04-19 10:47:59 +08:00
读写的同步延迟间隙可以用缓存覆盖掉
|
4
guyujiezi 2019-04-19 10:48:10 +08:00 1
你用过 memcache 或 redis 缓存吗?缓存就是一种读写分离
|
5
jimrok 2019-04-19 10:49:18 +08:00
大部分的查询请求,其实不用那么实时,例如订单完成之后,可以不用变化。而且 mysql 的复制还是非常快的,100 毫秒上下,这样主库就可以把 cpu 剩下来提高写入的 ops。
|
6
huadi OP |
7
expkzb 2019-04-19 10:50:26 +08:00 via iPhone
虽然我不是搞后端的,但我知道磁盘也是有 io 瓶颈的。一般业务都是读多写少的。
|
8
zhengyongtao 2019-04-19 10:51:59 +08:00 1
读写分离是为了负载均衡,单机性能同时做读写撑不了多少 qps,另外一般订单这种都会使用缓存和队列,而不是直接入库,什么都等入库黄花菜都凉了。
|
9
gaius 2019-04-19 10:54:21 +08:00
多数据源,具体方法上加自定义注解结合 aop 选折具体数据源,这样讲是不是细节了一点。
|
10
tiedan 2019-04-19 10:55:26 +08:00
读写分离的情况下,无实时性要求的业务读从,高实时性的业务强制读主。
|
11
wentaoliang 2019-04-19 10:55:28 +08:00 via iPhone
@huadi 比如你是一个库的某个表请求几 w,你就只能读写分离了,对于读写同步延迟的。就不应该在同一个请求中写完立马就读,如果非得这么做,让第二次强制读主库
|
12
huadi OP |
13
huadi OP @wentaoliang 分库分表最大的问题是数据倾斜和运维水平。比如按 userId 分,可能有某个 user 的请求很多。或者 db 特别多。否则无论如何我都可以降低 QPS。
强制读主库确实是个好方法。但我不知道怎么控制这个时间阈值,因为同步延迟很难保证。网络抖动这个天灾就受不了。 |
14
vmskipper 2019-04-19 11:06:00 +08:00
根据自己的 qps tps 而定 链接过多或者活动事务过多 就弄呗 读压力过大就加缓存 写压力过大就分片 每天 2000 万的记录 消息几亿 总量百亿 现在就这么弄的
|
15
guyujiezi 2019-04-19 11:11:58 +08:00
|
16
whileFalse 2019-04-19 11:22:40 +08:00 4
“写完马上读就会有问题”
没错。所以要从业务层弥补。 当如果写和读属于两个无关业务,比如用户 A 刚更新了自己的头像,用户 B 立即去拿结果拿到了旧的;这种情况通常是可忍受的。因为如果用户 B 的这个请求早发送 1s,那么他看到的肯定就是旧的;两个业务之前不存在因果关系。 如果写读属于同一个业务,比如更改用户单张借记卡的余额,然后显示用户所有卡的总余额;这种情况要不将两个数据库操作合并为同一个操作,要不从业务层将两个独立的请求合并为一个请求,要不强制读主库;要不告诉用户”更新可能延迟“之类的。 总之,上读写分离是和业务相关的,无法做到对开发透明。在业务理解并做针对性优化的情况下,可用性不会是问题。 |
17
huadi OP @guyujiezi 我的原始问题是原文的两个如果。
如果不双写,做异步同步,这没问题的。那我的疑问是怎么处理延迟。 主从延迟很低是没错,但关键是这个延迟是无法保证的。也就是说平常 100ms 之内,但网络一抖动,分钟级别的延迟都有可能发生。这个时候从库是没办法提供服务的,怎么处理这种问题呢? |
18
huadi OP @whileFalse 想请问下,合并两个请求有什么思路么?现在大部分应用都是 HTTP,有什么工具或者链接能分享下?
|
19
glfpes 2019-04-19 11:40:21 +08:00
你举的例子就是那种“读写分离不太合适”的情况。订单系统这种强一致性的场景不多且一般都是关键场景,花更多的资源有价值。
实际上很多场景,不需要强一致性。当读写不均衡的情况下,比如读的 qps 比写高几个数量级,读写分离还是很常见的做法。比如用户画像。其实用到本地缓存的地方都可以搞读写分离。 |
20
lhx2008 2019-04-19 11:46:20 +08:00 2
分布式系统最大的问题就是很难保证强一致性,对于普通业务,主要通过逻辑弥补。对于敏感的业务,主写从读会出问题。
逻辑弥补有很多方法 (来自李运华的文章) 1. 业务可以接受,系统不优化 2. 写操作后的 n 秒内读操作指定发给数据库主服务器 3. 读从机失败后再读一次主机 4. 关键业务只走主机,可接受延迟的业务走从机 5. 走缓存,先更新缓存,缓存过期肯定已经刷入从机了 楼主看的“所谓架构文章”,怕不是 CSDN 上面的? |
21
wentaoliang 2019-04-19 11:51:50 +08:00
@huadi 理解一下 cap 理论就知道,按目前分区无法完全保证的情况下,强一致性和高可用只能满足一个。所有从理论层面告诉你,你的只能在业务层面控制
|
22
yanaraika 2019-04-19 12:12:59 +08:00
首先,CAP 定理了解一下,要 C+P 必然会牺牲可用。实际中是要检测延时的,这也是不推荐在公有云上自己搭数据库集群的原因:网络没有专有网络稳定。
现在也有一些利用 RDMA 提升 RSM 复制的方案。 另一点是:吞吐和延时也是不可兼得的,要使用 batching 等方式提升 replicate 的吞吐就会牺牲延时。 一种折中方案是 写 + 读确认节点 > N。一般写少读多的话成功写入 3/4 N 节点返回,成功读到 1/4N + 1 节点返回。 顺带一提,当你需要读写分离的时候再折腾 MySQL 读写分离主从之类的基本就是收效最小的方案,之后主不可用、一致性太弱等问题会让你很头疼,最后层层 patch 后会发现自己造了一个已有的轮子。建议一步到位上无中心化的方案如 Cassandra/NewSQL 等。 @lhx2008 这几个方案都有很多缺陷,最简单的就是客户端时钟不可靠 |
23
lhx2008 2019-04-19 12:20:43 +08:00
@yanaraika 这个确实是一个折中的方法,但是实现的复杂度也非常高了。当然 NoSQL 的经过验证的分布式的主从架构更加可靠。至于时钟问题,确实是一个存在的因素,我没有过多研究,不过 Redis 作者在说 RedLock 的时候,没有认为这是一个主要问题。"well, you can assume your clock is mostly correct though!"
|
24
guyujiezi 2019-04-19 12:28:10 +08:00
@huadi 如果你的业务代码写得正确,那是不会发生你所说的问题的。因为这个分布式模型是可以实现最终一致性的
我猜你的意思可能是:往从库里取得商品库存,由于数据没有同步,所以从库中的库存数量多余实际数量,然后程序因此创建了本不应该创建的订单。是这样吧? 然而你这个代码本身就是错误的呀 |
25
alvin666 2019-04-19 12:28:24 +08:00 via Android
我们做的主从是直接走的云服务商的内网,内网要是再波动了,就算不做主从也会受影响
|
26
yanaraika 2019-04-19 12:35:53 +08:00 via Android
@lhx2008 http://antirez.com/news/101 这个问题非常复杂。我个人是不喜欢 redlock 这种对时间源一个外界因素有依赖的方案的
|
28
calpes 2019-04-19 12:56:35 +08:00
@huadi 最简单的例子。。。你确认写入成功后不再次读库,直接将你写入的数据返回给用户,这就是一种很常见的操作,主从分离这个事某种意义上是无法做到对开发者透明的,而且这里你有一个钻牛角尖的思路,就是主从之间的网络可能一抖抖个一分钟啥的,需要业务代码来容错,如果把这些东西加到研发成本里,基本上是没法接受的。OP 角度上来讲每个服务都是有他的可用性指标的,五个 9 以上我觉得这种抖动很难感知到,而如果你的业务在主从分离的场景下由于 db 的可用性指标不够导致业务上大面积的出问题,我建议你们换个更专业的 DB 和 OP,毕竟服务可用性他是一个系统性工程。
|
29
idblife 2019-04-19 13:22:56 +08:00
楼主土鳖而已
哪家互联网公司不做读写分离能扛得住? |
30
wangxiaoaer 2019-04-19 13:39:59 +08:00
@jimrok #5 即使多个实例机器,那么同一个数据库的某张表你打算放到几台机器呢?如果 1 台,势必会有瓶颈,如果多台,不还是主从复制吗
|
31
passerbytiny 2019-04-19 13:43:00 +08:00
读写分离是抽象,分库分表是技术实现,啥时候被当成解决问题的两种方式了。人家文章的意思其实是:一般你用分库分表这种简单实现方式凑合以下就行了,要是扛不住,你得从根本的抽象上去寻找解决方案,比如说读写分离;至于具体是什么,限于篇幅无法细说(其实我也只知道个标题)。
读写分离这种标题很容易理解但内容很复杂的高级抽象,网上你就只能找到标题,想系统的学习,还是要买书。推荐你一本书《实现领域驱动设计》,里面“架构”、“事件”等章节有关于读写分离架构的描述。 |
32
jimrok 2019-04-19 14:03:22 +08:00
@wangxiaoaer 你这个问题,要结合业务场景来讲。就说订单这个东西,如果你的用户少,可以不做拆表处理的。当用户购买商品后,去查询订单,这个请求是个读请求,而且查询可能根据时间,状态过滤。这个列表数据是不容易做 memcahced 缓存的,所以,必须将这部分查询请求放在从库上执行。这样主库的压力就会降下来,放几台要根据你的 qps 量进行估算。如果每秒有 2w 笔查询,那你单台能支撑的是 5000qps,你就需要准备至少 4 台服务器。如果你再能在应用层将请求分割开,那么每台的 mysql 可能只负责某个区域的用户查询请求,那么 mysql 的 buffcache 就可能非常高效。
|
33
jimrok 2019-04-19 14:14:08 +08:00
@huadi 主从复制不是解决强一致性问题的,通常情况下的应用,不用害怕这个延迟。在运维层面来讲,通常有一个读写分离的 proxy,有这个 proxy 来代理 sql 端的请求。这个代理也同事负责监控主从库的监控状况,如果从库失败,这个代理会把 select 的语句发给主库执行的。如果你用过阿里云的 RDS,他们会给你一个 readonly 的 mysql 地址,还有一个读写分离的地址,你不需要考虑后端是否可用,这个 proxy 已经管理了后端的群集。
|
34
CRVV 2019-04-19 14:20:56 +08:00
如果一个数据库 instance 搞不定所有的读写请求,那么你可以使用多个 instance 来搞
有不同的方案来使用多个 instance,读写分离是其中的一个 或者说你也不一样要完全读写分离,只要把一部分读请求分走,也许就能好了 > 如果是异步同步,可以保证写主库成功之后返回,但保证不了延迟。比如写完马上读就会有问题 是会有问题,但有的业务可以接受有这个问题,或者用另外的手段处理这个问题。 或者你可以只把不需要强一致性的读请求分走。 > 如果等待同步从库成功后再返回,实际就是双写。那么故障几率就更大了,可用性就会降低 出故障的概率会变大,但是数据出错的概率会变小。某些情况下可以接受系统挂掉但是不能接受丢数据。 而且这么干的前提是你用一个 instance 搞不定,搞不定的事情就没有可用性,现在能搞定了才有了可用性。 这里不应该说可用性降低了。 |
35
Raymon111111 2019-04-19 14:22:51 +08:00
读写分离太多了
我们目前单库写入 qps 3000 (分库分表 5 个库), 读写分离框架用的很顺利 你说的主从延迟的问题, 绝大多数情况下都能控制在 100ms 内. 也就是说主库写入后从库很短时间内就可以读到, 能够满足绝大多数的使用场景 有一些很特殊的场景会采用强制读主库的方案, 但是这种一般控制量, 主库读影响写入性能 至于监控, 产品也很多, 最快的学习方法是进一个大公司, 一般这些东西很成熟, 你一看就明白了 |
36
tairan2006 2019-04-19 16:13:12 +08:00 via Android
喷了,用缓存本质上就是读写分离吧。网络分区下高可用和一致性是矛盾的,这不就是 CAP 定理么,楼主自己瞎琢磨啥呢。
|
37
zzzzzzZ 2019-04-19 16:54:34 +08:00
读写分离是标配,不是大招,不是人云亦云,是一个稍微水平正常的开发都能做的标配架构
如果你还卡在你第一个"如果",那就是你代码写的烂,别玩架构了 如果你出现了你第二个"如果",那就是你架构搭的烂,别玩架构了 你要的所谓的“中间件”,数据库层面的是 Mycat,就是 @jimrok #32 做的事 代码层面的纯代码就能解决,想不明白怎么敲代码,就别来纸上谈兵的学一些架构的东西,架构从来都是实践出来的 |
38
janxin 2019-04-19 17:45:43 +08:00
我觉得其实楼主你应该实操一下?空想很容易钻牛角尖。
另外一个,基本上根据 CAP 去牺牲某一个,根据业务需求再去考虑牺牲哪一个。 即便加上缓存,也是根据具体的业务需求,决定缓存 miss 和脏缓存的接受程度。 毕竟,没有银弹嘛。 |
39
phx13ye 2019-04-19 19:22:14 +08:00 via iPhone
借楼问问各位大佬,多对多关系的的中间表一般怎么分库分表。是要双向查的,只能冗余两份各自 sharding 吗?。例如我给哪些文章点赞了,文章被谁点赞。学生选修哪些课程,课程有哪些学生选修。
|
40
bringyou 2019-04-19 20:33:29 +08:00
分库分表限制了一些业务实现,比如说表之间的 join。一般业务场景下,对于数据的读取是多于写入(更新),所以为了那些特性,很多场景下都只是先读写分离了。而且一般的规模下这个也足够了。至于主从同步的延迟,阿里云 rds 是 1 毫秒级别的,很少超过 10ms,动辄 100ms 甚至 1min 的延迟,我觉得还是先招些专门的网络运维人员,先优化网络链路来的实在一些,因为网络慢不只是影响数据库主从同步,正常的业务应用也会受到影响。
如果对于 1ms 级别的延迟不能接受的话,可以向前面老哥提到的,再次查询写库,这个重复的工作可以通过 aop 或者中间件来做,自己不用重复的。 同步模型可以看看 mysql 的异步同步、半同步和组同步。 |
41
bringyou 2019-04-19 20:36:58 +08:00
@phx13ye 之前我司是这样。如果不局限于数据库的话,可以试试一些 newsql 或者 elasticsearch 这样的搜索引擎
|
42
akira 2019-04-20 01:05:09 +08:00
之前做读写分离,是把一些报表类的,不太要求实时性的查询 放在只读库 ,
大部分业务还是落在主库上面 |
43
curdoldman 2019-04-20 13:01:18 +08:00
SQL HINT 了解一下。
HINT 是一种可选的 SQL 片段或注释,可用于在 SQL 语句里强制索引,强制使用主库,强制不使用缓存等等 |
44
fox0001 2019-04-20 20:52:41 +08:00 via Android
我们是采用 Solr + 数据库,不完全的读写分离。但是 Solr 抗住了绝大部分的复杂查询,效果不错
|