工作碰上的技术问题及处理经验

前言:工作中经常碰上各色各样的问题。有些问题处理过后就记在心中,下次碰上同样问题能很快解决。

但还是有不少问题,花了不少精力处理,但是印象不深刻,再次处理时又花了不少时间重新来过。

有鉴于此,我打算以后碰上问题处理完后都简单记一下,有空就翻翻,加深印象。

以下是目前整理出来在部分问题及处理经验,还有些问题没有整理好,我会持续完善。

 

20161021:
今天碰到一个bug,一个shell脚本里面尝试启动程序myProc处理一个日志文件时这样写:
v_logname="runlog"; myProc "$v_logname_20170101" # 日志文件格式是“runlog_yyyymmdd”
这脚本有个问题是"v_logname_20170101"被shell认为是一个变量名字,从而导致传给myProc的参数是空字符串。
正确写法应该是:myProc "$v_logname"_20170101; 或者 myProc "${v_logname}_20170101"

20161031:
今天项目除了一个事故:下游bi系统供应数据给我们,由于数据量太大(200G)导致影响了其它系统的交易。
因为其它系统跟我们共用一个nas(网络共享存储),我们这边持续写文件导致其他文件读写速度大幅降低。
以后碰上这种生产应用共用存储的情况需要多加小心。

20161101:
今天发现excel 2013和excel 2010保存xls(不是xlsx)文件格式有区别,具体区别还不清楚。
发现过程是这样的: 我们项目自己开发了一个excel导数工具,负责将txt文本的数据填到一些xls模板,方便用户浏览。
最开始我在自己电脑(2013)保存一份xls模板,用那个工具导入了一些数据进去,能顺利导入。但同样的模板到另一台装了2010的机器上,导入时就失败了。
通过日志定位,发现是CApplication::get_Workbooks()::open出错。然后我就在那台机看,用2010重新弄了一份xls模板(把原来模板的内容复制粘贴过去),
再用我们那个excel导数工具打开就没问题了。

20161102:
今天使用p1svc的加密函数SecAPI_NodeInit总是返回2043,咨询了一圈人,最后终于解决:调整环境变量就好。
LD_LIBRARY_PATH变量,需要把/home/ap/sa/lib64放在最前面。
不过奇怪的是这样改了后编译独立程序链接时又会报错(undefind referencet to DTCSP***)。原来未知。

20161107: 今天测试一下使用otl操作infomix插入10w条数据(sql: insert into hch_tmp values(:1, :2)),实验结果如下:
otl_stream缓冲区 使用绑定变量 耗时
1 是 1m28s
10 是 38s
100 是 32s
1000 是 31s
10000 是 31s
100000 是 34s
1 否 53s
10 否 58s
100 否 58s
1000 否 1m1s
10000 否 1m
100000 否 55s

重复了几次,数据波动不大。从实验得出结论,
1 使用绑定变量情况,otl_stream缓冲区大小设置为100~1000比较合适
2 不使用绑定变量情况,otl_stream缓冲区大小设置为1比较合适
3 缓冲区大小为1时,不使用绑定变量入库速度明显比使用绑定变量快很多
4 使用绑定变量并设置合适的缓冲区能明显提高入库效率

以上结论只适用于本机informix数据库,并且是insert语句比较简单的情况

2016110:
今天一个同事问了我一个strtok的问题:在while循环里面使用strtok拆分字符串没有问题,但是在循环里面加上输出日志语句,就会有异常。
一开始我猜测是因为多线程原因,像strtok跟printf这样的,内置静态变量的函数在多线程使用可能会有异常。但是他说没有用线程。
后来跟他一起分析代码,发现是由于他们项目的日志输出函数,里面也使用了strtok!写这个日志函数的人真是坑呀,应该用strtok_r的。

20161101:
今天发现使用vs2012编译的mfc的程序,在调用filedialog对话框后,可能会改变程序当前目录。但是同一个程序在win7系统上跑没有这个问题。

