首页 MySQL

MySQL面试题,亿级数据库分表方案

pyweeX 发布于 02-16
MySQL
pyweeX

面试官问了一个MySQL分表的问题,让我出一个可行的MySQL优化方案,解决他们的实际业务。

问题是这样的:有一张用户表,记录了邮箱和手机号字段,让用户可以使用邮箱或手机号登录,现在已经有了过亿数据(暂且不考虑真假),该如何分表才不影响现有的业务?而且不能依赖中间件(如redis)。

user 数据表结构

id email phone
1

首先需要说明一下,本文章写的并不是一个标准答案,只是写下自己的思路仅供参考,肯定有不完善或者不正确的地方,欢迎指正。


关于MySQL分表,我之前在工作中有实际使用过,无非就是横切、竖切或者横+竖切几种方式,主要得结合业务,有时候还得考虑客户端实际的交互。

这种业务的分表并不像平时的日志分表那么简单暴力,大多数情况下,日志表只是做记录,分表的时候可以按月份或者按年份横向切分,比如按月分表,分成12张,log1~log12,查询的时候根据月份做查询条件就能定位到具体的表名字。也还有一些大型网站,对订单表做横向切分,只支持查询一年或半年内的数据,这种做法比较直接,也是由业务而定。

用户表不太一样,用户表涉及到比较频繁的增删改查,特别是这里涉及到的,查询的时候需要支持多个关键字段(email 和 phone)的条件查询。

单纯竖切在这种情况下不会起到分表作用,至少解决不了这种业务实际要解决的那个问题,采用竖切的场景主要是针对一张有着非常多字段,尤其是很多冗余字段的数据表进行的工作。对于当前这种业务,如果只是竖切,将 phone 字段单独移出来与uid建立一张user表,将 email 字段移出来建立另一张user表,反而加重了数据库的存储,而且这两张表的记录数还必须与原来的user表记录数一致,这样就凭空多了两亿条数据了~

user_phone

uid phone
1

user_email

uid email
1

我的解决方案是这样的,竖切+取模平行切。将手机号单独存到若干张表,邮箱号单独存若干张表。

因为手机号和邮箱是可以很明确通过正则表达式区分出来的,所以这不是要担心的地方。我要做的是在程序端就能确定两种规则针对这两个属性,当后端在取得手机号或者邮箱的时候,就能根据自己制定好的规则得到具体的表名称,或者说表的索引编号,这个索引编号就是自己手动分表时根据自己制定的规则去算出来的。

根据邮箱号码分表

邮箱通常由字母+数字组成,所以通过ascii码能直接把邮箱中的每个字都转换成十进制的数字,所以我将邮箱的每一个号码直接转成十制,再将它们相加起来,再把得到的结果进行取模,取模是手动分表最常见的方式,如果我要将这一亿条数据放在100张MySQL表里面,我就将每个邮箱计算得到的结果%100。

看代码吧,我随便打了一些邮箱进行模拟,如有雷同纯属性巧合,以下是go语言代码:

MySQL分表

上面的代码一看就懂,假如我们要将这1亿条数据分成100张表来保存,那么就将每一个邮箱计算出来的结果取余100,得到的余数就是表的索引,代表这个邮箱应该落在哪个分表上,执行代码之后看效果:

数据库分表

看,效果很不错,我们看到了第一个邮箱会被索引到 user_80 这张表。


根据手机号码分表

手机号的分法可以更直接一些,因为考虑到每一个手机号都是1开头,所以用它来做计算意义不大,我这里只取手机号的后10位数据,将每一个数字转换为整型,然后把它们全部相乘,假如有手机号是 19999999999 那么最大值就是9的10次方,一个整型变量完全可以存下来不会溢出。

看代码,假如将用户手机号拎出来,存到30张表当中,每个手机号经过以下逻辑,都会很好的均摊出去。

go语言

现在模拟了处理最大的这个199的手机号,执行后它落在 user_21 这张表下面。

以上就是我的MySQL数据库分表的思路,当然肯定不够完美。

我查了一下网络上针对手机号分表的方式,有程序员使用手机号的前三位作为索引进行分表,比如189开头的号码分到表a,136的分到表b等等…仔细想一下,这可能会导致数据更加不均匀,你怎么看?


对于我这种mysql优化方式,有两个明显的缺点。

第一个缺点就是这样做的话,分表的时候比较被动,因为我们要提前确定好要分成多少张表,只有确定好分多少张表才能用表的数量取模。我这里根据邮箱分成了100张表、根据手机号分了30张表。如果数据不断膨胀,想要再次扩展更多的表,就需要修改规则,原有的索引会被全部重新打乱,改动成本是很高的。

第二个缺点是这种做法不能完全均摊这1亿条数据,会导致有些表可能有上千万数据,而有的表只有几十万、几百万数据。

但总的来说,我认为这种方式比较适合这个业务的,不结合业务去盲目做优化,恐怕会很容易带来新的问题。因为我们在这个场景里面要考虑对 user 表的增、删、改、查。通过这个规则进行的分表能够满足这些条件,我们可以在用户使用邮箱注册的时候就能确定它应该插入到哪张表上,查询和更新的时候也是如此。

声明: 因编程语言版本更新较快,当前文章所涉及的语法或某些特性相关的信息并不一定完全适用于您当前所使用的版本,请仔细甄别。文章内容仅作为学习和参考,若有错误,欢迎指正。

讨论 支持 Markdown 语法 点击演示
回复
评论预览框

开发者

开发者·注册登录
  • 获取验证码
  • 取消