数据库体系架构
可用性设计
A、单点
在最原始的架构中,是单一数据库,一旦数据库宕机之后,整个服务都不可用,不存在高可用。
B. 主从复制
解决高可用的思路,就是冗余、复制;
在这种主从的架构中,即使Master节点挂掉,还有Slave节点,整个数据库的数据依赖存在,但是在这种架构中,无法保证读、写的高可用,而且会存在一致性问题。
C. "读"高可用
为保证读的高可用,可以对读(从)库进行冗余,但是冗余读库,也会存在副作用: 读写有延时,可能存在不一致。
在上图中确实是保证了"读"高可用,但是写节点依然是单点,不能保证写高可用。
D. "写"高可用
保证"写"高可用,就可以在上述架构的基础上,再冗余写库,采用双主双从模式。
而在这种架构下,两个主库,都会执行写请求,而且互相同步。
那么可能会存在以下问题: 双主同步,主键如果是自增的,会存在冲突?
解决方案:
a. 两个写库使用不同的初始值,相同的步长来增加id ; 1写库的id为0,2,4,6…;2写库的id为1,3,5,7… 。
b. 不适用数据库的自增ID,业务层机子生成一个唯一的ID。
读性能设计
在数据库中,我们为了提高读的性能,最常用的做法就是建立索引,但是如果索引过多,又会存在其副作用:
a. 降低了增删改性能;
b. 索引占内存多了,放在内存中的数据减少,数据命中率降低,IO次数增多;
A. 不同库建立不同的索引
主库只提供写操作, 不建立索引;
从库提供读操作,在从库上建立适当的索引 ;
B. 增加从库,负载均衡
这种做法上面已经提到,会存在主从不一致的问题,从库数量越多,主从延时越长,不一致问题越严重。
C. 缓存
①. 发生写请求时,先淘汰缓存,再写数据库
②. 发生读请求时,先读缓存,缓存命中则直接返回,没有命中,则查询数据库,并将查询的结果缓存在redis中(而此时旧数据可能入缓存)。
一致性设计
A. 引入中间件
通过中间件将key写操作路由到主, 在一定时间范围内,该key上的读也路由到主,当主从同步完成后再将读操作路由到从。
B. 读写都到主
读写都到主,不做读写分离,也就不存在主从不一致的情况。
C. 缓存两次淘汰
异常的读写时序,或导致旧数据入缓存,一次淘汰不够,要进行二次淘汰
a. 发生写请求时,先淘汰缓存,再写数据库,额外增加一个timer,一定时间(主从同步完成的经验时间)后再次淘汰
b. 发生读请求时,先读缓存,hit则返回,miss则读数据库并将数据入缓存(此时可能旧数据入缓存,但会被二次淘汰淘汰掉,最终不会引发不一致)
拓展性设计
在上述的架构中,针对于单库的可用性、读性能、一致性进行了分析,在电商系统的数据库中,数据量是特别大的,而单台服务器的容量、性能都是有限的,如果来完成扩容,则我们需要考虑到拓展性的设计。
A. 垂直拆分
根据业务划分,将不同的数据库表切分到不同的数据库上,以实现扩容的目的;
B. 水平拆分
将同一块业务的数据库表,进行拆分,将一张表的数据根据一定的规则(取模,hash等)切分到不同的数据库上。
而上述的架构中是没有考虑高可用的,当其中的任何一个节点挂掉之后,数据就不完整了,考虑高可用的架构如下:
C. 平滑、高效扩容
随着业务系统的扩张,数据库中的数据量会不断增加,如果实现扩容,最为直接了当的办法就是直接增加服务器,从而实现更多数据的存储;
如果按照以下的思路扩容,会存在大量的数据迁移
如何来完成高效、平滑的扩容呢, 可以按照以下架构进行。