20161201:
今天分行客户报障:跨行转账时输入正确的联行号,系统仍然提示“输入的联行号不存在”。为了定位问题,我把日志开关打开,让分行重新操作。
几次测试后发现一个奇怪的现象:开日志时操作没有问题,一关日志同样操作就过不去。于是无法通过日志定位问题。
查代码也没发现有什么逻辑问题,不过发现很多个变量没有初始化。于是怀疑是由于这个影响的,试了一下初始化变量,果然可以了。
这个程序已经跑了好几年了,也就一个客户触发这个bug,其它客户转账一直好好的。变量没初始化这种不定时炸弹真可怕,幸好这次没啥大影响。

20161205: 今天写的一个脚本,在启动进程时报fork出错。原因是为了加快脚本处理速度,我在循环里面以后台命令方式启动处理程序,类似这样
i=0;
while [ $i -le 1000 ]
do
sleep 100 &
i=$(expr $i + 1)
done


由于系统限制一个用户只能起1000个进程,所以报错。解决方法是fork出10个进程后执行wait命令,等后台进程处理完再继续

i=0;
iProc=0;
while [ $i -le 1000 ]
do
sleep 100 &
i=$(expr $i + 1)
iProc=$(expr $iProc + 1)
if [ $iProc -eq 10 ]
then
wait;
iProc=0;
fi
done

20161109: 今天使用filecli传输文件时,发现如果文件字节数为0,程序会传输异常,认为传输失败。这是程序bug,有机会要改一下。

20170103:
由于项目需要,最近我写了一个在linux上将txt文件转换成excel文件的工具,用的是libxl类库。自己单元测试是跑起来没有问题,实际应用时发现一个坑。
当输出excel单元格数超过一定个数时,会随机输出“buy me”字样。略坑,幸亏还没上生产。
最后没办法,网上都说要花钱买正式版才行。只能找了一个java的开源类库,叫jxl,并且用java重写了工具。c++开源类库没java丰富。

20170103:
今天执行一个shell报错"/bin/sh不存在",cat文件发现第一行是 "#! /bin/sh",很标准的注释,不知道为啥报这个错。
后来把文件拿下来,放到ue打开,发现状态栏提示文件是“utf-8签名”,于是猜测是由于文件头存在不可见的字符(utf8文件签名)。
尝试一下把文件改成"utf-8无签名"格式,重新运行果然没报错了。

20170313:
今天发现在crontab命令行不能使用$(date +%Y%m%d)获取日期,因为'%'在crontab中是有特殊含义的字符,要写成这样_$(date +\%Y\%m\%d)

20170314:
今天不小心清空了crontab,幸亏有备份。原因是记错了显示crontab的命令参数,应该是"crontab -l",打成"crontab -r"。crontab这个命令真危险。

20170314:
今天碰上了一个二级const指针报错的问题,代码如下:
const char *strstrs(const char *line, const char * *key, int keyCnt);
char **keywordList = NULL;
strstrs(lineBuf, (keywordList), nKeywordCnt));

报错:
error C2664: “strstrs”: 不能将参数 2 从“char **”转换为“const char **”

正确写法:
const char *strstrs(const char *line, const char * const *key, int keyCnt);

20170320:
今天配置完odbc数据源后,用isql怎么也连接不上。
后来用strace isql hpe epos epos 查看系统调用(主要是open),发现访问的配置文件目录有问题
并没有访问到我配置在/etc目录下的配置文件,后来查资料发现有个环境变量ODBCINSTINI会影响isql读取配置文件行为
查了一下${HOME}/.bash_profile,果然发现有配置该变量,去掉即可。


20170324:
今天注意到awk使用空白字符作为分隔符与使用其它普通字符作为分隔符,$1定义不一样。
echo ' |a |b |c' | awk '{print "$1 = ["$1"]"}' # 输出“$1 = [|a]”
echo ' |a |b |c' | awk -F '|' '{print "$1 = ["$1"]"}' # 输出“$1 = [ ]”

20170327:
今天搭建一个新的服务器,发现不输入字符时SecureCrt容易自动断开,即使设置了定时发送空格字符也无效。
后来查了一下,是环境变量TMOUT造成的影响,把这个变量清除就行。 unset TMOUT

