技术分享 | MySQL:count(*)、count(字段) 实现上区别( 二 )


bool Item_sum_count::add(){if (aggr->arg_is_null(false))return 0;count++;return 0;}END LOOP
最终我们只需要返回这个计数就可以了 。 下面是发送的数据 , 断点可以设置在 Query_result_send::send_data 中 。
$22 = Item::SUM_FUNC_ITEM(gdb) p ((Item*)(items)->first->info)->field_type()$23 = MYSQL_TYPE_LONGLONG(gdb) p ((Item*)(items)->first->info)->val_int()$24 = 3(gdb) p (items)->first->info$26 = (void *) 0x7ffe7c006580(gdb) p ((Item_sum_count*)$26)->count$28 = 3我们可以发送的数据实际就是这个计数器 , 最终值为 3 。
三、示例中 count(c) 获取数据流程的不同实际上整个流程基本一致 , 但是区别在于:

  • 构建的 read_set 不同 , 模板个数自然不同 , 因为需要 2 个字段 , 即 b、c 两个字段 , 其中 b 列用于 where 条件过滤 , 而 b 列用于统计是否有 NULL 值 , 因此模板数量为 2 , 如下:
(gdb) p prebuilt->n_template$29 = 2
  • 做 COUNT 计数器的时候会根据 c 列的 NULL 值做实际的过滤 , 操作只要是 NULL 则 count 计数不会增加 1 , 这个还是参考这段代码:
bool Item_sum_count::add(){if (aggr->arg_is_null(false)) //过滤NULL值return 0;count++;return 0;}最终会调入函数 Field::is_null 进行 NULL 值判断 , 断点可以设置在这里 。
四、不同点总结示例中的语句 count(c) 返回为 0 。 现在我们很清楚了 , 这些数据什么时候过滤掉的 , 总结如下:
  • Innodb 层返回了全部的行数据 。
  • MySQL 层通过 where 条件过滤 , 剩下了 b='g' 的行 。
  • MySQL 层通过 NULL 判断 , 将剩下的 count(c) 中为 NULL 的行也排除在计数之外 。
而 count(*) 则没有第 3 步 , 这是一个不同 。
然后的不同点就是在返回的字段上:
  • count(c) 很明显除了 where 条件以外 , 还需要返回 c 列给 MySQL 层
  • count(*) 则不需要返回额外的字段给 MySQL 层 , 只需要 MySQL 层过滤需要的b列即可 。
通过上面的分析 , 实际上效率没有太大的差别 , 我觉得同样执行计划 , 同样返回数据结果的前提下 , 可能 count(*) 的效率要略微高一点 。
五、备用栈帧(下图需点击放大查看)NULL 值计数过滤栈帧
技术分享 | MySQL:count(*)、count(字段) 实现上区别文章插图
最后推荐高鹏的专栏《深入理解 MySQL 主从原理 32 讲》 , 想要透彻了解学习 MySQL 主从原理的朋友不容错过 。