2024-08-04
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/6982311714

回答

在分库分表中,我们大概率都会选择表的 id 来作为拆分的键,例如我们有一张 tb_user 表,它有四个字段 :idnamephoneaddress,我们利用 tb_userid 作为拆分 id。在大多数的场景下,我们都是利用 id 来查询 tb_user 表中的数据,这种情况是没有问题的。但是,有些场景我们需要根据其他不是拆分键的 id 来查询,例如查询 name = '张三',如下:

select * from tb_user where name = '张三'

由于 name 不是拆分键,所以我们无法定位到具体的某张表上去执行这条语句,于是就会对所有的 tb_user 表都执行一次:

对于这种情况,如果有 100 张表,就要查询 100 次,当然你可以使用 union 操作合并查询,但还是避免不了要查询100 张表。

所以,针对这种随着表越来越多,查询的表就会越来越多,这就说所谓的读扩散。

那怎么解决这种情况呢?目前主流的方案有三种:

  1. 建索引表
  2. 引入 ES
  3. 使用分布式数据库 tidb

建索引表

产生读扩散的根本原因就在于我们无法通过 name 字段来确定到具体的表,所以我们只需要新建一张 name 和 id 的映射表就可以了,这样当我们要通过 name 字段来查询,先到这个索引表里面去找对应的 id,然后再根据 id 去对应的分表找对应的数据:

这种方式虽然能解决问题,但是它的缺点非常明显:你需要维护两套表,有任何对 name 字段更新的场景,都需要维护两套表。而且这还是只有一个字段,如果有多个字段呢,就需要维护更多的映射表。而且开发工作量也比较大,所以这种方案,大明哥一般都不推荐使用。

引入 ES

建立索引表的思路和倒排索引的思路非常相似,所以对于这种需求,我们直接利用 ES 就可以了。ES 天然支持分片,内部利用倒排索引的形式来加速数据查询。比如上面的需求:

在 ES 中,它内部会以 id 分片键来进行分片,同时还会建立一个 name 到 id 的倒排索引,如果我们想以 phone 来查询,ES 同样会建立 phone 到 id 的倒排索引。利用 ES,我们无需自己维护 name --> idphone --> id 之间的关系。

同样,引入 ES 也非常简单,利用开源工具 canal 监听 mysqlbinlog 日志变更,再将数据解析后写入 ES,我们查询的时候直接查询 ES 就可以了。

使用分布式数据库

我们可以不适用分库分表的架构,直接使用 TiDB 替换。

TiDB 是一个分布式关系型数据库,它兼容 MySQL 协议和 MySQL 5.7 语法,在绝大多数应用下,我们是可以不需要做任何修改能够实现 MySQL 无缝切换到 TiDB 的。

理论上,TiDB 的单表支持十亿级别的数据量是毫无压力的,大明哥曾经有个项目,有个表有将近 12 亿的数据量,使用 tidb 查询也没有什么压力。

阅读全文