20170328:
今天使用informix的unload命令导出库表数据到文本文件,发现一些值得注意的细节:
1 char类型导出时,会自动去除尾部空格
2 如果varchar类型字段的值是null,则导出结果是空字符串,但如果值是'',导出结果是斜杠空格("\ ")
3 char类型字段如果值是'',导出结果是空格(" ")
4 unload导出时可以指定字段分隔符delimiter,如果字段值本身包含delimiter,则会导出成\delimiter
5 如果字段值含有'\',则会导出成双斜杠("\\")


20170329:

今天使用gdb调试程序,用到了一些不怎么常用的命令,百度了好一会。这里记录下来,方便下次查看:

s n c 分别是单步进入 单步跳过 执行到下一个断点
bt 查看函数堆栈
f n n是整数,结合bt命令输出,切换当前调试环境到指定函数
info locals/args 查看函数局部变量/参数
run 启动进程
b n 添加断点
d n 删除断点
dump 打印进程内存数据
save source 保存调试信息
p *array@len 打印数组元素值
set args 设置程序启动参数
list 打印源码
otl get_rpc
b 334 if a == 1 条件断点


20170331:
今天犯了一个低端的错误
cat << EOF
str
EOF <- 多了个空格


20170401:
informix对频繁变更的表进行select/update操作是有时会报错“Could not position within a table ”
此时可以使用set lock mode to wait n; (n是正整数,代表秒数)减少出错概率
oracle里面很少出现这种情况,只有ddl被select或者update语句阻塞,修改ddl_lock_timeout参数可以减少出错

 

20170405:

各种常见的工具忽略大小写:
vi :set ignorecase
awk awk -v IGNORECASE=1
sed sed 's/keyword/newstr/i'
grep grep -i

需要特别注意的是sed也有-i选项,但是这是直接在文件中修改的意思,不是忽略大小写。
很多用习惯grep的人会想当然以为都是用-i命令忽略大小写,结果容易出现误操作。

 

今天用到了ssh进行端口转发,记录一下用法:
ssh -g -L 6666:128.128.96.10:6666 epos@128.128.96.84 -N

20170412:
今天碰上isql访问数据库中文显示乱码问题,后来发现数据库用的字符集是UTF8,需要修改odbc.ini,增加
CHARSET = UTF8


查看正在运行中的进程环境变量:
cat /proc/${PID}/environ| tr -s '\0' '\n'
查看正在运行进程的当前目录
ls -lrt /proc/${PWD}/cwd
查看系统当前代码页
linux - locale win - chcp


20170417:
今天登陆一个安装了hp-ux系统的服务器,发现在上面操作有点怪异,按ctl+c不退出程序,按退格键不删除字符
后来百度了一下找到了解决方法,记录下来:
1 stty echoe # 按退格键不删除字符
2 ctl+c不退出程序可以使用stty -a|grep intr,看看intr设置成什么按键。(一般是^C 我这台HP-UX是设置成DEL)
stty命令还能设置屏幕行列数

vim修改行末尾回车换行符方法:
set fileformat=unix #vi替换回车

 

20170418:
sed匹配整个单词用法
sed 's/\<mode\|level\>/&_o/g'
#ksh -o vi

find / -mtime -5 2>/dev/null | xargs ls -lrtd
find / -mtime -5 >tmp.log 2>/dev/null; ls -lrtd $(cat tmp.log) 不一致

20170420:
iconv命令在转换文件时,碰上异常字符会报错退出。可以使用-c命令忽略这些异常字符。

iconv_open(to_charset, from_charset);
getline
sed '\<\>' 对于 xxx_oxx 不支持


20170420:
今天使用plsql developer在一台新的数据库服务器运行语句:“select * from tran order by 1 desc”
发现比原来服务器慢很多,tran表是我们系统的流水记录表,第一个字段是有索引的,理论上应该很快。
后来查询了一下,发现是由于optimizer_mode设置问题导致的:跑的正常的服务器设置成oltp模式,新装的机器是olap模式
导致查询计划不一样
show parameter optimizer_mode;


