前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据库中千万数据常见问题以及如何解决(超详细,附导入数据教程)

数据库中千万数据常见问题以及如何解决(超详细,附导入数据教程)

原创
作者头像
天下之猴
发布2024-09-06 20:40:58
590
发布2024-09-06 20:40:58
举报

提前准备

本次我们采用从文件导入数据到数据库中的方式,LOAD DATA INFILE,我们先在IDEA中生成要导入到mysql中去的数据

代码语言:txt
复制
public static void main(String[] args) {
		//procedure_partition_test2_188||100188||test_模块||test_模块_方法||SEARCH||SUCCESS||2020-10-27 19:38:54||2020-10-27 19:38:54
		String sdf = "yyyy-MM-dd HH:mm:ss";
		Date date = new Date();
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);

		SimpleDateFormat dateFormat = new SimpleDateFormat(sdf);
		File file = new File("C:\\Users\\12192\\Desktop\\load_data.txt");
		String center = "||100188||test_模块||test_模块_方法||SEARCH||SUCCESS||";
		long start = System.currentTimeMillis();
		System.out.println("start:[" + start + "]");
		try {
			PrintWriter pfp = new PrintWriter(file, "UTF-8");
			for (int j = 0; j < 30; j++) {
				String id_prefix = "procedure_partition_test" + j + "_";
				calendar.add(Calendar.DAY_OF_MONTH, -1);
				Date time = calendar.getTime();
				String yesterday = dateFormat.format(time);
				String start_datetime = yesterday;
				String end_datetime = yesterday;
				for (int i = 0; i < 300000; i++) {
					StringBuffer sb = new StringBuffer();
					sb.append(id_prefix).append(i).append(center).append(start_datetime).append("||").append(end_datetime);
					pfp.print(sb.toString() + "\n");
				}
			}
			pfp.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		long end = System.currentTimeMillis();
		System.out.println("end:[" + end + "]");
		System.out.println("耗时:[" + (end - start) / 1000 + "s]");

	}

可以看到我们往文件写入900w数据只花了4s,接着将文件中的数据写入到mysql中去

接着数据库打开本地数据加载

代码语言:txt
复制
set global local_infile=1;

看看有没有打开成功

代码语言:txt
复制
show global variables like 'local_infile';

再将文件中的数据导入的数据库中

代码语言:txt
复制
load data local infile 'C:\\Users\\12192\\Desktop\\load_data.txt' into table igs_sm_interface_access_log
CHARACTER SET utf8 -- 可选,避免中文乱码问题
FIELDS TERMINATED BY '||' -- 字段分隔符,每个字段(列)以什么字符分隔,默认是 \t
    OPTIONALLY ENCLOSED BY '' -- 文本限定符,每个字段被什么字符包围,默认是空字符
    ESCAPED BY '\\' -- 转义符,默认是 \
LINES TERMINATED BY '\n' -- 记录分隔符,如字段本身也含\n,那么应先去除,否则load data 会误将其视作另一行记录进行导入
(
    interface_request_seq,
    user_id,
    interface_access_func_name_cn,
    interface_access_method_name_cn,
    interface_access_method_type_name_en,
    interface_response_status_cd,
    begin_datetime,
    end_datetime
) -- 每一行文本按顺序对应的表字段,建议不要省略

可以看到900w数据导入还是很快的,这种方式在导入大量数据时要比之前提到的分批导入还快,接下来我们来一起看看千万级数据查询所出现的问题,以及如何优化

近千万数据的导入花费时长
近千万数据的导入花费时长

深分页问题

话不多上,上图更直观,深分页问题故名思意,就是当数据多到一定程度时,常规的分页查询,越往后所需要的时间就越长

下面是数据量不同同,偏移量相同,以及数据量相同偏量不同的比较

数据量不同,偏移量相同
数据量不同,偏移量相同
数据量相同,偏移量不同
数据量相同,偏移量不同

可以知道数据量和偏移量都会影响到mysql查询的速度

使用具体字段减少回表操作

回表操作就是当你sql写*号时,mysql会去查该表的元数据,将所有字段添加进去,可以看到单线程时差距0.02秒,但是当并发量上去之后,该操作还是能节省不少时间的

回表与不回表的对比
回表与不回表的对比

添加索引,按需查找字段(效果明显)

索引字段与非索引字段查询对比
索引字段与非索引字段查询对比

接下来我们给user_id添加上索引试试,可以看到,添加上了索引之后查询时间有了很明显的优化

未添加索引
未添加索引
设置为索引之后
设置为索引之后

使用游标查询(效果明显)

游标查询即,如果有某个字段是递增的的话,我们只需要维护查询结果的最后一条数据的id是多少,那么我们下次直接将该ID作为下一次查询的条件即可

为什么通过where筛选之后,再进行limit速度会快这么多???

想弄清这个问题,我们需要知道where和limit在mysql中是如何执行的,想弄明白这个,我么就需要知道mysql的service层和存储引擎层,话不多说上实例,有下面查询语句,他在mysql中是如何执行的呢?

代码语言:txt
复制
select * from table where key > 1 and key < 100 and orther_key != 520;
//key是二级索引字段(非主键的索引),orther为非索引字段
  1. service层告诉存储引擎层:"菜贩子,你先找个鸡蛋我尝尝鲜(返回满足二级索引的第一条数据,非索引字段由service层判断),顺便给我讲讲这个鸡蛋的故事(进行回表操作,详细字段)"
  2. 存储引擎层找到了满足二级索引的鸡蛋后告诉service层:"客户你好,这是我们的尝鲜蛋(满足where中的二级索引),你先常常,这鸡蛋无菌,健康(告诉了service层记录中有哪些字段,也就是回表操作)"
  3. servece层:"你这鸡蛋不够圆啊(不满足我的非索引字段的条件,也就是orther != 520),这个我不要,你继续给我拿"
  4. 存储引擎层:"好好好,我再给你拿一个"
  5. 就这样,他们一来一回,终于service层买到了所有满足条件的鸡蛋

有人问,不是我寻思你这也没说limit啊,不急我们接着看,如果我的语句添加limit之后会是什么样子的呢?

代码语言:txt
复制
select * from table where key > 1 and key < 100 and orther_key != 520 limt 100000, 100;
//key是二级索引字段(非主键的索引),orther为非索引字段
  1. service层告诉存储引擎层:"菜贩子,你先找个鸡蛋我尝尝鲜(返回满足二级索引的第一条数据,非索引字段由service层判断),顺便给我讲讲这个鸡蛋的故事(进行回表操作,注意:回表操作只会在满足条件的字段上进行!!!!!!!!!)"
  2. 存储引擎层找到了满足二级索引的鸡蛋后告诉service层:"客户你好,我找了一个,满足您条件的鸡蛋,但是你先别急,我先给你存着,满100000个之后再把后面满意的100个鸡蛋一次给你
  3. 就这样,limit一直维护,进行了1000000次回表操作之后,终于,又找了100个鸡蛋,给了service层

可以看到为什么这两种差距很大了吧,limit是把满足条件的数据积攒起来,并且这些数据也还执行了回表(如果查询字段为非索引的话),而where只需要找到满足条件的,再对需要的数据条数进行回表也就是直接limit 100,

PS:小白,有错误请多多指正,立马了解改正!!!!持续更新中.............


参考连接

mysql导入千万级数据实操 - 习惯沉淀 - 博客园 (cnblogs.com)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 提前准备
  • 深分页问题
    • 使用具体字段减少回表操作
      • 添加索引,按需查找字段(效果明显)
        • 使用游标查询(效果明显)
        相关产品与服务
        云数据库 MySQL
        腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档