早期的架构基本都是这样,我们的应用程序在请求数据时,会先从缓存中获取数据,如果缓存中拿不到,再去数据库拿一次,拿成功之后在写入缓存,这样模式应该是很多公司前期开发模式。在这单体架构中会产生很多问题,tom 哥从常见问题的几个方面去帮大家踩坑,并提供解决方案。
数据库
我们的系统一上线,靠着"老奸巨猾"的产品设计的套路,源源不断的用户开始在我们的平台注册,3 个月内用户注册数量已经达到 3000W,这时候单表 3000W 的 user_info 表在瑟瑟发抖,这时候该怎么优化呢,很多同学都会说分库分表,但是具体怎么分呢?tom 哥会采用常见 hash 方案,首先我们可以把用户单独提出来做一个数据库(垂直拆分),这个数据库里我一次创建
user_info_0、user_info_1、…user_info_1023,
1024 张用户表,我们预估每张表 500W 数据,算下来 500W * 1024 = 51 亿 2 千万,这样的用户预估对于大部分互联网公司绝对够用了。但是随之而来带来很多新的问题,历史数据迁移问题、用户信息查询问题等等。先说说历史数据迁移吧。
历史数据迁移
我们的线上系统是一直再跑的,同步又发生在线下,这期间很难做到一致性,看过 tom 哥前面的文章应该知道历史数据迁移的时候会采用数据双写方案,也就是 2 个阶段。
第一阶段上线我们会同时往老表和新表各写一份,而迁移程序会在线下去跑,当然迁移程序和
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
线上程序也会存在一个先后顺序的问题,假如迁移程序在某个更新线程之后就很有可能会存在覆盖的情况(请广大同学自行脑补这个画面,一定要想明白哦~),所以我们必须加上版本号或者时间戳等方案,判断当前更新的数据是否等于数据库内存在的版本,以保证原子性,尤其是用户余额这一类的字段操作一定要谨慎。
//类似乐观锁的实现模式,如果能够实现幂等更优
update userInfo set balance = balance + #{money} where userId = #{userId} and version = #{version}
第二阶段在线下同步程序跑完之后,这时候老表和新表数据应该基本都一致了,这时候我们可以把老表的操作代码直接删除,查询相关的内容统一更换成新表的操作,然后再上线一次,就成功完成数据迁移的过程了。
用户信息查询
这个也是一个常见的问题场景,假设我们分了 1024 张表,用户数据均匀的落在了在 1024 表内,例如之前我们按照用户手机号去查询时,本来可以轻松的写成
select * from user_info where mobile like ‘%#{mobile}%’
换成现在的查询方式该怎么写呢
select * from user_info_0 where mobile like ‘%#{mobile}%’
select * from user_info_1 where mobile like ‘%#{mobile}%’
···
select * from user_info_1023 where mobile like ‘%#{mobile}%’
额…这不能把 1024 张表全部循环一遍吧,并且公司的运营人员可能会根据更多用户信息去检索用户,如果我们按照目前的分表方式确实需要过滤全部的表才能找到匹配电话号码的用户,这里要怎么做呢?
tom 哥总结的分布式法则,要用合适的工具去做合适的事,这里我们引入 elasticsearch nosql(PS:引入不同的中间件会大大提升系统的复杂度),我们可以根据用户需要搜索的条件将这些条件全部作为索引存入 es 内,这里 tom 哥是不建议数据库和 es 表结构保持一致的,可能查询时我们没有那么多需要过滤的内容,es 作为 nosql 数据库,我们应该把需要的热数据存入 es,,先从 es 内根据条件查到对应的用户信息,根据用户 id 再去对应的表查询详情数据,我们在 es 插入 doc 时,索引一个字段可以存入该条 doc 用户数据对应的数据库表名,这样就可以快速定位到从 DB 中那个表中查询到该用户的详细信息了。
引入 es 中间件我们也要同时考虑新库、老库数据迁移工程(解决思路可以继续使用数据双写方案),但是如何保证 DB 内数据与 ES 的一致性是新的问题,很多小伙伴应该都会遇到 db 与缓存不一致,db 与 nosql 不一致这类问题,我们该如何呢,cap 理论:
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。



