Mysql join大表优化案例

一、准备知识(Mysql join原理及结论)

1、MySQL join分为
  • inner join
  • left (outer) join
  • right (outer) join
  • full join(mysql不支持full join,但是可以利用left join + union + right join实现full join)
  • cross join(笛卡尔积),实际上inner join不指定on即和cross join表现一样
  • straight_join(效果等同于inner join,只是固定了驱动表顺序)
2、驱动表与被驱动表
  • inner join:由执行器自行决定谁是驱动表,谁是被驱动表
  • left join:左表是驱动表,右表是被驱动表
  • right join:右表是驱动表,左表是被驱动表
  • straight_join:固定左边为驱动表,右边为被驱动表
3、join执行流程

每取驱动表一行数据,去和被驱动表匹配。可以理解为双层for循环
所以数据量的时候,循环次数变多,这也是join性能问题的根源。

4、join执行的实现原理
  • Nest Loop Join (NLJ),就是单纯双层循环
  • Block Nest Loop Join (BNLJ),在NLJ基础上,利用join_buffer,一次取出一批驱动表数据,可以减少循环匹配次数
  • Index Nest Loop Join (INLJ),在NLJ基础上,利用被驱动表连接字段的索引直接找到匹配数据,可以减少循环次数
5、join on
  • on后跟连接条件,一般必须指定,且只对被驱动表有效(即即使对驱动表加了过滤条件该条件也无效)
  • 由此可知join on之后,驱动表包含全部数据,被驱动表只包含on条件过滤后的数据
6、on和where
  • on在join时就会过滤数据,而where是join完成后再对数据进行过滤,所以on比where先作用
  • 所以理论上过滤条件放在on后,比放在where后性能好
  • 但是过滤条件放在on后和where后,结果可能不一样(原因就在于on后条件只对被驱动表有效),所以谨慎在on后加驱动表的过滤条件
  • 针对inner join,on和where无啥差别

根据前面的说明,以下属于结论性说明

7、加过滤条件要想清楚

是先对被驱动表进行过滤还是join完再对驱动表和被驱动表进行过滤

8、尽量小表驱动大表

这里针对的是left join和right join,因为inner join会自动选择

9、被驱动表连接字段要加索引

否则Mysql就会使用Block Nest Loop Join,影响性能

10、explain命令分析出的第一行的表即是驱动表
11、优化join的思路
  • 顺着减少join时驱动表匹配被驱动表时的循环查找次数进行思考(想一下哪些结论是针对这个思路的?)
  • 如果join后的数据量很大,且还要进行相关聚合操作,可以考虑先聚合出临时表,再join(本案例就是这个思路)

二、案例

1、原sql
EXPLAIN
select camf.asset_management_id
from crm_asset_management_friend camf  INNER JOIN crm_asset_management cam ON camf.asset_management_id=cam.id
where camf.`status`=1
	 and cam.wx_status != 0
group by camf.asset_management_id having count(camf.id) > 10;
  • 用时:1.5s
  • explain结果:在这里插入图片描述
2、分析
  • cam数据量4000多,且基本不会再大量增加;camf数据量接近400万,且持续稳定增加ing
  • camf在asset_management_id上有索引
  • join完之后有320万数据(表明每一条驱动表cam数据匹配被驱动表camf循环次数很多(1条匹配几千条)),如下sql,再进行groupby,性能可想而知
select camf.asset_management_id
from crm_asset_management_friend camf  INNER JOIN crm_asset_management cam ON camf.asset_management_id=cam.id
where camf.`status`=1
	 and cam.wx_status != 0
3、新sql
EXPLAIN			
select cam.id from (		
select camf.asset_management_id
    from 
    crm_asset_management_friend camf  
    where camf.`status`=1
    	group by camf.asset_management_id having count(camf.id) > 10
    ) tmp INNER JOIN crm_asset_management cam ON tmp.asset_management_id=cam.id
where  cam.wx_status!=0;
  • 用时:1.0s
  • explain结果:
    在这里插入图片描述
4、结语
  • 之所以还是在1s以上,是因为本身如下groupby就耗时0.9s多
select camf.asset_management_id
from 
crm_asset_management_friend camf  
where camf.`status`=1
group by camf.asset_management_id having count(camf.id) > 10
  • 限于业务,表结构,也只能优化到这儿了。
  • 要想彻底优化,是需要提取出每个asset_management的count(camf.id)作为一个新字段维护到cam表(维护比较麻烦,这也是没有维护的原因)。这样查询friend数大于10的资产就不需要绕这样一大圈了。sql就会变为类似:
select cam.id from crm_asset_management cam
where  cam.wx_status!=0 and friendCount>10

这速度就会杠杆的了!!!

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单链表是一种常见的数据结构,用于存储一系列的节点连接起来的数据。 在C语言中,我们可以使用结构体来定义一个节点,包含一个数据域和一个指向下一个节点的指针域。代码如下: ```c struct Node { int data; struct Node* next; }; ``` 然后,我们可以定义一个指向链表头部的指针,表示整个链表的起始位置。初始化链表时,这个指针可以指向NULL。 ```c struct Node* head = NULL; ``` 接下来,我们可以实现一些基本的操作,例如插入节点、删除节点、遍历链表等。 插入节点的过程包括创建一个新节点,并将其插入到链表中适当的位置。 ```c void insertNode(int value) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->data = value; newNode->next = NULL; if (head == NULL) { head = newNode; } else { struct Node* curr = head; while (curr->next != NULL) { curr = curr->next; } curr->next = newNode; } } ``` 删除节点的过程需要找到目标节点,并调整前后节点的指针。 ```c void deleteNode(int value) { if (head == NULL) { return; } if (head->data == value) { struct Node* temp = head; head = head->next; free(temp); return; } struct Node* prev = head; struct Node* curr = head->next; while (curr != NULL && curr->data != value) { prev = curr; curr = curr->next; } if (curr != NULL) { prev->next = curr->next; free(curr); } } ``` 遍历链表的过程是从头节点开始,依次打印每个节点的数据。 ```c void printList() { struct Node* curr = head; while (curr != NULL) { printf("%d ", curr->data); curr = curr->next; } printf("\n"); } ``` 这是一个简单的单链表的实现示例,你可以根据需要进一步扩展和优化

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值