今天在新的服务器上使用odbc操作oracle,发现中文会出现乱码。查了一下是因为编译odbc类库时漏了两个参数导致的:“--enable-iconv=yes --with-iconv-char-enc=GB18030”
正确示例:
./configure --prefix=${HOME}/usr/local/unixODBC-S --includedir=${HOME}/usr/local/include --libdir=${HOME}/usr/local/lib -bindir=${HOME}/usr/local/bin --sysconfdir=${HOME}/etc --enable-gui=no --enable-drivers=no --with-libiconv-prefix=${HOME}/usr/local/ --enable-iconv=yes --with-iconv-char-enc=GB18030


20170424:
今天发现服务器上sqlplus出错时提示是中文的,不方便上百度搜索错误信息,找了一下,原来是环境变量配置问题。
export NLS_LANG='AMERICAN_AMERICA.ZHS16GBK' # 错误信息是英文
export NLS_LANG='SIMPLIFIED CHINESE_CHINA.ZHS16GBK' # 错误信息是中文

20170425:
今天出现一个情况:我们的程序一连接数据库服务器就挂了,而isql连接没有问题。对比isql的代码和我们的底层代码发现
区别在于我们的程序在SQLConnect之前有一个操作:SQLSetConnectAttr( _hdbc, SQL_LOGIN_TIMEOUT,( void* )7, 0 ); //设置超时时间
去掉就能正常运行。
代码一直在用,之前一直没这个错误。但是最近数据库服务器从informix换成oracle,猜测是以前infoxmix没有超时登录的功能。

20170427:
vs ctrl+alt+e 异常 断点

GROUP BY rollup(b.bran1, b.bran2, b.bran3, b.bran4)

-fPIC

20170503:
今天发现一直在跑的备份语句有bug:
find . -type f|xargs file |grep "text"|awk -F ':' '{print $1}'|xargs tar -czf src_$(date +%Y%m%d).tgz
如果文件太多,会备份漏了


20170505:
windows上char是无符号的,以下代码可能会返回非预期结果。char c = -1; char a = 'a'; if (a < c) {}


20170506:
if (getHostIpList( hostname, ipList, sizeof(ipList) / sizeof(ipList[0]) ) < 1);
{
DebugLog(__FILE_UNCONST__, __LINE__, "getHostIpList err!");
return -1;
}


20170516:
今天在windows上设置了NLS_DATE_FORMAT修改oracle日期显示格式,发现不生效,后来增加设置了NLS_LANG才有效。linux上只需要设置NLS_DATE_FORMAT即可。


20170524:
strncpy(src, dest, n),不保证\0结尾,如果n > strlen(src),会往dest填充\0直至n
如果n远大于strlen(src)会造成性能浪费,如果不想填充多余的\0可以用strlcpy

20170525:
新装的oracle服务器,偶尔发生登录失败的问题。后来找dba查原因,回复说是服务器起了多个监听服务,改了就好。

20170526:
今天碰上一个多进程同步bug,示例代码如下:
while (1) {
//循环等待,直到lock_table表sn=1的记录没有被锁
//将该记录锁上,退出循环
if ((select count(*) from lock_table where sn = 1 and lock_state = 1) == 0) {
update lock_table set lock_state = 1;
break;
} else {
sleep 60;
}
}
//开始处理任务
doSomething();
//解锁
update lock_table set lock_state = 0;

这个同步逻辑存在缺陷,当进程1启动时对记录加锁,接着进程2和进程3同时启动,发现记录被锁
则进入休眠状态,此时进程1做完任务解开记录说,进程2和3同时从休眠状态恢复,此时都会发现记录没有被锁,继续往下走。
导致两进程同时进入临界区。


20170624:
今天程序上线后行为异常,怀疑是打包了旧版本导致。用以下命令取回程序信息,分析结果确实如此。
# objdump -tT /home/epos/bak/CScheduler.bak20170528 > CScheduler_bak.dump.txt 2>&1
# strings /home/epos/bak/CScheduler.bak20170528 >> CScheduler_bak.dump.txt 2>&1
# sum /home/epos/bak/CScheduler.bak20170528 >> CScheduler_bak.dump.txt 2>&1

 

