C++开发的Redis数据导入工具优化

背景

使用C++开发了一个Redis数据导入工具
从oracle中将所有表数据导入到redis中;
不是单纯的数据导入,每条oracle中的原有记录,需要经过业务逻辑处理,
并添加索引(redis集合);
工具完成后,性能是个瓶颈;

优化效果

使用了2个样本数据测试:
样本数据a表8763 条记录;
b表940279 条记录;

优化前,a表耗时11.417s;
优化后,a表耗时1.883s;

用到的工具

gprof, pstrace,time

使用time工具查看每次执行的耗时,分别包含用户时间和系统时间;
使用pstrace打印实时运行,查询进程主要的系统调用,发现耗时点;
使用gprof统计程序的耗时汇总,集中精力优化最耗时的地方;

使用简介:

1.对g++的所有编辑和连接选项都必须要加上-pg(第一天由于没有在连接处加上-pg选项,导致无法出统计报告);
2.执行完程序后,本目录会产生gmon.out文件;
3.gprof redistool gmou.out > report,生成可读文件report,打开report集中优化最耗时的函数;

优化过程

优化前11.417s:

代码如下:

time ./redistool im a a.csv
real    0m11.417s
user    0m6.035s
sys     0m4.782s (发现系统调用时间过长)

文件内存映射

系统调用时间过长,主要是文件读写,初步考虑是读取文件时,调用api次数过于频繁;
读取样本采用的是文件fgets一行行的读取,采用文件内存映射mmap后,可直接使用指针操作整个文件内存快;

日志开关提前

改进了文件读写后,发现优化效果比较有限(提高了2s左右);fgets是C的文件读取库函数,相比系统read(),是带了缓冲区了,应该不会太慢(网上有人测试,文件内存映射相比fgets()能快上一个数量级,感觉场景应该比较特殊);

之后通过pstrace工具发现log.dat打开次数过多;原来是调试日志的开关写到了后面,导致 调试日志都是会打开日志文件open("log.dat");
将日志开关提前;改进后,3.53s

代码如下:

time ./redistool im a a.csv
real    0m3.530s
user    0m2.890s
sys     0m0.212s

vector空间预先分配

后续通过gprof分析,某个函数的vector内存分配次数多,并有不少复制次数:
改进以下这行代码:

vector <string> vSegment;
使用静态vector变量,并预先分配内存:

代码如下:

static vector <string> vSegment;
vSegment.clear();
static int nCount = 0;
if( 0 == nCount)
{
    vSegment.reserve(64);
}
++nCount;

优化后,提升至2.286s

代码如下:

real    0m2.286s
user    0m1.601s
sys     0m0.222s

同样,另外一个类中的成员vector也使用预先分配空间(在构造函数中):

m_vtPipecmd.reserve(256);
优化后,提升至2.166s;

代码如下:

real    0m2.166s
user    0m1.396s
sys     0m0.204s

函数改写 && 内联

继续执行程序,发现SqToolStrSplitByCh()函数消耗过大,改写整个函数逻辑,并将改写后的函数内联:
优化后,提升至1.937s

代码如下:

real    0m1.937s
user    0m1.301s
sys     0m0.186s

去除调试符和优化监测符号

最后,去掉debug和pg调试符号后,最终效果为1.883s;

代码如下:

real    0m1.883s
user    0m1.239s
sys     0m0.191s

满足生产要求

以上最后几步看似毫秒级的提升,扩大到全表数据后,效果就很明显了;
优化后,生产上a表为152w,导入耗时大约326s(~6分钟);
b表数据420w,导入耗时大约1103s(~18分钟)

以上所述就是本文的全部内容了,希望大家能够喜欢。

(0)

