前天正在跟前端的同事調試功能。服務器開好,模擬的玩家登錄好,就在倒計時。這時突然運營的同事跑過來說要統計幾個服務器玩家的一些情況,也就是需要從幾個服的數據庫導出部分玩家的數據。好吧,我看了一下時間,11:47。心想,跟前端調試完,去吃個飯再午休一下那就下午再給吧。沒想對方來一句"就導個數據庫而已,要這么久么?",而且還是直接跟我上司說的。我嚓,好吧,我導??蓡栴}來了,平時的統計是由php做的,批量部署這些是由運維做的。服務端完全沒有對應的工具。而且服務器是在阿里云上的,數據庫的用戶是限制了ip段登錄的,我所在的ip沒法登錄的。于是,只好終止調試,切ip,寫sql,然后用navicat手動一個個服務器導出數據到excel。
事后想想,還是寫個腳本吧,不然以后還是會被坑的。
從環境來看,數據庫不能直接登錄,沒法直接導出。不過可以由運維提供key通過ssh登錄到遠程服務器再將數據導出到本地。
先配置ssh通過key登錄服務器。這里略過...
然后就是通過ssh執行命令。先看一下ssh的幫助文檔:
usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L [bind_address:]port:host:hostport] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q cipher | cipher-auth | mac | kex | key] [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname [command]
最后一項就是執行指令的。假如遠程服務器地址為180.97.33.108,開啟的ssh端口為998,ssh用戶名為xzc,然后想通過在遠程服務器上執行命令ls,那么腳本應該這么寫:
如果ssh的認證key已配置好,那么會把登錄后當前目錄下的文件列出來,當然第一次登錄會提示保存遠程服務器的指紋?,F在是要導出數據庫的數據,那么需要把ls命令換成導出數據庫的命令。
上面兩命令都可以使用數據庫用戶xzc,密碼xzcpwd從數據庫xzc_db打印出user表,使用的是默認的本地數據庫地址localhost,默認的端口。如果不是默認,需要指定。
登錄OK了,打印也OK了,那么下一步就是導出到文件了。這在bash也就是一個 > 的事。把上面的命令拼起來就是:
這樣就把user表導出來本地的user.txt中了。注意"> user.txt"如果放到 ''里則是在遠程服務器執行,導出的文件在遠程服務器。文件現在也有了,不過是txt,這樣交給運營不太好吧。那就導出excel吧。不過遺憾的是我查了N多資料,也找不到mysql不依賴第三方插件或工具導出原生excel的方法。而navicat導出的可是貨真價實的excel,如果用notepad++之類的文本工具打開是會亂碼的,并且導出的文件不會有編碼問題。幸好如果一個txt以tab分割的話,excel也是能認得出來的。于是把user.txt改名user.xls就可以了。但這樣做的問題是excel會按自己的方式處理內容的。比如把一個很大的數字轉換成科學記數法形式。這些都得手動去處理一下了。
最后,就是寫成腳本批量操作了。附上我使用的腳本一個:
#!/bin/bash# 通過ssh遠程執行遠程指令# 需要先部署key認證,保證ssh只需要ip、port即可連接# 如果需要和遠程服務器交互,請參考ssh的-t、-tt參數# 如果需要反復登錄服務器執行多條指令,請使用ssh的通道重用# 參考:http://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing# 使用通道需要注意退出通道,如"ssh github.com -O exit"或者"ssh github.com -O stop"# --by coding my life#分別設置ssh用戶名、數據庫用戶名、數據庫密碼、導出數據SSH_USER='xzc_ssh'DB_USER='xzc_db'DB_PWD='xzc_db_pwd123'EXP_PATH=export_data/# 執行遠程命令# $1 服務器ip# $2 ssh端口# $3 指令function exec_remote_command(){ ssh $SSH_USER@$1 -p $2 '$3'}# 執行遠程sql,導出數據# $1 服務器ip# $2 ssh端口# $3 指令,多個sql指令如select * from user;select * from bag;也可執行,但結果將會寫到同一個文件# s4 服務器# $5 導出文件function export_remote_sql(){ echo export from $4 ... cmd="echo /"$3/" | mysql $4 -u$DB_USER -p$DB_PWD --default-character-set=utf8" ssh $SSH_USER@$1 -p $2 "$cmd" > $EXP_PATH$4_$5 #如果要導出到遠程服務器,將把 > $EXP_PATH$4_$5放到cmd中}# $1 區服名# $2 ip# $3 端口function exec_sqls(){ cat SQLS | while read sql ; do fc=${sql:0:1} if [ "#" == "$fc" ]; then #被注釋的不處理 continue fi #sql語句中包含空格,不能再以空格來區分。最后一個空格后的是導出的文件名 exp_file="${sql##* }" #兩個#表示正則以最大長度匹配*和一個空格(*后面的空格),截取余下的賦值給exp_file sql_cmd="${sql%% $exp_file}" #兩個%表示從右至左刪除%%以后的內容 export_remote_sql $2 $3 "$sql_cmd" $1 "$exp_file" done}# 需要在當前目錄下創建服務器列表文件SERVERS,格式為"數據庫名 ip ssh端口",如"xzc_game_s99 127.0.0.1 22"# 需要在當前目錄下創建sql命令列表文件SQLS,格式為"sql語句 導出的文件",如"select * from user; user.xls"# 多個sql請注意用;分開,sql必須以;結束# 文件名中不能包含空格,最終導出的文件為"數據庫名_文件名",如"xzc_game_s99_user.xls"mkdir -p $EXP_PATHcat SERVERS | while read server ; do fc=${server:0:1} if [ "#" == "$fc" ]; then #被注釋的不處理 continue fi name=`echo $server|awk '{print $1}'` ip=`echo $server|awk '{print $2}'` port=`echo $server|awk '{print $3}'` exec_sqls $name $ip $portdone
當前目錄下的文件如下,其中SERVERS是服務器列表,里面指定數據庫名,ip,ss端口,SQLS則指定sql指令及導出的文件名。這兩個文件里以#開頭的都不會處理:
xzc@xzc-HP-ProBook-4446s:~/桌面/remote_cmd$ lsremote_cmd.sh SERVERS SQLSxzc@xzc-HP-ProBook-4446s:~/桌面/remote_cmd$ cat SERVERS xzc_game_s99 120.0.0.99 6162xzc_game_s91 120.0.0.91 6162xzc_game_s92 120.0.0.92 6162xzc_game_s93 120.0.0.93 6162xzc_game_s94 120.0.0.94 6162#xzc_game_s91 120.0.0.91 6162xzc@xzc-HP-ProBook-4446s:~/桌面/remote_cmd$ cat SQLS #select * money from money; money.xlsselect * from user; user.xlsxzc@xzc-HP-ProBook-4446s:~/桌面/remote_cmd$
到這里,腳本基本完成了要求。
新聞熱點
疑難解答