V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
pabno
V2EX  ›  程序员

10 亿用户数据分库分表设计

  •  
  •   pabno · 2020-02-28 23:34:48 +08:00 · 7893 次点击
    这是一个创建于 1765 天前的主题,其中的信息可能已经有所发展或是发生改变。

    使用 mysql 存储 10 亿的用户数据,用户表的设计是 (bigint user_id, bigint phone, varchar email)

    查询分为两类 case:

    1. 用户登陆,可能会使用 phone 登陆,也可能使用 email 登陆
    2. 登陆后操作用户数据

    目前方案:使用 user_id >> 32 % 2 分库,user_id >> 22 % 20 分表。user_id 由雪花算法生成

    针对 case 2,直接用 user_id 取模就很容易定位到具体到库和表,但是 case 2 需要使用 email 或者 phone 反查 user_id. 暂时想到的就是在对 email,phone 分别进行分库分表再做查询,然后将数据缓存到 redis 中。当用户使用 phone 或者 email 进行登陆时,先查询缓存中有没有数据,没有再计算相应当分片库和分片表进行查询

    不知道还有没有什么比较好的方案

    第 1 条附言  ·  2020-03-01 03:15:39 +08:00
    原本只是想为了简化说明问题,但是没想到适得其反。我们真实的业务场景是:每个 Android 设备可能存在很多类型的 ID,比如国外从 google play 下载的应用可以拿到 gaid,国内的没有 ga,就只能拿到 android。一共有 5 个这样的 ID 类型,一个设备可能同时拿到多个类型的 id. 我们就是为了要把这些 ID 整合起来,生成一个我们的 ID,然后基于这个 ID 做一些运营。其实就是一个很简单的场景,只要同一个用户的数据落到同一个库上,就不会有分布式事务问题,而且数据一致性要求比较低
    37 条回复    2020-03-01 15:36:45 +08:00
    daimaosix
        1
    daimaosix  
       2020-02-29 01:28:00 +08:00 via iPhone   ❤️ 2
    直接上 TiDB,都不用做分库分表,100%兼容 MySQL,就像不曾离开 MySQL
    akira
        2
    akira  
       2020-02-29 01:40:01 +08:00
    10 亿... 是国内的哪家大厂么。。
    peyppicp
        3
    peyppicp  
       2020-02-29 01:45:23 +08:00 via iPhone
    email,phone 分别存在两个分片库里,分别关联对应的 uid。比较操蛋的就是新用户进来的时候要双一写了。
    luxinxin
        4
    luxinxin  
       2020-02-29 01:48:44 +08:00 via iPhone
    其他还好,但感觉登陆并不是啥热数据,不需要 redis 缓存
    luckylo
        5
    luckylo  
       2020-02-29 07:22:26 +08:00 via Android
    @akira 用户数量能过亿行业就那么些。
    传统行业的运营商+银行 +某些垄断行业。这些基本上是 Oracle ,就算不是,也基本跟 mysql 没啥关系。然后就是互联网的那几个大厂
    mikulch
        6
    mikulch  
       2020-02-29 07:35:12 +08:00 via iPhone
    @peyppicp 你这个操作相当于数据异构啦,那不上 es 加列数据库了
    opengps
        7
    opengps  
       2020-02-29 07:59:10 +08:00 via Android
    登录名缓存下来,值为数据库的路由标识
    neoblackcap
        8
    neoblackcap  
       2020-02-29 10:08:29 +08:00
    同 @peyppicp 的解决方法,采取非规约化的方法对数据库进行处理,分别以 phone, email 为主键(cluster key)建表,关联 user_id。
    问题还是在于用户注册时就变得需要一次性写三个表。不过这个不算是大问题,哪怕是后面进行分布式事务,这个性能也是可以接受的。
    pabno
        9
    pabno  
    OP
       2020-02-29 10:53:03 +08:00
    @luckylo
    @akira
    这个只是我类比过来当一个场景,为了方便说明问题。实际上是 10 亿设备(国内外都有业务),但是不同设备可能会有各种不同当 id,比如 gaid,androidid,imeiid
    pabno
        10
    pabno  
    OP
       2020-02-29 10:53:37 +08:00
    @luxinxin 这个确实是
    pabno
        11
    pabno  
    OP
       2020-02-29 10:55:29 +08:00
    @opengps 想过这个问题,但最终还是需要持久化下来。不然很久没有登陆后重新登陆没有命中缓存就找不到路由标识了
    ifconfig
        12
    ifconfig  
       2020-02-29 11:14:41 +08:00
    请问下,直接用分区不行么,为什么要分表
    encro
        13
    encro  
       2020-02-29 11:48:07 +08:00
    应该没有更好的方案,如果通过号码查询的频率高,应该采用 kv 内存存储吧。
    没有试过 kv 和数据库 hash 索引,10 亿数量级性能会差别多少。
    楼主花半天做一个实验给我们看看?

    到达这数量级,就是一个尝试验证改良的过程。
    lovelife1994
        14
    lovelife1994  
       2020-02-29 11:58:46 +08:00
    @neoblackcap 如果登录数据不是热点数据,在有一级缓存的前提下,是不是单表做分区,在查询列上建 non-clustered 的覆盖索引就行了,这样能避免分库的带来的路由,双写和分布式事务的问题。感觉需要根据当前数据规模和预估增长的到的规模做下压测做取舍。如果 phone 和 email 都是可变更的,还要考虑分区,分表,分库间数据迁移怎么做。
    neoblackcap
        15
    neoblackcap  
       2020-02-29 12:25:56 +08:00
    @lovelife1994 我提的那边理论上只要开发支持(分布式事务,查询变更),你说的几点都解决了。无论是 phone, email 的可变更,还是查询的性能。我说的主要是 Cassandra 的解决方案。
    不过如同你所说,分布式方案肯定是有其他问题,路由,分布式事务。要结合生产环境取舍(压测)。这样的问题的确可以算是真正架构师的活了。
    neoblackcap
        16
    neoblackcap  
       2020-02-29 12:29:47 +08:00
    @ifconfig 分区还是单机,分表就可以多节点了
    justRua
        17
    justRua  
       2020-02-29 12:44:23 +08:00
    es 存一份用于搜索,mysql 里做分表分库,现公司是这么做的,不过用户数据是千万级,没上亿
    cabing
        18
    cabing  
       2020-02-29 13:00:31 +08:00
    如果是单纯的用户表应该不会涉及到跨表事务吧。
    一般的设计,登录相对应该是不频繁。case1 经常使用,case2 不那么频繁


    说一个简单的方案:

    针对 cace2 可以通过分布式的 kv(持久化)系统去存储,做备份就行,数据的变更,可以通过消息队列去更新。

    此外还有针对活跃用户,还需要定期比对 case1 中的和 kv 系统中的数据。

    kv 系统,可以考虑小米的 Pegasus,也可以找个类似的 kv 系统,也可以自己根据 rocksdb 撸一个。

    如果 kv 系统只是存储 uid 的,

    10 亿数据,如果只是存储 email=>(uid) phone=>(uid)

    256 * 1000000000/1024/1024/1024 = 238G,其实存储数据量不是特别多。


    此方案只是通过一次 email 或者 phone 寻址到 uid,后续的流程还是走到 uid 查询了。
    dragonsunmoon
        19
    dragonsunmoon  
       2020-02-29 13:21:00 +08:00
    全球接近 80 亿的人, 地球上每 8 个人,就有一个人是你们系统的用户,牛逼,呵呵
    dragonsunmoon
        20
    dragonsunmoon  
       2020-02-29 13:22:03 +08:00
    哦, 才注意到, 是设备,😅
    dragonsunmoon
        21
    dragonsunmoon  
       2020-02-29 13:24:43 +08:00
    不过设备也很牛逼呀, 假设单个设备的成本是 10.00 元, 光设备他妈的也要 100 亿元. 还不算分发,部署,运维,网络通讯费,后台带宽服务费, 后台服务器,存储等费用. 这个系统真弄下来, 耗费巨大呀
    brucefu
        22
    brucefu  
       2020-02-29 14:27:13 +08:00
    这些数据分表就可以了啊,为嘛要分库
    brucefu
        23
    brucefu  
       2020-02-29 14:30:38 +08:00
    @dragonsunmoon 注册的多,活跃的少
    mreasonyang
        24
    mreasonyang  
       2020-02-29 18:18:46 +08:00 via iPhone
    @dragonsunmoon 这个很常见啊,又不是自然人
    GDC
        25
    GDC  
       2020-02-29 18:50:57 +08:00   ❤️ 1
    @akira 用户 10 亿的大厂不需要来 V2 问这样的问题…
    mysunshinedreams
        26
    mysunshinedreams  
       2020-02-29 19:57:29 +08:00
    @dragonsunmoon 其实你理解的太绝对了,举个例子,一台手机,可能对应几十个设备 ID,因为用户存在各种各样的复原的操作,举个实际的例子,亚马逊对 Kindle 设备的管理,如果你经常重做系统的话,就会发现它们的识别策略是每一次都会识别为一个新的设备。
    dragonsunmoon
        27
    dragonsunmoon  
       2020-02-29 22:58:27 +08:00   ❤️ 2
    @pabno 不好意思,我的回复是稍微带着点调侃的味道,先对你说声 sorry
    虽然是带着点调侃的话,可是我想说一下技术外的问题. 先从"10 亿用户"这个数量来说. 题主在问题里给出的表设计描述: 用户表的设计是 (bigint user_id, bigint phone, varchar email). 用户是以手机和电子邮箱为用户标识的.
    @mysunshinedreams 但从表的字段来说,注册的对象背后是手机号码和邮箱这种身份标识资源, 与具体设备无关.不存在一个手机对应几十个设备 ID, 因为关注的是 phone 和 email
    因为现在是移动互联网,所以普通用户使用邮箱注册的毕竟是少数,我们先假设 10%的用户是使用邮箱注册,另外 90%使用手机注册. 抛开 1 亿个邮箱注册的用户, 剩下 9 亿个手机号码. 再假设人均 2 个号码(实际生活里, 绝大多数"正常用户"只会使用一个手机号码, 并且,人均 2 个号码这个数值其实已经给得很高了,这里是为了方便计算, 如果调低了,只会让计算出来的真实注册用户数更多).

    回到之前的描述, 假设人均 2 个号码都注册了, 那么使用手机号码注册的人数也有 4.5 亿. (还没有把使用 email 来注册的用户算进来). 4.5 亿个用户, 大家能理解这是个什么概念吗?
    如果题主所在的公司是一个小公司或者初创公司. 我认为 10 亿用户 数,根本就是个伪命题. 因为, 很大程度上, 等系统上线后, 能不能撑到 100 万的注册用户数, 都是个未知数. 所以担心一个几乎根本不可能发生的问题, 没有必要.
    如果题主所在的公司, 依旧有一定的规模. 有超过 100 万的注册用户, 我觉得这个公司也应该有钱请个资深的架构师来解决这个问题(重构系统), 就像 @GDC 所说的,应该也不会来 V2 问这样的问题了.
    这让我想起来多少年前业界的一个笑谈. 一帮人创业, 先用 ruby on rails 开发出产品原型, 上线. 然后融到资, 接着再招聘另外一帮 java 的人, 把 ruby 写的系统再重构到 java 上. (Twitter 还真就是这样的)

    @pabno 我觉得, 你现在所处的公司应该是个初创企业(我猜的, 如果我猜错了话,这些回复内容就当做废话随便看看吧) 你其实你现在根本就不需要担心 10 亿用户这个问题. 因为到 10 亿用户, 整个系统面临的不仅仅是用户登录查询的问题. 10 亿用户, 假设 10%的的活跃用户, 那就是 1 亿的活跃用户. 这些用户在你们系统里的行为活动, 会产出大量的数据. 这已经不单单是一个用户表设计的问题了.
    量变引起质变, 海量用户数据的处理,会引申出一系列的问题, 单机数据库在性能(tps/qps), 容量上不够. 需要分布式数据库, 按照业务领域进行垂直分隔, 按照数据进行水平拆分. 分库,分表,数据归档. 引入 no sql 的数据库在应用层建立索引服务, 媒体数据的海量存储, 加速访问的 cdn. Api 应用服务的负载均衡, 微服务,集群,数据容灾备份等等. 更别说, 处理海量数据所需要的云计算的云主机,带宽,存储等资源, 需要的也是流水般的费用呀.

    但是, 对于一个初创公司的初创项目, 压根就不需要考虑海量数据问题. 因为初期压根就不会遇到海量数据带来的问题.
    不要提前过渡设计. 一开始也不要把问题给复杂化. 不要试图做一个完美的系统. 不要考虑你几乎不会遇到的问题.
    dragonsunmoon
        28
    dragonsunmoon  
       2020-02-29 23:38:18 +08:00
    @pabno 我才留意到你在后面的回复里, 指出是以用户表设计来当做类比场景的.
    但是, 即便以设备数来说, 10 亿个设备数也是很大的数字. 考虑到 @mysunshinedreams 所说的, 对应几十个设备 ID 的情况, 会有,但是不会是普遍情况, 对应几十个设备 ID 的情况占比应该很小的. 所以我们粗略的给定一个设备的 ID 重复产生系数, 假设为 2, 那么也有 5 亿的设备. 这个设备数量已经很高了. 5 亿设备中, 假设有 1 亿个活跃设备, 每天产生 10 条数据. 每条数据假设为 1KB (因为不清楚业务类型,不清楚实际情况,这个值可能高, 也可能低了, 暂时先用这个值来描述问题), 一年产生的纯数据容量(数据进入到数据库后, 还会有索引等, 所以实际需要的存储容量只会比这更高) 约为: 100,000,000 * 10 条 * 1 KB / 1024 / 1024 / 1024 * 365 天 = 339.93 TB

    所以, 真有 5 亿个设备, 是会产生海量数据问题的. 而海量数据问题的性质, 复杂度, 解决方法, 已经超过表设计这个问题范畴了.

    初期,不要考虑海量数据问题. 因为, 很大概率上, 公司都不能撑到有那么多的用户量 /设备量.
    如果真的遇到了海量数据问题, 那么恭喜你们! 你们项目,你们的事业已经成功一大半了.
    pabno
        29
    pabno  
    OP
       2020-03-01 03:19:29 +08:00
    @justRua 有考虑过这个方案,不过太贵了
    pabno
        30
    pabno  
    OP
       2020-03-01 03:23:37 +08:00
    @cabing 原来也想着映射关系方 redis 做持久化的,但是一个设备可能一两个礼拜才会做一次登陆
    pabno
        31
    pabno  
    OP
       2020-03-01 03:36:23 +08:00   ❤️ 1
    @dragonsunmoon 我们海外和国内都有业务,设备数还是比较大的。其实现在这些数据都有落地在我们的大数据平台,现在是想整合这些 id 做营销,最主要是生成一个统一的 ID。据反馈 19 年 Q4 以来的设备数就已经有 2 亿了,这还是 18 年底被谷歌点名后设备数大幅度下降后的

    老板的事业是挺成功的,我只能争取努力努力,帮老板再买一辆玛莎拉蒂
    dusu
        32
    dusu  
       2020-03-01 03:48:57 +08:00 via iPhone
    光从性能角度来说

    一级热数据进 redis (取最近登录多少天)

    二级进 pika 或 ssdb 这类 kvdb (取最常登录多少次)

    三级命中走 mysql 分表查询

    无论存还是查 100 亿都问题不大
    staticor
        33
    staticor  
       2020-03-01 08:20:36 +08:00
    少了描述背景, 会带来更多的问题

    有种 X/Y problem 的味道
    cabing
        34
    cabing  
       2020-03-01 11:53:10 +08:00
    @pabno

    如果觉得 redis 成本高。

    持久化方案不一定非得放内存。

    ssd+lru(内存)


    ssd 1T 也不贵。内存不用太大,lru 就行。
    sagaxu
        35
    sagaxu  
       2020-03-01 11:54:09 +08:00 via Android   ❤️ 1
    去年就有两亿设备,业务遍布全球,这样的公司没有架构师也没有 dba ?难道是触宝么
    mysunshinedreams
        36
    mysunshinedreams  
       2020-03-01 12:35:11 +08:00
    @dragonsunmoon 你忽略了黄牛,黄牛的设备并不一定是真实设备,但是他们能弄出庞大的设备群体,举个例子,X 团,一些低价促销活动,会有很多黄牛再抢,他们用的就不是真实的设备。
    watsy0007
        37
    watsy0007  
       2020-03-01 15:36:45 +08:00
    分区就可以了吧. 查询压力有多大
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1031 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 19:20 · PVG 03:20 · LAX 11:20 · JFK 14:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.