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

2015-07-05 20:44:26 +08:00
 dangyuluo

假设有个表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中取数据的时候,可以指定某一个字段作为数据的索引?将其直接以我转换后的形式返回?

3231 次点击
所在节点    程序员
17 条回复
alphonsez
2015-07-05 21:40:31 +08:00
这个效率不低啊。你query一次db比你iterate一次数组要慢好多。
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
2015-07-05 22:10:48 +08:00
@alphonsez not sure
LaughingMeMe
2015-07-05 22:12:06 +08:00
@rqrq nice
br00k
2015-07-05 22:15:58 +08:00
丢给客户端处理。split()
:)
rming
2015-07-05 22:18:26 +08:00
怎么看起来像是前端的事,把 product,texture 和 两个table给前端,前端随便搞,爱咋显示咋显示,后端就是三个查询,都会走主键索引
rqrq
2015-07-05 22:20:51 +08:00
另外,考虑到『墨菲定律』,如果 store 表要放缓存的内容,不管缓存的是什么样的形式,都应该有两个单独的表来存放 store 跟 product 以及 texture 的对应关系,当 product 或者 texture 中的 item 名字有变化或者被删除时,要更新对应的缓存数据。

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

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

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

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

楼主的解决方案应该是没其他给定条件下的比较高效率的了
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对比着看哦,不然没有意义。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/203515

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX