V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dangyuluo
V2EX  ›  程序员

今天面试遇到的一个问题,关于 MySQL 相关效率的问题

  •  
  •   dangyuluo · 2015-07-05 20:44:26 +08:00 · 3238 次点击
    这是一个创建于 3431 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设有个表store内有字段producttexture, 内容分别类似12|15|18,与61|22|29,第一组数字代表这个商店内的所有产品id,第二组数字代表所有材质的id。
    产品和商圈数据库结构如下:(产品与材质都是store的属性,互不相关)

    product:

    id product
    1 手链
    2 戒指
    3 项链

    texture:

    id texture
    1 紫檀木
    2 花梨木

    以什么方式才能达到如下展示并且尽量高效呢?

    商家名称:某某某
    产品: 手链 戒指 项链
    材质: 紫檀木 花梨木

    我想了半天,给出了自认为效率比较低的办法:
    由于MySQL返回的数组索引是递增的,如

    [
    0=>[id=>1,product=>'手链'],
    1=>[id=>2,product=>'戒指'],
    2=>[id=>3,product=>'项链'],
    ]

    我对这个数组进行一个变换,将其变成以id为主键:

    $product=> [
    1=>'手链',
    2=>'戒指',
    3=>'项链'
    ]

    然后将store的属性12|15|18 explode开,将每个数字作为key值,显示$product[$key]

    但是总觉得这种效率好低,请问程序员朋友们有没有更好的办法?
    或者说,有没有什么办法,从MySQL中取数据的时候,可以指定某一个字段作为数据的索引?将其直接以我转换后的形式返回?

    17 条回复    2015-07-08 05:33:58 +08:00
    alphonsez
        1
    alphonsez  
       2015-07-05 21:40:31 +08:00
    这个效率不低啊。你query一次db比你iterate一次数组要慢好多。
    rqrq
        2
    rqrq  
       2015-07-05 22:08:25 +08:00
    题本身就有问题,要缓存也不应该是这种形式,字段直接存带 id 和名字的 serialize 或者 json_encode 之后的字符串不就行了,表都不用查。
    你可以再反问他,store 表数据量有多大?访问量有多大?几万数据小百万 pv 的话缓存都不需要做。

    再,你的数组问题:
    返回的 『0, 1, 2 数组』不是 mysql 直接生成的,而是通过 while 循环或者调用了 mysqli_fetch_all 组合而成的,你可以自己去控制,生成你想要的数组,减少一次循环,伪代码如下,可能缩进显示不出来,凑合看。

    生成『0, 1, 2 数组』
    $query = mysqli_query($this->link, $sql);
    if (function_exists('mysqli_fetch_all')) {
    return mysqli_fetch_all($query, MYSQLI_ASSOC);
    } else {
    $ret = $retlist = array();
    while ($ret = mysqli_fetch_array($query, MYSQLI_ASSOC)) {
    $retlist[] = $ret;
    }
    return $retlist;
    }


    生成你想要的『 1, 2, 3 数组』
    $query = mysqli_query($this->link, $sql);
    $ret = $retlist = array();
    while ($ret = mysqli_fetch_array($query, MYSQLI_ASSOC)) {
    $retlist[$ret['id']] = $ret['name'];
    }
    return $retlist;
    LaughingMeMe
        3
    LaughingMeMe  
       2015-07-05 22:10:48 +08:00
    @alphonsez not sure
    LaughingMeMe
        4
    LaughingMeMe  
       2015-07-05 22:12:06 +08:00
    @rqrq nice
    br00k
        5
    br00k  
       2015-07-05 22:15:58 +08:00
    丢给客户端处理。split()
    :)
    rming
        6
    rming  
       2015-07-05 22:18:26 +08:00
    怎么看起来像是前端的事,把 product,texture 和 两个table给前端,前端随便搞,爱咋显示咋显示,后端就是三个查询,都会走主键索引
    rqrq
        7
    rqrq  
       2015-07-05 22:20:51 +08:00   ❤️ 1
    另外,考虑到『墨菲定律』,如果 store 表要放缓存的内容,不管缓存的是什么样的形式,都应该有两个单独的表来存放 store 跟 product 以及 texture 的对应关系,当 product 或者 texture 中的 item 名字有变化或者被删除时,要更新对应的缓存数据。

    话说这种缓存真的很无聊,省几毫秒,反而带来很多麻烦,如果不是访问量特别大尽量少用。
    dangyuluo
        8
    dangyuluo  
    OP
       2015-07-05 22:46:17 +08:00
    @alphonsez 数据比较少的时候我觉得可以,但是如果product和texture有上千条,平白无故添加很多计算量。
    dangyuluo
        9
    dangyuluo  
    OP
       2015-07-05 22:48:13 +08:00
    @rqrq 谢谢!看来是我对MySQL理解不精!常用别人的库,以后自己也得好好读一下。
    Septembers
        10
    Septembers  
       2015-07-05 23:41:47 +08:00 via Android
    @dangyuluo 我觉得 单库单表规模没达到一亿行以上,还是不要讨论这个话题
    Septembers
        11
    Septembers  
       2015-07-05 23:44:42 +08:00 via Android
    @dangyuluo 现代Java模板引擎性能最好的能达到1.5万次每秒
    alphonsez
        12
    alphonsez  
       2015-07-06 00:33:45 +08:00
    @dangyuluo
    没多多少,从数据库度这些出来就是O(n)的,你只不过再for一遍而已。很快的。
    别的不说,你从数据库取出来要过一遍,你最后显示的时候总要过一遍,中间有啥过滤操作还是要过一遍。多过一遍而已。
    另外,product, texture真的会有数千条吗?如果有,你展示的时候怎么也给个分页吧不然谁看啊。
    mhycy
        13
    mhycy  
       2015-07-06 08:48:44 +08:00
    @alphonsez product, texture两个是属性,属性有上千个可能值是很正常的。
    说回来,这种建表模式总觉得该打。。。
    dangyuluo
        14
    dangyuluo  
    OP
       2015-07-06 11:28:36 +08:00
    @mhycy 我也觉得这么建表效率很低。哥们有什么建议么?估计这货以后我也得处理。
    mhycy
        15
    mhycy  
       2015-07-06 11:48:44 +08:00
    @dangyuluo
    我个人会把那两个用“|”分割的属性拆成两个表。
    输出的时候用join,但是有人说这样做性能不好。
    我并没有足够的数据量进行对比,所以这个不好说。

    现在这样也可以直接拆|然后用in的方式在数据库中查出属性。
    当然,如果一个页面有多个这样的属性还是用map性能好一些

    具体情况具体分析了。
    反正我是不会主动选择这样的建表方式的
    realpg
        16
    realpg  
       2015-07-06 13:05:37 +08:00   ❤️ 1
    @mhycy join的性能不好主要是说被join的(一般说来是被关联字典表或者字典表地位的表)的行数以及范围,如果被关联的这个表行数比较小,一般我都是像楼主这样处理一下把这个表做到数组里

    这种建表方式遇到过,主要是为了照顾其他模块的,然后设计时候2了没冗余一些相关列,楼主这个的需求是后提出的,就容易这样了

    楼主的解决方案应该是没其他给定条件下的比较高效率的了
    alphonsez
        17
    alphonsez  
       2015-07-08 05:33:58 +08:00
    @mhycy 如果一个商家下面有上千个product,并且不做paging,怎么展示呢?我做过的项目里最终从DB出来的数据集都不大,几百个最多了,这种情况下iterate一遍的开销可以忽略不计了。

    这个貌似可以常驻内存,定期刷新,刷的时候iterate一遍的开销相对于整个刷新而言,还是忽略不计。

    按照一楼的例子,可以想一下,如果赋值做两遍:
    while ($ret = mysqli_fetch_array($query, MYSQLI_ASSOC)) {
    $retlist[$ret['id']] = $ret['name'];
    $retlist[$ret['id']] = $ret['name'];
    }
    会慢很多吗?

    如果一个写一个循环:

    for (int i = 0; i < 100; ++i) { }

    会担心慢吗?如果都不会,那本来的写法其实就不会慢很多啦……

    关于这类内存操作的速度,如果真的不放心,可以实际测一下。记得和数据库操作的latency对比着看哦,不然没有意义。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3027 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 14:46 · PVG 22:46 · LAX 06:46 · JFK 09:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.