mysql 读扩散

258 阅读2分钟

分库分表

  • 根据id范围分表
    • 涉及写热点表问题
  • 根据id取模分表
    • 数据均匀分布
    • 扩展还要涉及数据迁移
  • 范围 + 取模

读扩散问题

我们上面提到的好几种分表方式,都用了id这一列作为分表的依据,这其实就是所谓的分片键

实际上我们一般也是用的数据库主键作为分片键

这样,理想情况下我们已知一个id,不管是根据哪种规则,我们都能很快定位到该读哪个分表。

但很多情况下,我们的查询又不是只查主键,如果我的数据库表有一列name,并且加了个普通索引。

这样我执行下面的sql

select * from user where name = "小白";

由于name并不是分片键,我们没法定位到具体要到哪个分表上去执行sql。

于是就会对所有分表都执行上面的sql,当然不会是串行执行sql,一般都是并发执行sql的。

如果我有100张表,就执行100次sql。

如果我有200张表,就执行200次sql。

随着我的表越来越多,次数会越来越多,这就是所谓的读扩散问题

读扩散问题.drawio.png

  • 引入映射表 并用 查询条件作为分片键

    • 通过引入一个新表,倒过来,先用name查到对应的id,再拿id去获取具体的数据。这其实就像是建立了一个新的索引一样,像这种,通过name列反查原数据的思想,其实就很类似于倒排索引

  • 引入es,查询通过es 查询

    • mysql同步es.drawio.png
  • 引入tidb,实际上tidb对于读扩散也是通过引入映射分片表实现的

总结

  • mysql在单表数据过大时,查询性能会变差,因此当数据量变得巨大时,需要考虑水平分表。
  • 水平分表需要选定一个分片键,一般选择主键,然后根据id进行取模,或者根据id的范围进行分表。
  • mysql水平分表后,对于非分片键字段的查询会有读扩散的问题,可以用普通索引列作分片键建一个新表,先查新表拿到id后再回到原表再查一次原表。这本质上是借鉴了倒排索引的思路。
  • 如果想要支持更多维度的查询,可以监听mysql的binlog,将数据写入到es,提供近实时的查询能力。
  • 当然,用tidb替换mysql也是个思路。tidb属实是个好东西,不少厂都拿它换个皮贴个标,做成自己的自研数据库,非常推荐大家学习一波。
  • 不要做过早的优化,没事别上来就分100个表,很多时候真用不上