20170626:
今天突然接到监控告警,说服务器cpu高。登上系统用ps -ef 查看进程
发现还真有几个进程占用cpu特别高(第四列)
为分析进程异常原因,我把进程内存dump下来(gcore -o core文件名称 进程号)
再搞到开发机器上,使用gdb像处理core文件一样处理dump文件,发现是一个循环条件有bug导致进程死循环。修复了就好。


20170703:

记录一下今天用到的sqlplus操作技巧:
whenever sqlerror exit sql.sqlcode; -- sql运行异常时退出,不执行后面的语句

show errors -- 显示存储过程编译错误

 

20170712:
shell命令 return用于退出函数 exit用于退出脚本

20170713

cap
c 一致性,所有数据变动都是同步的
a 可用性,响应时间迅速
p 可靠性,网络中断导致分区可用

acid
a 原子性 所有操作都必须全部完成或全部不完成
c 一致性 在事务开始或结束是,数据应该在一致状态
i 隔离层 事务假定只有自己在操作数据库
d 事务完成不可返回

20170719
awkprint函数无参数情况下默认输出$0

BEGIN{
FS = ";\t*";
}
{
if (NR % 600 == 1)
{
v_sn++;
v_file_name = "update_" v_sn ".sql"
print "" > v_file_name
}

print $1 ";1" >> v_file_name;
print $2 ";2" >> v_file_name;
print $3 ";3" >> v_file_name;
print $4 ";4" >> v_file_name;
}
END{
print "total "v_sn" files"
}


20170720:

今天实践了dbaccess非交互式数据库操作,记录操作命令

# 需要人工输入密码
dbaccess - - << EOF
connect to "hpe@onlinetcp" user 'epos';
select * from dual;
EOF

# 自动输入密码
echo connect to "hpe@onlinetcp" user 'epos' using 'epos' > demo.sql;
dbaccess - demo.sql

# 无需密码
dbaccesss -e hpe << EOF
select * from dual;
EOF

这样操作前提是要配置db信任ap1(主机名)的epos用户

可以在db的epos用户下:
echo "AP主机名 epos" >>.rhosts
.rhosts的权限是644

同时,需要确保db主机的/etc/hosts文件里面有AP主机的解析

 

20170801:
查找infomix表名
select * from sysmaster.systables where tabname like '%check'

20170802:
今天碰上一个比较少注意的语法细节:sizeof("123456") == 7
之前一直以为sizeof("123456")的值是4,以为字符串其实就是指针,其实"123456"本质是一个字符数组

 


20170803:
snprintf linux 返回待拷贝字符串长度,不包括\0 不管什么情况,肯定有\0 所以如果返回值大于等于缓冲区长度,说明数据被截断了
_snprintf windows 返回待拷贝字符串长度len,不包括\0 如果len大于等于缓冲区长度,则末尾不包含\0 否则末尾会以\0填充

char buf[8];
size_t nWriten = snprintf(buf, sizeof(buf), "hello world");
if (nWriten >= sizeof(buf))
{
buf[sizeof(buf) - 1] = '\0';
printf("truncate!");
}


awk 强制输出日志 close

yes | awk -v v_log_file="awk.log" '{
print $0
if ((NR % 1000) == 0)
{
print "run "++i, NR > v_log_file;
}
}' > output.log

echo | awk -v v_log_file="awk.log" '{
for (i = 0; i < 100; i++)
{
system("sleep 10");
print "abc" > v_log_file;
}
}


20170807:
今天发现一个比较少注意的c++语法问题:
char szField59Flag[8] = {"Y"};
char szField59Flag[8] = {'Y'};
上面两行初始化字符数组的代码,效果果是一样的。编译器会自动给数组中未初始化的字符元素置零。
不过奇怪的是,int szField59Flag[8] = {1};就没有这个待遇

 

