在做一件什么事情:
对新用户创建一个账号。如果用户账户已经存在,则对该账户的余额进行增减update操作。如果用户账户不存在,创建一个新的账户。并对用户账户明细表进行记录。
对要插入的数据和系统中已经存在的数据取交集,然后与要插入的数据取补给,所得数据就是要插入系统中的新的用户数据。
遇到了什么问题:
在本地模拟了上线操作脚本的场景,当时模拟的数据是30W+的用户数据,然后mysql实际配置是本地笔记本的配置信息,发现sql执行有点慢,担心上线时会对线上用户造成影响,所以本地排查,定位一下问题。
点击查看代码
insert into brand_value_record (
brand_record_id,
source_record_id,
brand_code,
code_id,
brand_code_value,
in_or_out,
user_id,
period_type,
period_start,
period_end,
operate_time,
operator_name,
source,
value_type,
remark,
is_delete)
SELECT
CONCAT(DATE_FORMAT(NOW(),"%Y%m%d%H%i%s"),CAST(round(rand() * 10000000) as CHAR)), # 记录流水
CONCAT(DATE_FORMAT(NOW(),"%Y%m%d%H%i%s"),CAST(round(rand() * 10000000) as CHAR)), # 源记录号
4, # 固定值 4 代表R
'Register_20210530', # 固定 code_id
1, # 增加B值 11
1, # 表示增加
m.user_id, # 对应账户
1, # 有效类型 方式
current_date, # 有效期起,
DATE_ADD(current_date, INTERVAL 1 YEAR), # 有效期终
current_timestamp, # 记录操作时间
'sys_0610', # 操作人标识
'sql_into', # 数据来源
'B', # 类型标识
'用户注册送1R值', # 备注信息
0 # 是否删除(0未删除)
from tmp_recommend_buy as m
where not EXISTS (select s.user_id from brand_value_total as s where m.user_id=s.user_id and brand_code=4) GROUP BY user_id;
问题分析:
select对象是一个子查询,子查询中包含not exists 语句,并且该语句需要对导入的数据进行分组,exists 关联条件中讲道理是应该用到了user_id索引的,但是通过explain发现该sql是通过走索引进行了全表扫描。索性,这里没有进行回表查询。但是整个语句很慢,实际上tmp_recommend_buy只有一千多条数据。
优化过程
- 查看系统中是否有线程执行语句存在锁表情况
show full processlist;
- 执行前面的慢查询语句
点击查看代码
SELECT
CONCAT(DATE_FORMAT(NOW(),"%Y%m%d%H%i%s"),CAST(round(rand() * 10000000) as CHAR)), # 记录流水
CONCAT(DATE_FORMAT(NOW(),"%Y%m%d%H%i%s"),CAST(round(rand() * 10000000) as CHAR)), # 源记录号
4, # 固定值 4 代表R
'Register_20210530', # 固定 code_id
1, # 增加B值 11
1, # 表示增加
m.user_id, # 对应账户
1, # 有效类型 方式
current_date, # 有效期起,
DATE_ADD(current_date, INTERVAL 1 YEAR), # 有效期终
current_timestamp, # 记录操作时间
'sys_0610', # 操作人标识
'sql_into', # 数据来源
'B', # 类型标识
'用户注册送1R值', # 备注信息
0 # 是否删除(0未删除)
from tmp_recommend_buy as m
where not EXISTS (select s.user_id from brand_value_total as s where m.user_id=s.user_id and brand_code=4) GROUP BY user_id;
Sending Data具体做什么
sending data 包含两个阶段。
1.收集数据阶段
mysql 使用索引检索到数据以后得到的都是主键ID,如果有的列不在索引中,还要对数据回表取数据
2.发送数据阶段
获取到数据后才是返回数据阶段
最后的结论
1.查询的过程中使用到了索引
2.整个sql的响应时间变慢的主要原因是回表查询了数据&&发送数据量大
优化措施
没有进行优化操作,原因如下:
1.因为查询已经走了索引,索引方面是没法再优化了
2.因为这个sql是在新的服务上线的时候,初始化的脚本,数据量大,执行过程长是必然现象
3.本地笔记本配置有限,实际线上mysql配置16C32G
4.服务发布时间点靠后
实际效果
实际线上执行时间20s左右,本地执行35s左右。