V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MySQL 5.5 Community Server
MySQL 5.6 Community Server
Percona Configuration Wizard
XtraBackup 搭建主从复制
Great Sites on MySQL
Percona
MySQL Performance Blog
Severalnines
推荐管理工具
Sequel Pro
phpMyAdmin
推荐书目
MySQL Cookbook
MySQL 相关项目
MariaDB
Drizzle
参考文档
http://mysql-python.sourceforge.net/MySQLdb.html
bubuXiaoqi
V2EX  ›  MySQL

[求教] 阿里 Java 编程规范:避免使用 IN。 那这个业务场景不用 IN 还有什么好方案么?

  •  
  •   bubuXiaoqi · 2021-05-31 10:57:13 +08:00 · 6834 次点击
    这是一个创建于 1305 天前的主题,其中的信息可能已经有所发展或是发生改变。

    公司里面有 10W 个客户,员工 A 负责其中 2W 个客户。 (数据在用户库里面)

    现在 员工 A 要查询 [自己负责的所有客户] 的实时交易明细。(数据在订单库里面,和用户库是两个实例)

    目前的方案是查这 2W 的客户的 ID,然后在交易明细里面进行客户 IDS 的过滤。

    但是这样 IN 的内容就特别大,而且可能还会变大。今天看到 JAVA 编程规范里面说避免使用 IN,那这样还有什么好办法么。

    因为 员工看自己负责的客户的 账户、订单、交易、等业务表太多太多了。每个表目前都是 in ids 的方案。

    第 1 条附言  ·  2021-05-31 11:32:10 +08:00
    员工和客户的关系是
    员工负责标签。 客户可以有多个标签。 所以是一个动态的关系。 一个客户可能被多个员工负责而且会变。
    所以没有办法存员工 id 。
    表关联的方式好像比较看索引,因为是大表间关联 。
    64 条回复    2021-06-11 16:17:31 +08:00
    BBCCBB
        1
    BBCCBB  
       2021-05-31 10:58:17 +08:00
    看能不能改成 join.
    bubuXiaoqi
        2
    bubuXiaoqi  
    OP
       2021-05-31 11:01:38 +08:00
    @BBCCBB 因为在不同的数据库实例里面了。所以没有办法用 join
    bubuXiaoqi
        3
    bubuXiaoqi  
    OP
       2021-05-31 11:02:17 +08:00
    用户、订单、商品 等都是单独部署的服务器和数据库
    statement
        4
    statement  
       2021-05-31 11:03:09 +08:00
    mysql 最大 1000 。oracle 最大 5000 如果 sql 不知道怎么写。 就分批 in
    buster
        5
    buster  
       2021-05-31 11:04:42 +08:00
    DBLINK 这种方案呢
    clf
        6
    clf  
       2021-05-31 11:13:24 +08:00
    试着跨数据库 join ?只要几个数据库的类型是一致的,各家都有跨服务器数据库查询的方法。

    还有就是所有表都存一个负责人的 id 。
    eason1874
        7
    eason1874  
       2021-05-31 11:14:44 +08:00
    可以考虑楼上说的 DBLink

    但如果是我,有可能的话,我直接在订单库维护一个客户关系表,反正占不了多少空间
    Jooooooooo
        8
    Jooooooooo  
       2021-05-31 11:15:53 +08:00
    in 特别多 mysql 踩不上索引, 200 个差不多是极限了.
    bubuXiaoqi
        9
    bubuXiaoqi  
    OP
       2021-05-31 11:23:43 +08:00
    @lychs1998 主要数据量也非常大。 哪怕都在同一个数据库 in 太多了用不上索引速度都很慢
    bubuXiaoqi
        10
    bubuXiaoqi  
    OP
       2021-05-31 11:24:58 +08:00
    @lychs1998 员工和客户的关系是会变的 而且一个客户可能会被多个员工负责。。。 就是员工负责 部分标签的客户。 所以不同的员工可能都会负责同一个标签
    bubuXiaoqi
        11
    bubuXiaoqi  
    OP
       2021-05-31 11:25:09 +08:00
    @Jooooooooo 对 所以现在很头疼
    bubuXiaoqi
        12
    bubuXiaoqi  
    OP
       2021-05-31 11:26:12 +08:00
    @eason1874 这个方案还是要进行数据库的关联过滤么。 但是大表关联大表 之前没有拆之前就是关联查询的 效果好差。
    bubuXiaoqi
        13
    bubuXiaoqi  
    OP
       2021-05-31 11:26:56 +08:00
    @statement 分批 in 的话 不仅数据要进行汇合 。而且面对过滤分页的场景处理不了。
    bubuXiaoqi
        14
    bubuXiaoqi  
    OP
       2021-05-31 11:28:36 +08:00
    @buster 最早的时候所有数据库都在一起的时候。效率都挺差的 因为数据挺多的。 除非针对每个 SQL 针对性的给索引 不然大表间关联 之前用的也很慢
    biuaxia
        15
    biuaxia  
       2021-05-31 11:31:46 +08:00
    同问,先关注一波。有好的回复麻烦 call 一下。
    @bubuXiaoqi
    bubuXiaoqi
        16
    bubuXiaoqi  
    OP
       2021-05-31 11:32:36 +08:00
    bubuXiaoqi
        17
    bubuXiaoqi  
    OP
       2021-05-31 11:33:37 +08:00
    @Jooooooooo 有一个参数可以改 in 的内容的值。我目前改的不小。但是这个方案本身好像不长久。而且性能也不行
    xwayway
        18
    xwayway  
       2021-05-31 11:38:04 +08:00
    总共 10w 客户,一个员工就 2w 了。这么厉害。2w 客户,感觉员工要忙死
    dswyzx
        19
    dswyzx  
       2021-05-31 11:43:20 +08:00
    ids 临时表然后 join or exists
    Jooooooooo
        20
    Jooooooooo  
       2021-05-31 11:45:35 +08:00
    @bubuXiaoqi 那个值只能算是临时解决, 改太大了也不行, 很影响性能

    考虑把数据异构一下吧, 这种特殊业务逻辑单独弄个表?
    bubuXiaoqi
        21
    bubuXiaoqi  
    OP
       2021-05-31 11:49:13 +08:00
    @Jooooooooo 你的意思 最合适的还是关联表查询过滤么
    wfd0807
        22
    wfd0807  
       2021-05-31 11:49:20 +08:00
    换一个思路,楼主描述的“ 员工 A 要查询 [自己负责的所有客户] 的实时交易明细”,这个需求真的没有问题吗?
    实时交易明细,是全部的交易明细吗?如果是的话,批量查询 2w 用户的全部交易明细的目的是啥呢?就算给员工 A 实现了这种查询,你确定有意义吗?我敢打保票,他会在点查询按钮之前,先筛选用户
    bubuXiaoqi
        23
    bubuXiaoqi  
    OP
       2021-05-31 11:56:39 +08:00
    @wfd0807 比如说 我要看我今天所有客户产生的明细或者明细的报表 ,这个时候我只有时间段的 不会针对到具体的客户的
    buster
        24
    buster  
       2021-05-31 12:02:47 +08:00
    如果现在 in ids 的代码还可以支撑运行,那完全可以考虑把所有的表都放在一个库里,之后使用 join 来解决 in ids 的问题,之后再考虑优化查询效率(逻辑、索引、分库分表)
    wfd0807
        25
    wfd0807  
       2021-05-31 12:15:06 +08:00
    @bubuXiaoqi 所以,你这个需求的真相是想查看再加工数据和报表,这应该通过 BI 手段是实现
    wfd0807
        26
    wfd0807  
       2021-05-31 12:21:51 +08:00
    @bubuXiaoqi ps,目前的分库方案没看出来有什么实际应用上的优势,如果不分库的话,楼上都说明了,join 可以解决 in ids 的问题,慢就优化,就算优化需要分库分表了,也不应该是现在这种分
    jorneyr
        27
    jorneyr  
       2021-05-31 12:56:59 +08:00
    冗余列存储关系
    admol
        28
    admol  
       2021-05-31 13:13:11 +08:00
    交易明细里面记录下员工 A 的 Id ?
    clf
        29
    clf  
       2021-05-31 13:22:04 +08:00   ❤️ 1
    其实感觉就是你们的业务数据存储的方式(数据结构)出了问题。另外就是分库分的看上去像徒增成本。跨服务器间查询会有连接成本的。

    你们用户标签估计是用逗号分隔丢在一个字段里的?也不知道员工会不会负责多个标签。

    其实也可以考虑引入 Elaticsearch,存进 Mysql 的数据都同步进去,员工搜索用户信息的时候放在 ES 里进行。
    yitingbai
        30
    yitingbai  
       2021-05-31 13:26:45 +08:00
    我也为这个问题头疼, 似乎没有比 in 更好的方法, 因为 in 太好用了
    lidashuang
        31
    lidashuang  
       2021-05-31 13:52:19 +08:00
    redis set
    geminius2333
        32
    geminius2333  
       2021-05-31 14:04:17 +08:00
    收藏一波,看看大佬们的回复
    bubuXiaoqi
        33
    bubuXiaoqi  
    OP
       2021-05-31 14:07:47 +08:00
    @lychs1998 员工 标签 和客户是三张表和两个映射表。 用 es 的话主要是设计的业务表太多了 都得弄一份 太麻烦了。。
    TomVista
        34
    TomVista  
       2021-05-31 14:18:17 +08:00
    客户的标签可以修改吗? 是不是可以构建一个 员工-标签-订单的关系?
    bubuXiaoqi
        35
    bubuXiaoqi  
    OP
       2021-05-31 14:18:53 +08:00
    @TomVista 客户的标签可以修改
    zoharSoul
        36
    zoharSoul  
       2021-05-31 14:31:55 +08:00
    拉到 es 里面搜索, 搜到到 id 回 mysql 查
    bubuXiaoqi
        37
    bubuXiaoqi  
    OP
       2021-05-31 15:01:37 +08:00
    @zoharSoul 如果业务重新设计的话 可能会这样做, 但是这样做不了分页。比如把 T1 表的数据放入 ES 了 ,业务有的需要进行 T1 T2 的关联过滤分页查询。就做不到了。
    hmdsw
        38
    hmdsw  
       2021-05-31 15:01:53 +08:00
    exists?
    glacial
        39
    glacial  
       2021-05-31 15:14:45 +08:00
    @bubuXiaoqi 建一张员工与客户的关联表, 到你订单库里,然后通过 消息中间件或 cdc 同步这张关联表
    PiersSoCool
        40
    PiersSoCool  
       2021-05-31 15:52:31 +08:00
    分批 in

    话说这种数据肯定没有意义,这么多数据,能看得过来吗?一次看 2000 * 交易量的数据,怕不是真需求...
    romisanic
        41
    romisanic  
       2021-05-31 16:35:40 +08:00
    记得之前跟同事聊,淘宝这种客户量极大的平台,几乎任何一样业务数据都会单独存一份,但是因为存储并不值钱,而且有些信息不是要求特别实时的,所以可以冗余存储。

    这里可以在其他地方存一份索引,比如前边有同学说的 es,至于你说的不好分页,既然要用 es 了,我觉得你不如把分页的条件考虑进去,一块做到 es 里。
    bubuXiaoqi
        42
    bubuXiaoqi  
    OP
       2021-05-31 17:34:02 +08:00
    @romisanic 我感觉也确实是只能 聚合数据到 es 然后考虑到各种分页情况 字段都提前设计好了。 额外还有一个 es 数据和 mysql 数据的一致性成本
    akira
        43
    akira  
       2021-05-31 20:19:25 +08:00
    现在有性能问题么,没有的话就不用管他
    lldld
        44
    lldld  
       2021-05-31 20:49:31 +08:00
    如果这个员工的需求是有意义的, 那么最好是弄个 job, 每天把全公司的交易明细都处理一遍, 分员工出报表, 不需要做数据库的 IN 查询.
    makdon
        45
    makdon  
       2021-05-31 21:02:14 +08:00
    把员工 A 的 id 作为交易明细的一个维度冗余进去呗?
    然后就可以 select * from order where manager=员工 A
    lldld
        46
    lldld  
       2021-05-31 21:03:53 +08:00
    "一个客户可能被多个员工负责而且会变。
    所以没有办法存员工 id "

    按说即使是多个员工负责, 也不会超过 10 个吧? 拼接多个员工 id 存在一个字段, 用 like 应该可以.

    如果觉得效率低, 可以把员工 id 分组, 比如用 4 个字段存负责的销售 id: sales00, sales01, sales02, sales03, 按照 id%4 分别存储(hash 更好).

    sales00 => #100#,#104#
    sales01 => #101#
    sales02 => #1002#,#1006#
    sales03 =>

    现在查询员工 1002 的客户的明细:
    select * from trades where sales02 like '%#1002#%'
    xuanbg
        47
    xuanbg  
       2021-05-31 21:15:53 +08:00   ❤️ 1
    这个需求不是一个简单查询能做得好的。就别去想什么 in/join 或者 es 什么的了,根本不解决问题好吧。

    客户经理查询 2w 客户的交易明细是不可能的事情,我能查得出,他也看不了。所以在没有具体需求的情况下,就不深入了。
    xiaomingVTEX
        48
    xiaomingVTEX  
       2021-05-31 22:00:53 +08:00
    这个可不可以加一张表用来存交易 + 员工呢
    cubecube
        49
    cubecube  
       2021-06-01 00:22:13 +08:00
    in 性能很差的,尤其是 in 里面如果有 null,貌似逻辑还有问题

    > An in statement will be parsed identically to field=val1 or field=val2 or field=val3. Putting a null in there will boil down to field=null which won't work.
    arvinsilm
        50
    arvinsilm  
       2021-06-01 08:37:31 +08:00
    考虑出一张标签+明细的关联表吧,员工直接和明细对应不是好的解决方案
    bthulu
        51
    bthulu  
       2021-06-01 08:46:42 +08:00
    你 in 试一试, 能满足业务需求就 in, 不要拘泥于什么规范, 规范时死的, 人时活的
    timethinker
        52
    timethinker  
       2021-06-01 09:22:48 +08:00   ❤️ 4
    好吧,又是一个没有贴出具体 [数据库以及版本] 的问题。

    使用 IN 语句在新版本的 MySQL 中是没问题的,加好索引就行,唯一的问题就是如果 IN 太多的值,可能会导致数据包超过 max_allowed_packet 设定的值。

    如果你的心里存在怀疑,那么最好的办法应该在自己的数据库上好好测试一下,看看是否真的存在性能问题。如果你想我们帮你测试,那么至少要列出数据库以及它的版本,更详细的可以列举到数据量、索引是否建立、硬件环境等信息。

    我觉得现在很多关于数据库的使用说法有点偏中医理论了,这不能用那不能用,很多时候这些问题都是出现在特定数据库的特定版本。人云亦云,迷信上个世纪的偏方和奇技淫巧,殊不知那些东西早就已经发生了变化。
    qiumaoyuan
        53
    qiumaoyuan  
       2021-06-01 10:12:34 +08:00
    @qwe520liao 然后流传出来出现人传人的现象,大家还纷纷以为得到了真理。
    qiumaoyuan
        54
    qiumaoyuan  
       2021-06-01 10:16:28 +08:00
    其实在我看来很多公司这种所谓的规范,就是为了某些人嘴上说的“软件工程的目的就是让低水平的人也能干活”,让大家不需要思考就能做事,造成的结果就是愚化员工,推动了视具体情况而选择工具和方法的能力。软件开发本没有那么多条条框框。
    qiumaoyuan
        55
    qiumaoyuan  
       2021-06-01 10:16:50 +08:00
    推动了视具体情况而选择工具和方法的能力 -> 失去了视具体情况而选择工具和方法的能力
    THESDZ
        56
    THESDZ  
       2021-06-01 10:29:24 +08:00
    既然是根据交易流水来的,不如使用 mq 做预处理.
    timethinker
        57
    timethinker  
       2021-06-01 11:50:18 +08:00   ❤️ 1
    @qiumaoyuan 规范的最终目的是服务于团队的,也就意味着想要提升整个团队的工作效率,就像代码是给人看的而不是给机器看的。进一步落地的方案可能是统一命名风格、禁用某些特性。时代在进步,同样我们使用的数据库或者其他中间件也在不断的改进和优化,但是很多方案却止步不前。开放学习和进步的团队文化是至关重要的,在我看来,现在很多的规范都已经过时了,也许在那个时候确实是有理由这样做,但是发展到现在就有点像用明朝的剑斩清朝的官。

    反映出来的现象就是,由于很多人学习了这些早期项目的编码实践,后来又被其他人给继承,我不知道他们有没有仔细想过这些细节,但是科学的方法是进一步测试,得出实实在在的证据表明这些实践是否仍然有效。试图发现和总结出现这些现象的原因是另一个问题。

    回到主题,对于楼主这个问题,我认为不在于 IN 本身是否好坏,而在于你的业务场景是否适合使用 IN,在 OLTP 场景,响应时间是至关重要的,同时也要保证数据的一致性和完整性,解决思路就是在时间和空间之间取得一个平衡。

    对于下单来说,大多数系统需要保证操作及时响应,这也就意味着你不能进行太多耗时的操作(大部分时间花在了 IO 上),但是对于查询来讲,实时性也许就不那么强,或许可以通过异步的方式产生派生数据(即根据订单数据生成另一份数据),这一部分数据可以专门优化为方便查询的结构。当然是否采取这种方法取决于楼主业务的实际使用场景以及我方才这些假设是否成立。
    qiumaoyuan
        58
    qiumaoyuan  
       2021-06-01 13:39:18 +08:00
    @qwe520liao 我觉得你把 52 楼的最后一句话又重复说了好几次,写成了四段话……其实有些东西只要是遇到过、思考过,从别人一句很凝练的话里就可以明白对方的意思并且认同。我们是没有分歧的。
    timethinker
        59
    timethinker  
       2021-06-01 17:29:34 +08:00
    @qiumaoyuan 没有分歧,我只是想回复你一下,然后分享一些关于这件事情的一些想法,前面两段内容是我的一些看法。后面两段也算是具体回复一下关于这个帖子的一些解决思路。
    opengps
        60
    opengps  
       2021-06-01 19:29:53 +08:00
    能用 in 实现的,往往可以轻松把原本 in 的字段作为左表进行表连接,我最近刚有过一个这样的功能体验过,一分钟查不出来的改成 leftjoin 不到一秒钟就拿到了
    myCupOfTea
        61
    myCupOfTea  
       2021-06-02 10:15:28 +08:00
    @opengps 举个例子看看,有点没理解怎么转化的 QAQ
    zhozho
        62
    zhozho  
       2021-06-02 10:41:01 +08:00 via Android
    不是,我是这么理解的哈,前端页面上分两步,先客户表查员工 A 负责的客户列表,然后员工想看哪个客户的交易明细单独 where 查就是了。

    实际场景有一下看那么多客户交易的的嘛,想到一个导出,导出也不能全部导出吧。

    我觉得避免 in,就先避免查所有
    bubuXiaoqi
        63
    bubuXiaoqi  
    OP
       2021-06-02 17:48:55 +08:00
    @opengps 不用 join 第一是因为不在一个数据库实例 第二是因为 员工-标签-客户 已经是 4 5 个表的关联。这一块是可以缓存数据的。 全在数据库查的话 关联的就更多了 而且也看起来可读性很差。 实在要关联还得再单独维护一张员工->客户 IDS 的表。
    QiangZai
        64
    QiangZai  
       2021-06-11 16:17:31 +08:00
    不能利用 存储过程吗 ? 创建临时表 ?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1012 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 21:05 · PVG 05:05 · LAX 13:05 · JFK 16:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.