相关推荐

  • C++使用redis的实例详解

    C++使用redis的实例详解 hiredis是redis数据库的C接口,目前只能在linux下使用,几个基本的函数就可以操作redis数据库了. 函数原型:redisContext *redisConnect(const char *ip, int port); 说明:该函数用来连接redis数据库,参数为数据库的ip地址和端口,一般redis数据库的端口为6379: 函数返回值:该函数返回一个结构体redisContext: 类似的提供了一个函数redisContext* redisConn

  • C++访问Redis的mset 二进制数据接口封装方案

    需求 C++中使用hiredis客户端接口访问redis: 需要使用mset一次设置多个二进制数据 以下给出三种封装实现方案: 简单拼接方案 在redis-cli中,mset的语法是这样的: 复制代码 代码如下: /opt/colin$./redis-cli mset a 11 b 22 c 333 OK 按照这样的语法拼接后,直接使用hiredis字符串接口redisCommand传递: void msetNotBinary(redisContext *c, const vector<stri

  • C++开发的Redis数据导入工具优化

    背景 使用C++开发了一个Redis数据导入工具 从oracle中将所有表数据导入到redis中: 不是单纯的数据导入,每条oracle中的原有记录,需要经过业务逻辑处理, 并添加索引(redis集合): 工具完成后,性能是个瓶颈: 优化效果 使用了2个样本数据测试: 样本数据a表8763 条记录: b表940279 条记录: 优化前,a表耗时11.417s: 优化后,a表耗时1.883s: 用到的工具 gprof, pstrace,time 使用time工具查看每次执行的耗时,分别包含用户时间

  • Redis数据导入导出以及数据迁移的4种方法详解

    1.aof 导入方式. 因为这种方式比较简单,所以我就先介绍它. 分两步来实现,第一步先让源 Redis 生成 AOF 数据文件. # 清空上文目标实例全部数据 redis-cli -h 目标RedisIP -a password flushall # 源实例开启 aof 功能,将在 dir 目录下生成 appendonly.aof 文件 redis-cli -h 源RedisIP -a password config set appendonly yes dir 目录,可以通过 config

  • Oracle和MySQL的数据导入为何差别这么大

    经常会有一些朋友咨询我一些数据库的问题,我注意到一个很有意思的现象,凡是数据导入的问题,基本上都是Oracle类的,MySQL类的问题脑子里想了下竟然一次都没有. 我禁不住开始思考这个未曾注意的问题: 为什么Oracle导入数据会碰到很多的问题? 我们来梳理一下这个问题,分别从导出导入的方式来聊聊. 首先Oracle导出的文件格式就没打算让你拿来即用,导出文件叫做dump,换句话说可以理解这是一个二进制文件.当然实际上这个文件还是有很多的方式去抓取一些关键的信息,比如dump头部的信息可以通过s

  • MySQL Shell import_table数据导入的实现

    目录 1. import_table介绍 2. Load Data 与 import table功能示例 2.1 用Load Data方式导入数据 2.2 用import_table方式导入数据 3. import_table特定功能 3.1 多文件导入(模糊匹配) 3.2 并发导入 3.3 导入速率控制 3.4 自定义chunk大小 4. Load Data vs import_table性能对比 5. 技术总结 1. import_table介绍 上期技术分享我们介绍了MySQL Load

  • MySQL数据库Shell import_table数据导入

    目录 MySQL Shell import_table数据导入 1. import_table介绍 2. Load Data 与 import table功能示例 2.1 用Load Data方式导入数据 2.2 用import_table方式导入数据 3. import_table特定功能 3.1 多文件导入(模糊匹配) 3.2 并发导入 3.3 导入速率控制 3.4 自定义chunk大小 4. Load Data vs import_table性能对比 MySQL Shell import_

  • 实现在线 + 离线模式进行迁移 Redis 数据实战指南

    目录 redis-full-check的使用背景 redis-full-check的基本介绍 编译源码 数据对比核心流程 参数信息介绍 对比结果差异分析不一致类型 key不一致 参考资料 redis-full-check的使用背景 在经历了之前的文章内容章节内容,已完成Redis迁移后,可能会存在以下问题需要进行数据迁移之后的对比.例如,如果Redis迁移的过程出现异常,源端与目的端Redis的数据将会不一致. 在Redis迁移完成后进行数据校验可以检查数据的一致性,该如何校验就是我们本文的内容

  • java实现excel导入数据的工具类

    导入Excel数据的工具类,调用也就几行代码,很简单的. 复制代码 代码如下: import jxl.Cell;import jxl.Sheet;import jxl.Workbook;import jxl.read.biff.BiffException;import org.apache.commons.beanutils.BeanUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory; import java.io.IOExc

  • sqlserver bcp(数据导入导出工具)一般用法与命令详解

    bcp是SQL Server中负责导入导出数据的一个命令行工具,它是基于DB-Library的,并且能以并行的方式高效地导入导出大批量的数据.bcp可以将数据库的表或视图直接导出,也能通过SELECT FROM语句对表或视图进行过滤后导出.在导入导出数据时,可以使用默认值或是使用一个格式文件将文件中的数据导入到数据库或将数据库中的数据导出到文件中.下面将详细讨论如何利用bcp导入导出数据. 1. bcp的主要参数介绍 bcp共有四个动作可以选择. (1) 导入. 这个动作使用in命令完成,后面跟

  • 基于C# 写一个 Redis 数据同步小工具

    概念 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set --有序集合)和hash(哈希类型).在此基础上,redis支持各种不同方式的排序.与memcached一样,为了保证效率,数据都是缓存在内存中.区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文

  • python导入导出redis数据的实现

    目录 一.导出redis某个库的数据 使用python向Redis批量导入数据 二.导入redis某个库的数据 注:以String类型为例 一.导出redis某个库的数据 import redis import json file_path = "why.json" redis_conn = redis.Redis(host="192.168.1.123", port=6387, password="123zxcv", db=2, decode_

随机推荐