20170814:
查看程序内存异常操作
valgrind --leak-check=full --track-fds=yes --tool=memcheck

 

20170816
indent命令格式化源代码时会直接修改文件,需要用以下参数才正确:
indent input.txt -o output.txt


20170919:

今天维护突然说生产tuxedo开启了tmtrace,需要关闭。
查了一下,确实每天都有ulog生成。网上找了关闭方法
export TMTRACE=off;tmshutdown -y;tmboot -y
试运行了一下,发现没关成功,ulog日志还在输出。

折腾很久,才发现是应用调用了userlog输出导致的,跟tmtrace无关
设置TMTRACE=on会发现日志多很多
export TMTRACE=on;tmshutdown -y;tmboot -y


20170926:
inotify linux文件监控


20171010:
oracle to_date 缺省月份是当前月

20171017:
程序core了没有corefile可以使用ulimit -c看是否禁止生成

ulimit -c unlimited


20171018:

true > abc.txt 清空文件


20171102:
ctrl + c信号会传递给所有子进程

短时间对同一个端口重复bind会出现异常(即使已经关闭监听),可以设置socket选项避免
setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));


20171121:
使用http协议作为通讯协议时,可能有些特殊字符会导致对方服务器接收异常。(如+ /)
此时应该将特殊字符用“百分号+十六进制asscii码代替”如用"%2F"代替/ %3F代替?

20171122:
清理系统内存cache
echo 3 > /proc/sys/vm/drop_caches


20171125:
如果一个tcp连接建立后不发送数据,默认是不会断开的。(断网) 可以设置keeyalive,两个小时没发数据会自动断开


20171129:

在vs混合编译c和c++文件时,如果使用了同一个预编译头文件,会报C1835错。需要为c文件建立并使用独立的预编译头。

在vs里面编译c文件时,如果有不在代码块开头声明的变量,会报C2275错,但在gcc不会

在vs编译cpp文件时,如果函数没有声明,会报错,但是编译c文件时只是警告。如果使用gcc,则默认是不警告,需要加上-Wall才会有提示

 

20171205:
结构体对齐原则:
1 未指定#pragma pack时,32位系统按4字节对齐,64位系统按8字节对齐
2 成员变量相对偏移地址应是其自身长度与#pragma pack指定的模数两者中较小的一个的整数倍
3 结构体本身大小是其最大成员长度与#pragma pack指定的模数两者中较小的一个的整数倍
4 结构体包含结构体时,先把所有内部结构体展开再进行对齐

20171213:
size_t变量在gcc的printf可以使用%uz输出

20171219:
vs修改了stdafx.h 需要编译 stdafx.c才生效

20171220:
linux自定义信号:
SIGUSR1 SIGUSR2 __SIGRTMIN~__SIGRTMAX (一共32个信号,有的文章建议前三个不要用)
发送自定义信号语句:
kill -s SIGUSR1 PID

 

20171221:
pid默认最大32767 如有需要可以修改参数/proc/sys/kernel/pid_max

20171222:
非阻塞socket编程,最好不要把监听socket设成非阻塞,这样会导致accept经常失败

20171225:
如果在send数据时,对方关闭socket会导致程序接受到sigpipe信号
该信号默认响应行为是退出程序,需要警惕。如果不需要特别处理,可以屏蔽该信号,signal(SIGPIPE, SIG_IGN) 或者加上MSG_NOSIGNAL
这样send会返回-1
如果在recv时对方关闭socket,会返回-1,虽然此时用select函数仍然能正常返回socket可读。
如果在send/recv/select时,自己程序其它线程关闭socket,行为未定义,无法预测

服务器还没写完数据,对方关闭socket,114错误 RST
time_wait 2msl最好由服务器主动关闭socket
close_wait 客户端已关闭,服务器没关闭
setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));

20171226:
socket被子进程close父进程还能用,但是被shutdown父进程则无法用
shutdown后还需要close close 计数为0时会自动shutdown

 

posted @ 2018-02-25 12:01  皇家救星  阅读(1488)  评论(0编辑  收藏  举报