續上一篇隨筆:https://www.cnblogs.com/kingstarer/p/12249028.html《作業碰上的技術問題及處理經驗》(四)
我這人記憶力比較差,經常出現有些知識學了不久后就忘了,或者有些問題花了很多時間百度解決后,再過一段時間碰上時只有模糊印象,卻忘了具體解決方法,
最近幾年作業時我開始有意識地把登記每天作業碰上的技術問題做個簡單筆記,
一般上班時間比較忙,只能草草記了一兩句話,等過一段時間,我會把這個筆記整理一下,把問題和處理經驗整理通順,以加深自己的印象,
20190402: 今天碰上一個腳本檔案編碼與系統編碼不符,導致sed運行報錯的問題,我腳本里面寫了一個命令sed -e 's/月份/月份 ${V_MONTH}/g' 執行時報錯"sed: -e expression #1, char 21: unterminated `s' command" 我在執行sed之前先執行set -x,打開腳本除錯模式,看到輸出是sed -e "s/\324\302\267\335/\324\302\267\335 201904/g", 這個輸出說明編碼是GBK,因為一個中文占了兩個位元組,但我系統的LANG設定為en_US.UTF-8,不相符,所以需要改一下, 20190407: 今天發現xargs命令做字串處理還是挺方便的,以前知道用它來跟管道組合處理引數太多超過命令列限制的情況,這里摘錄幾個用法: 替換字串:echo "nameXnameXnameXname" | xargs -dX 多行輸入單行輸出:(echo a; echo b; echo c) | xargs 組裝shell命令:cat arg.txt | xargs -I {} ./sk.sh -p {} 20190408: 今天發現sourceinsight雙擊選中文字后會自動被修改,變成一個奇怪的問號圖示,開始以為是我鍵盤有鍵被卡住了,挨個鍵檢查了一下,沒發現問題, 后來偶然發現原來我打開了金山詞霸,想起以前也看過網友吐槽,詞霸里面的取詞和劃譯功能實作方法很粗暴,會不停往系統發快捷鍵訊息, 于是嘗試把金山詞霸關了,果然好了, 20190409: 今天發現oracle的systimestamp型別是帶時區的timestamp,需要用cast(systimestamp as timestamp)轉換一下才變成不帶時區的, 今天用proc*c寫的程式匯出資料庫表資料,發現有一些欄位值是NULL,但我程式沒報錯,理論上proc如果不用指示變數,取出空的資料時會報錯的, 后來查查,發現原來我這個程式用proc*c預編譯時設定了UNSAFE_NULL=yes,所以沒報錯, 20190410: 今天發現oracle有一個語法insert all,可以指定一條記錄同時插到兩個庫表: insert all into t1(c1) int t2(c2) select * from t; proc*c執行dml陳述句時支持預編譯一次,執行sql多次,不過前提是要設定選項release_cursor=no(默認是no) 今天發現proc有一個引數:prefetch,指定sql陳述句句柄打開后預先從資料庫取出多少條記錄,這個引數默認是1,批量任務可以考慮把它改大,有助于提高性能, 今天想用gcc的-Og選項編譯程式,發現居然不支持,估計是我用的版本太低了,網上說-Og選項只會打開與-g不沖突的優化選項, 20190415: 今天發現cp有一個引數-p,復制時保留原檔案的修改時間:cp -p old new shell清空檔案的技巧 true命令: true > emptyfile 20190416: 用awk寫了一個輔助宏代碼對齊的腳本 # 輸入宏代碼,腳本會在每行后面補齊空格和\ awk -v v_tab_lenth=8 '{ # 首先讀取所有輸入行 計算所有輸入文本單行最大長度 v_arr_input[NR] = $0; v_str_len = length($0); v_arr_input_len[NR] = v_str_len; if (v_str_len > v_max_len) v_max_len = v_str_len; } END{ v_max_tab_cnt = int(v_max_len / v_tab_lenth) + 2; print "v_max_tab_cnt = "v_max_tab_cnt for (i = 1; i <= NR; ++i) { v_str_tab_cnt = int(v_arr_input_len[i] / v_tab_lenth); v_tab_cnt = v_max_tab_cnt - v_str_tab_cnt - 1; #if (v_str_tab_cnt * v_tab_lenth < v_arr_input_len[i]) v_tab_cnt = v_max_tab_cnt - v_str_tab_cnt - 1; #else v_tab_cnt = v_max_tab_cnt - v_str_tab_cnt; # print "v_tab_cnt = "v_tab_cnt, "v_str_tab_cnt = "v_str_tab_cnt, "v_arr_input_len[i] = " v_arr_input_len[i] printf("%s", v_arr_input[i]); for (j = 0; j < v_tab_cnt; ++j) printf("\t"); print("\\"); } }' tmp.txt 20190417: 今天發現一個函式fcloseall(_fcloseall),作用是把fopen打開的檔案句柄全關了, 但我實際測驗,發現fopen打開一個句柄fp,然后呼叫fcloseall,但這個fp句柄還可以正常讀寫和關閉, 不過確實證明它有fflush的作用, 20190420: 今天發現proc查詢資料到char *型別縮主變數,居然是通過strlen計算其空間大小, 這個設定好奇怪,在官方檔案里面也提醒了呼叫者,需要用非'\0'字符填充空間, proc執行rollback/commit會影響sqlca.sqlcode,所以如果需要這個值要先備份起來, 20190423: 今天創建一個磁區表時發現oracle在創建表的速度比刪表的速度快很多, 創建陳述句雖然很長,但oracle一般在十幾毫秒內能完成,刪表陳述句很短,但執行要數秒鐘, 暫時不知道是什么原因導致的, 20190424: 今天復習了oracle的savepoint語法: savepoint a; rollback to a; 今天我寫了一個函式,使用proc*c的陣列批量操作技巧,一次插入N條記錄, 但是有一個問題沒解決,如果在插入程序中陣列里面某些元素由于資料例外插入失敗怎么處理? 我暫時的解決方法是:插入前先使用savepoint建立一個保存點,如果插入失敗則回滾到該保存點, 不知道plsql里面的forall命令是怎么實作的使用forall批量插入資料時,如果某個資料插入出錯會忽略,繼續執行, 所有出錯記錄會存在sql%bulk_exceptions陣列的, 20190428: 今天同事分享的一個問題:進行非功能測驗,隨著時間增長,交易速度有明顯下降, 后來查了一下日志,發現有一些sql執行耗時例外,通過分析sql執行計劃,發現sql沒走索引, 再進一步分析原因,是因為庫表沒有及時做統計收集,oracle以為是空表,本來應該走索引的,給走了全表掃描, 一般認為,庫表記錄較小(小于100)或者查詢的資料量超過總資料的10%走全表掃描比走索引掃描快,(一次順序IO比多次隨機IO快) 2190506: 今天花了點時間查了一個proc的問題: 執行update陳述句時沒找到記錄,sqlca.sqlcode回傳1403,但是前面我用EXEC SQL WHENEVER SQLERROR DO指定的proc錯誤處理函式沒有被呼叫, 后來把.pc檔案翻譯成.c檔案看,才知道EXEC SQL WHENEVER SQLERROR DO指定的函式只有在sqlca.sqlcode < 0時才會被呼叫, 今天還注意到一個oracle語法細節:select for update后面還可以跟上of 欄位,指定只鎖定記錄哪些欄位, select from hch for update of c1; 20190507: 查看正在運行程式環境變數的方法: strings /proc/${PID}/environ 20190508: 今天使用valgrind檢查程式記憶體使用情況,發現有幾個出錯位置函式堆疊里面沒有main,而且里面都是一些oracle底層的函式,后來發現原因是出錯位置函式呼叫層次太深,超過了默認值12, 我重新啟動valgrind,加上引數--num-callers=100,這樣就能顯示從main到出錯位置的所有函式, 20190509: 今天看到了這樣的代碼: if (bFlag) { EXEC SQL CONTEXT USE :ctx; } else { EXEC SQL CONTEXT USE DEFAULT; } EXEC SQL ...; 寫代碼人的本意根據bFlag的值決定使用不同資料庫連接執行sql,但這樣寫是有問題的,因為EXEC SQL CONTEXT并不是一個陳述句, 它有點類似C語言的宏定義,在預編譯后根本不存在,(把.pc檔案預編譯成.c后"EXEC SQL CONTEXT USE"這段代碼就是一段注釋) 他這樣寫代碼問題就跟用C語言這樣寫代碼一樣: #define SQL_CONTEXT_USE ctxDeault if (bFlag) { #undef SQL_CONTEXT_USE #define SQL_CONTEXT_USE ctx } else { #undef SQL_CONTEXT_USE #define SQL_CONTEXT_USE DEFAULT } EXEC_SQL(SQL_CONTEXT_USE); //無論bFlag的值是什么,這里都是用EXEC_SQL傳的值都是ctxDeault 正確寫法應該是這樣: if (bFlag) { EXEC SQL CONTEXT USE :ctx; EXEC SQL ...; } else { EXEC SQL CONTEXT USE DEFAULT; EXEC SQL ...; } 201900512: 今天一下網友分享一個案例:他用瀏覽器訪問某個網站時能正常獲取頁面資訊,但用爬蟲訪問時卻獲取不到, 他已經修改了http請求頭,把agent改成跟瀏覽器的一樣也不行, 后來他發現原來是訪問網站時服務器會回傳一段js,瀏覽器執行那段js后才能正常生成頁面資訊,他的爬蟲不支持js決議,所以出了錯, 201900513: oracle應用程式,有時會需要使用sysdate(或者序列號)修改庫表欄位,但又想知道修改后的值是什么,這時可以使用returning into 子句,示例代碼如下: set lines 1000 pages 1000 set serveroutput on alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'; alter session set nls_timestamp_format='yyyy-mm-dd hh24:mi:ss.FF6'; alter session set nls_timestamp_tz_format='yyyy-mm-dd hh24:mi:ss.FF6 TZR'; drop table tab_hch; create table tab_hch(c1 date, c2 timestamp); declare v_dt date; v_dtmicro timestamp; BEGIN insert into tab_hch(c1, c2) values(sysdate, systimestamp) returning c1, c2 into v_dt, v_dtmicro; dbms_output.put_line('c1 = ' || v_dt); dbms_output.put_line('c2 = ' || v_dtmicro); -- 這里輸出的時間就是新插入的記錄c2的值 END; / col c1 for a20 col c2 for a32 col c3 for a20 col c4 for a34 -- 比較c2與dbms_output.put_line的值,看是否一致 select c1, c2, sysdate c3, systimestamp c4 from tab_hch; 20190515: oracle 交換磁區如果存在全域索引,會導致全域索引失效,可以在交換磁區時加上with update global index 20190529: sqlplus系結變數用法: var v_hch number; exec :v_hch := 1; print :v_hch; select :v_hch from dual; 20190530: 今天碰上makefile里面執行cd失效導致遞回編譯的問題,事情是這樣的:有一個程式編譯時要求先到子目錄下面執行makefile編譯后,再回到當前目錄編譯,于是我寫了下面這個makefile, prog: cd common make $@ cd .. make $@ 結果編譯時發現make被無限回圈呼叫了,網上查了一下,要這樣寫cd common && make $@,原來每次執行一行命令后,makefile會重新回到當前作業目錄,所以我原來的makefile等于無限回圈呼叫make編譯, 今天有網友問我他們系統有一個一個行程卡住,不知道是死回圈了還是在等待什么條件,要怎么辦,我建議他用pstack看一下堆疊,結合代碼分析程式的狀態, 20190531: 寫了一個awk腳本,用于把長行拆成多個短行,里面考慮到了行尾單詞連續性問題: awk -v v_line_len=70 '{ v_str = $0; v_TotalLen = length(v_str); while(1) { v_str_sub = substr(v_str, 1, v_line_len); nLen = length(v_str_sub); if (nLen < v_line_len) { print v_str_sub; break; } nLen = v_line_len; v_ch = substr(v_str_sub, nLen, 1); # 如果結尾不是非字母或者數字,則一直往前找,直到找到第一個非字母或者數字的字符 # 如果找了20個還找不到,則放棄 這樣做是為了避免行尾的單詞被一分為二 while ((v_ch == ":") || (v_ch >= "a" && v_ch <= "z") || (v_ch >= "A" && v_ch <= "Z") || (v_ch >= "0" && v_ch <= "9")) { --nLen; v_ch = substr(v_str_sub, nLen, 1); if (nLen <= v_line_len - 20) { nLen = v_line_len; break; } } v_str_sub = substr(v_str, 1, nLen); print v_str_sub; v_str = substr(v_str, nLen + 1); } }' 20190603: 今天學習了makefile一個特殊變數:MAKECMDGOALS 這個變數記錄了命令列引數指定的終極目標串列, 舉例:執行make debug命令,MAKECMDGOALS的值是debug,在遞回腳本里面這個引數很有用, make還有其它幾個特殊變數,分別代表當前目錄,makefile名稱,make版本等 20190604: 今天碰到一個情況:df顯示磁盤空間還剩很多,但用cp命令拷貝生成新檔案時提示no space left on device, 上網查了一下,說可能是磁盤inodes空間滿了,用df -i可以查看, 我試了一下,果然如此,用df可以看出inodes空間占用 100%,cd到懷疑有大量檔案的目錄下,ls時卡住了,無法定位是什么檔案導致的, 沒辦法最后只能把目錄刪了重建, 20190605: oracle寫sql時如果需要指定轉義字符(k逃逸字符),可以使用escape關鍵字,例如: select owner, table_name from all_tables where table_name like '%\_17_\_%' escape '\'; -- 找出所有以包含"_17?_"的表名,?代表一個任意字符 fuser可以查看檔案被哪些行程打開 ${0}:${LINENO} shell輸出日志加上這個前綴,可以更方便定位 bash里面還可以使用${FUNCNAME[0]}輸出當前函式名稱 一個日志輸出函式示例: logerr() { echo "error at ${BASH_LINENO[1]}${FUNCNAME[1]} $*" } 20190613: 由于oracle的索引是不記錄NUUL值記錄,所以唯一索引列是可以存在多個為NULL的記錄的 另外,oracle的NULL和空串''是等價的 20190614: 寫了一個方便查看檔案資料的awk腳本(讀取行,拆成多列顯示) awk -F '\\|@\\|' '{ printf("%s:%d\n", FILENAME, FNR); for (i = 1; i <= NF; ++i) { printf("% 4d:%s\n", i, $i); } getline < "-"; }' 20190616: 一個web請求可能需要經過的cdn,dns負載均衡,f5負載均衡,nginx反向代理,訊息佇列,redis,資料庫(讀寫分離,集群),檔案緩沖等這么多種快取機制 20190620: 使用static變數時,記得注意變數之間關連關系,我這次就踩了坑: 我在程式里面使用了兩個static變數s_this_date快取當前時間,s_next_date快取s_this_date的下一天, 為減少計算時間消耗,只有當s_this_date變化時,才會計算s_next_date, 但代碼里面有個問題,存在修改了s_this_date后,在s_next_date修改前函式就退出的情況, 這樣就導致s_next_date有時跟s_this_date不是差一天的關系, 20190630: select for update是上鎖,不是檢查有沒有鎖 20190701: 今天使用了merge陳述句幫同事優化了一個陳述句: update tab_check_task t set Mcht_Name=(select Mcht_Name from tab_mcho_sre where mcht_no=t.mrch_id ); 13分鐘沒跑完 merge into tab_check_task t using tab_mcho_sre mcht on (mcht.mcht_no=t.mrch_id) when matched then update set t.Mcht_Name = mcht.Mcht_Name; 4分鐘跑完 今天一個同事說他在.bash_profile配置了兩個環境變數,但重新登錄后只有其中一個生效了,問我怎么回事, 我建議他執行sh -x .bash_profile,看是哪里出錯了,他執行后很快回復我,是.bash_profile后面加載了另一個腳本,那個腳本里面有同名變數,相互覆寫了, sh -x 是除錯shell的利器,‘ 20190704: 今天發現time命令和\time呼叫的是不同的程式,\time呼叫的是/usr/bin下面的time程式,而time則是呼叫shell內置的功能 今天看到一個除錯一個release程式的方法: 先使用-g重新編譯一次程式,然后取出變數資訊 objcopy --only-keep-debug projedtd projedtsymbol.dbg 再使用這些變數資訊去除錯release程式 gdb -q symbol=projedtsymbol.dbg --exec=projectr x /10x $sp # gdb查看當前程式堆疊的內容: 列印stack的前10個元素 20190705: 除錯記憶體問題時,可以設定環境變數 export MALLOC_CHECK_=3 這樣程式在發現記憶體例外時會列印出錯資訊并退出 20190712: 今天來上班,發現昨天晚上掛的一個crontab任務沒啟動,一開始沒想明白是什么原因,以為是任務腳本有問題,查半天沒查出來, 后來在同事建議下,我試了加了一個簡單的crontab任務,設定成1分鐘后跑,發現也沒啟動,這才確定原來是crontab服務沒啟動,可以使用以下命令確認: service crond status 20190715: proc呼叫存盤程序,出參,也要初始化,不然使用valgrind檢查時會提示未初始化的記憶體, 20190716: 今天使用vallgrind檢測程式時,出現很多記憶體泄漏提示, 但其實我確認代碼沒寫錯,后來查了一下,是因為使用了glib的一些資料結構,里面使用了記憶體池, 并且查到了,如果設定環境變數G_SLICE=always-malloc這樣就不會啟動glib記憶體池, 20190717: 今天看代碼時發現一段邏輯,需要判斷一張資料庫表有沒有記錄,里面直接用了select count(*) from tab檢查,其實可以使用 select count(*) from tab where rownum < 2,因為要檢查有沒有記錄,只要有一條記錄就可以判斷,不需要全表掃一次, 20190718: 從csdn上保存的博文,在沒有連公網時打開,總是過一會就自動重定向到csdn.net,很煩人,雖然知道肯定是某段js搞鬼,但一直沒搞清楚是哪段, 今天發現用谷歌瀏覽器按f12可以單步除錯網頁,利用這功能終于定位到了“元兇”, <img src=https://www.cnblogs.com/kingstarer/p/"https://blog.csdn.net/chuxuan909/article/details/72465748?utm_source=blogxgwz0" onerror="setTimeout(function() {if(!/(csdn.net|iteye.com|baiducontent.com|googleusercontent.com|360webcache.com|sogoucdn.com|bingj.com|baidu.com)$/. test(window.location.hostname)) {window.location.href="\x68\x74\x74\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x63\x73\x64\x6e\x2e\x6e\x65\x74"}},3000); "> 把onerror批量替換成onerror1即可,這段代碼里"\x68\x74\x74\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x63\x73\x64\x6e\x2e\x6e\x65\x74" 其實就是https://www.csdn.net 20190719: kafka閱讀心得: 觀察臟頁 cat /proc/vmstat | egrep 'dirty|writeback' 啟動g1垃圾回收器 export KAFKA_JVM_PERFORMANCE_OPTS='-server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+DisableExplicitGC -Djava.awt.headless=true" kafka-server-start.sh -daemon server.propertities 20190724: proc對匿名結構體支持不大好,建議盡量使用typedef struct __StuName_t{}StuName_t;代替typedef struct {}StuName_t; 20190725: sqlldr加快裝載速度的引數設定建議: sqlldr errors=9999999 control=tab.ctl log=tmp.txt sreamsize=256000 bindsize=2560000 userid=stlm_o/stlm_o@RCC8TPD2 direct=TRUE rows=1000 20190726: 今天發現有一些商家使用退貨交易套利: 第一天先做了一大筆消費,第二天一開門就做了退貨交易,由于這時商戶資金不足,為了不影響用戶體驗,我們系統會先墊錢給用戶,后面再從商戶的賬戶扣錢, 但有一些不法商戶跟消費者合作,退款后立即注銷商戶,導致系統出現短款, 為了解決這個問題,我們緊急做了一個白名單功能,不在白名單的商戶,當日營業額不足時不允許退貨,(這種情況商家一定要退貨時,需要自己先做一筆大額消費) 20190731: 今天過來發現crontab任務沒啟動(檢查crontab任務重定向輸出的日志,發現不存在),檢查一下,發現因為是硬碟沒空間了,這才知道原來硬碟滿了會導致crontab調度例外, 20190801: 今天才發現df出來的size != used + free,查了一下,說原因有兩個1 inode節點空間占用了一些磁盤空間,2 系統會占用一些預留空間,以便出現磁盤空間滿時止root用戶登錄處理(一般是0.05 * size),不過我的系統加上這兩項空間,還是差一些,不知道是不是還有其它隱藏的占用空間情況, 20190810: 這段時間都在研究kafka,kafka其實就是一個分布式訊息佇列中間件,主要用于吞吐量高(號稱每秒十萬寫,百萬讀),但業務邏輯簡單(如資料同步,通知)的場景, kafka借助zookeeper實作了集群,高可用特性,本身又支持多副本功能,集群少量機器出現故障時仍可正常使用,但kafka的訊息一致性比較差,容易出現訊息丟失或者重復訊息的情況, kafka支持多個不同客戶端訂閱同一個訊息主題,這樣可以做到一份訊息多個場景使用,這點對于資料分析特別有利,另外,kafak的訊息都是落盤存盤的,所以生產者跟消費者耦合非常低,可以分別獨立部署運行, 20190812: 這幾天用glib寫了一個多執行緒的程式,鏈接時報g_thread_init函式未定義(undefined reference to `g_thread_init'),但奇怪的是我明明有libglib-2.0.a,原來網上查了一下,原來是要額外鏈接libgthread-2.0.a cout從右到左計算,從左到右輸出 lsof -u username # 列出用戶所有打開的檔案 20190814: 今天編譯libcjson.a,發現默認的編譯選項是沒有開啟-O2的,不知道為什么,我把它改了一下,增加-O2選項,發現只快了一點, 跑10萬次cJSON_test,耗時從1.15s降到0.97,又測驗了5000000次的,發現分別耗時是55秒和45秒,還是有點效果的,但效果不明顯,先使用默認的吧, 今天又出現某個bug增加列印日志 就不出現,不加就出現的情況,后來仔細看代碼,是因為我有兩個不同檔案,同時宣告了一個全域變數(名字一樣),其中一個有初始化,一個未初始化,導致兩個變數鏈接時被合并成一個,從而互相影響, 20190815: 今天發生一個事情,由于溝通問題,我跟同事改了同一個word檔案,只能想辦法跟他的合并,網上找了一下,說word的審閱選項卡下面有比較功能, 試了一下,確實是可以,但不是很直觀,后來經同事提醒,原來svn自帶啟動word比較功能, 20190820: 今天發現ue有一個好用的快捷鍵F5,作用是把大寫資料庫列名轉成駝峰式的大小寫混合的單詞,例如THIS_IS_A_TEST會被變成This_Is_A_Test 20190821: 小知識點:free命令輸出的記憶體使用資訊里面used并不代表真正可用記憶體,可用記憶體是free + buffer + cached之和 20190822: extern int g_lib_flag = 0; //鏈接時報錯 extern不能增加初始化 20190823: 今天生產系統發現有一些交易存在沖正交易比正交易先到,導致對賬不平的情況, 出現bug的主要原因是我們接到沖正請求時發現資料庫沒有正交易,直接就回傳前端沖正失敗(交易不存在),但沒有登記流水,后面正交易過來時我們當普通交易正常處理了,但前端系統不知道,所以沒有重復發起沖正請求, 暫時要求前端嚴格控制順序發送交易,以后再出現需要考慮一些機制避免這種差異, 20190904: 這兩天使用Loadrunner對程式進行壓測,壓測程序中經常提示壓力機cpu使用率超過80%的提示,開始時我沒怎么在意,想著不是目標機80%,壓力機沒到100%應該沒事, 后來偶然把測驗腳本改一下,壓力機cpu消耗下來,發現目標機tps居然有明顯升高, 看來以后要注意一下, oracle默認洗掉表后不會清除空間,而是放到回收站,如果要強制識訓這些空間,可以使用select * from user_recyclebin;查出表名,然后使用 purge table "表名"; 20190914: 今天發現一個bug: 我們一個程式在執行資料庫查詢程序中,收到信號,轉去執行中斷處理函式, 結果函式里面也有資料庫查詢操作,導致死鎖了,(proc的api對于這種情況沒處理好) 臨時解決方法是在資料庫操作期間先屏蔽了信號,不過這樣可能導致信號漏處理,以后需要優化, 20191030: 之前對日志組件做了優化,使用自己寫的帶快取的localTimeCache代替localTime,減少鎖競爭, 今天寫了一個測驗,發現在高頻呼叫下時,有時會出現跟localTime的秒數有差異的情況,代碼如下: while (1) { int *pSecCache = &localTimeCache(&time)->tm_sec; int *pSec = &localTime(&time)->tm_sec; LogInfo("cache %d", *pSecCache); LogInfo(" nocache %d\n", *pSec); } 后來仔細分析,原來LogInfo里面也會呼叫localTimeCache,導致pSecCache的值發生了變化,對于這種靜態變數使用,需要多注意, 今天又碰上一個行程因為信號處理函式出bug的情況:在呼叫system函式時接到信號,轉到信號處理函式時又呼叫了system,導致死鎖, 20191030: proc查詢資料庫資料到char陣列時,如果目標陣列空間不足,則會截斷(但還是會以\0會結尾), 如果陣列空間比資料庫欄位長度長,則剩下的空間會以空格填充, 20191114: void func(char * const arr[]) 只讀字串陣列 strcpy(arr[0], "123"); //ok arr[0] = "123"; // not ok 20191125: oracle 在線壓縮磁區的方法: 1 完美方法 使用12 c的move online功能 2 變通方法a 先將要壓縮的磁區資料匯出來到臨時表并壓縮 把臨時表的資料跟待壓縮磁區做在線交換(要加上with update indexes) 從臨時表里面找操作期間變更的記錄,更新回壓縮磁區 3 變通方法b(壓縮磁區不需要查詢的情況) 建一個空表 把磁區的資料跟空表交換 對表做壓縮 20191126: oracle做磁區交換時,可以使用including indexes,在做交換磁區時順便維護local索引 float型別的變數在計算時很容易損失精度:0.1回圈加8次 > 0.8 double也會,但比float損失精度的概率會低一些, 20191127: 今天出現一個問題:我在程式里面一個sql使用了hint:result_cache想提高查詢速度,單行程測驗時發現確實有明顯改善, 但到生產機器時,多行程環境跑的時候卻導致了大量lanth free事件,反而拖慢了速度,后來dba臨時關閉了result_cache功能才恢復, 20191204: 行程數估計 機器cpu跑滿 看是否有堵塞 交易太頻繁,沒有發心跳 mq recv 20191205: oracle使用merge into有時也會因為主鍵沖突失敗,主要是出現在高并發插入的場景, 20191206: watch命令特別方便監測行程運行情況:執行 watch 'ps aux www|grep -w -E "ProgName|TTY"' 系統會顯示行程資源占用情況,并且自動每2秒重繪 20191207: 今天使用valgrind檢測程式時,發現好多invalid write,仔細檢查,發現是因為 strptime函式沒有填充目標變數的夏令時欄位,導致這個欄位是未知位元組,然后我又拷貝了這個變數的值,就出現這個問題, 解決方法是strptime函式執行后再設定tm_isdst為0 20191208: 今天發現c++的throw比較消耗資源,即使沒觸發throw部分代碼, 我寫了兩個程式,代碼是一樣的,只是一個加了try catch,另一個沒有加, 編譯后運行,發現效率相差20% 20191210: 今天使用callgrind檢測程式性能時,發現我有一個函式雖然有呼叫,但是在callgrind輸出結果卻看不到實際呼叫情況, 后來經過多次測驗,發現原來是這個程式被編譯器自動行內了,雖然我沒有指定inline,也沒有加-O,但編譯器還是將它行內了, 兩個靜態庫的函式互相呼叫,會導致鏈接時無論怎么調整順序,都有可能報“找不到物件”,這時可以多鏈接幾次: 例如我有libA.a libB.a相互呼叫了,寫makefile時可這樣:-lA -lB -lA -lB,不過這只是臨時處理方案,實際上不應該出現這樣的相互呼叫的情況, linux訊息佇列好像沒有支持io多路復用的api,我設想了一個解決方案: 啟動一個專門的讀執行緒,使用阻塞讀訊息佇列,如果讀到了訊息,則發送一個信號給主行程監聽的句柄(例如匿名管道) 20191211: 最后生產資料庫空間告警,只能把一些不常用的流水表做壓縮,以節省空間,幸好oracle提供了壓縮表的功能, 20191212: 最近半年做了一個查詢庫同步的功能,程序還是很折騰的: 最開始打算直接使用goldengate同上交易庫的表和查詢庫的表,這是最方便的, 但是運維團隊說沒有購買這個工具,而且有安全問題,于是只能作罷, 后來又想著在業務庫表加上觸發器,把修改內容登記到一個日志表,再寫一個程式根據日志表同步資料到查詢庫, 但運維團隊又說不能加觸發器,因為觸發器影響性能, 之后有同事提出使用物化視圖,但我查了一下網上,oracle的物化視圖功能bug很多,沒建議, 最后偶然間,我查proc開發檔案時發現oracle提供了一種returning into的語法,可以回傳update/insert修改后記錄的內容, 于是最后決定: 1 并且給業務表增加修改時間欄位,insert或update時順便更新這個欄位, 2 在所有修改資料庫流水表的代碼增加returning into子句,把修改結果查詢回來, 3 把回傳的記錄發給同步行程,同步行程把記錄merge到查詢庫的表 4 為防止順序錯亂,同步查詢庫時需要加上判斷,如果修改時間比資料庫記錄的時間早,則不處理 20191216: 檔案編碼不是檔案的屬性之一,這是很多人沒搞清楚的,他們往往會說“我檔案已經保存成gbk編碼,為什么傳到linux上打開還是亂碼” 其實問題就是檔案編碼不是檔案的屬性,linux上的檔案工具打開檔案時只能“猜測”檔案的編碼,如果它猜錯了,顯示就是亂碼, 20191217: 邏輯尺:一個欄位,其中的各個位,由我們自己規定為0、1, 然后,用這一串0、1,依次去選擇兩種操作, 這個位元組,往往有人稱之為邏輯尺, 20191218: 今天用proc編譯.pc檔案時出現core,看原因是段錯誤, 后來檢查一下,是由于檔案路徑太長,超過100個字符,觸發proc的bug 20191219: vim顯示不了回車符解決方法:vi -b vim輸入回車符方法:按住ctrl,再按v,再松開v按m 20191220: 今天使用loadrunner壓測的程序中,報了不少"server has shut down the connection"的錯誤 觀察被壓測機器的接入程式,卻沒有發現例外, 猜測是由于客戶端機TIME_WAIT太多造成的,不過修改后也沒多大改善,具體原因暫時還不知道 20191223: Disql是DM資料庫自帶的資料庫命令列客戶端工具,類似于sqlplus,不過做得不太好, 今天隨便試用了一下,就發現一個bug:進入disql后,如果不執行sql,直接按ctrl + c,會導致程式退出 20191225: %08s,不是用0左補齊,而是用空格 20191226: oracle在多個會話查詢同一條記錄,容易出現LanchFree競爭事件 20191227: 今天做非功能測驗,發現有一個交易跑一會tps就急劇下降,一開始我以為受清理日志影響,但后來把日志清理停了也一樣, 觀察vmstat輸出,tps下降時cpu使用wait的資料急劇上升,并且so也上升,猜測是由于記憶體換頁影響,使用ps aux www觀察,發現行程占用的記憶體大漲, iostat -x 2 5 # 每隔2秒列印io資訊 共5次 20191228: gdb打開core檔案,使用where發現堆疊例外,有一些函式根本不可能呼叫到,但卻顯示在輸出里面, 后來發現core檔案生成時間比程式的修改時間還早,說明程式被修改過,后來我找了修改前的程式,重新用gdb除錯,這里where輸出就正常了, 20191230: 今天啟動業務系統時出現有一些行程打開監聽埠時失敗,重啟時就好了, 仔細分析日志,發現問題是某些行程使用的隨機埠正好與監聽埠沖突了, 20191231: 今天壓測時,發現tps突然下降,看vmstat,bi突然增加,wa增加,查看free,發現buffer突然減少,原來是有行程占用了太多記憶體導致的, 20200106: 今天做非功能測驗時,發現資料庫很多"cursor: pin S wait on X"的等待事件, 百度查了一下,出現這個等待事件的原因很多,其中一個比較常見的情況是資料庫硬決議sql太多, 但我們系統高并發的sql都是使用系結變數的,理論上不應該出現很多硬決議,所以當時認為不是這個問題,(其實當時應該檢查一下硬決議次數再下結論) 后來偶然間發現有一些20年的流水表沒建,導致sql執行時出現"table or veiw not exist"的錯誤,這才明白過來為什么會有很多硬決議, 后來把表建好后再跑就沒這個等待事件了, 20200107: 看網上的實驗說是rac切換時,該節點沒提交事務會自動回滾 如果應用此時不進行rollback操作,再執行其他陳述句(包括commit)都會報ora-25402 20200108: loadrunner引數取隨機值設定方法: Select next row: Random Update Value On: Each iteration 20200109: 查看系統在監聽的埠和對應程式名:netstat -anp|grep LIS 20200110: 資料庫緩慢,執行 set linesize 240 col sid format 9999 col s# format 99999 col username format a15 col event format a30 col machine format a20 col p123 format a18 col wt format 999 col SQL_ID for a18 alter session set cursor_sharing=force; SELECT S.SID, S.SERIAL# S#, P.SPID, NVL(S.USERNAME, SUBSTR(P.PROGRAM, LENGTH(P.PROGRAM) - 6)) USERNAME, S.MACHINE, S.EVENT, S.P1 || '/' || S.P2 || '/' || S.P3 P123, S.WAIT_TIME WT, NVL(SQL_ID, S.PREV_SQL_ID) SQL_ID FROM V$PROCESS P, V$SESSION S WHERE P.ADDR = S.PADDR AND S.STATUS = 'ACTIVE' AND P.BACKGROUND IS NULL; 等待事件sga:allocation forcing component growth 20200217: 今天安裝redis時發現一些語法比較奇怪的腳本,上網查了一下,原來是tcl腳本 百度了一下,介紹這樣的:“Tcl 是一種 腳本語言, 由John Ousterhout創建, TCL很好學,功能很強大,TCL經常被用于 快速原型開發,腳本編程, GUI和測驗等方面,”,TCL念作“踢叩” "tickle" 以后有機會要了解一下 20200220: make install時可以臨時指定安裝目錄,make PREFIX=/home/huangcihui/local install 20200225: 今天測驗redis性能時偶然發現redis服務設定了密碼,但運行redis-benchmark時沒輸入密碼引數結果也能跑成功, 后來研究了一下代碼才發現,redis-benchmark工具默認對回傳結果不做檢查,需要加上-e引數,才會檢查redis命令執行結果是否成功, 20200228: 今天發現在高并發情況下,oracle的merge陳述句也可能會出現主鍵沖突,之前在低并發情況時一直用merge一次完成INSERT+UPDATE,避免主鍵沖突, 20200229: 今天同事反映,生產上有一些交易被拒絕,報20200229日期非法,但今年是閏年,理論上不應該出現這個問題,而且奇怪的是不是所有交易都被拒絕,后來查了一下代碼,里面有這樣的代碼: char year[5]; strcpy(year, dateTime, 4); if (IsLeap(atoi(year)) { ... } 于是明白了,程式里面對于year沒有做初始化,導致有時atoi結果出現例外,導致程式判斷閏年錯誤, 20200302: ps aux www|grep -E 'TTY|redis',這樣可以保留ps輸出的標題 201200303: 當你使用sudo去執行一個程式時,處于安全的考慮,這個程式將在一個新的、最小化的環境中執行,也就是說,諸如PATH這樣的環境變數,在sudo命令下已經被重置成默認狀態了, 要想改變PATH在sudo會話中的初始值,用文本編輯器打開/etc/sudoers檔案,找到”secure_path”一行,當你執行sudo 命令時,”secure_path”中包含的路徑將被當做默認PATH變數使用, sudo -v 延長密碼有效期限5分鐘,這樣就可以臨時省去sudo輸入密碼的麻煩 20200304: redhat的屏保居然是向上拖動的,不是像windows一樣點擊自動消失的, 開機后等好久一直停留在時間界面,點了沒反應,我一直以為開機沒完成,,, 20200305: 新安裝的redhat默認啟動是進入圖形界面,可以執行命令 systemctl set-default multi-user.target,這樣重啟后優先進入命令列界面 如果以后需要恢復回來,則執行systemctl set-default graphical.target即可 臨時命令列切換至桌面命令: init 5 臨時桌面切換至命令列命令: init 3 20200308: redhat配置網卡自動連接,并使用dhcp配置網路地址方法: 需要修改配置/etc/sysconfig/network-scripts/ifcfg-<網卡名稱> 假設網卡名字叫eth0,則修改/etc/sysconfig/network-scripts/ifcfg-eth0 將BOOTPROTO改為dhcp,ONBOOT改為yes 修改后使用ifup eth0啟動連接
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/56853.html
標籤:C++
上一篇:C++運算子多載
