redis队列缓存 + mysql 批量入库 + php离线整合
需求背景:有個調用統計日志存儲和統計需求,要求存儲到mysql中;存儲數據高峰能達到日均千萬,瓶頸在于直接入庫并發太高,可能會把mysql干垮。
問題分析
思考:應用網站架構的衍化過程中,應用最新的框架和工具技術固然是最優選擇;但是,如果能在現有的框架的基礎上提出簡單可依賴的解決方案,未嘗不是一種提升自我的嘗試。
解決:
問題一:要求日志最好入庫;但是,直接入庫mysql確實扛不住,批量入庫沒有問題,done。【批量入庫和直接入庫性能差異參考文章】
問題二:批量入庫就需要有高并發的消息隊列,決定采用redis list 仿真實現,而且方便回滾。
問題三:日志量畢竟大,保存最近30條足矣,決定用php寫個離線統計和清理腳本。
done,下面是小拽的簡單實現過程
一:設計數據庫表和存儲
考慮到log系統對數據庫的性能更多一些,穩定性和安全性沒有那么高,存儲引擎自然是只支持select insert 沒有索引的archive。如果確實有update需求,也可以采用myISAM。
考慮到log是實時記錄的所有數據,數量可能巨大,主鍵采用bigint,自增即可。
考慮到log系統以寫為主,統計采用離線計算,字段均不要出現索引,因為一方面可能會影響插入數據效率,另外讀時候會造成死鎖,影響寫數據。
二:redis存儲數據形成消息隊列
由于高并發,盡可能簡單,直接,上代碼。
<?php/*************************************************************************** * *?獲取到的調用日志,存入redis的隊列中. *?$Id$ * **************************************************************************//** *?@file?saveLog.php *?@date?2015/11/06?20:47:13 *?@author:cuihuan *?@version?$Revision$ *?@brief* **///?獲取info$interface_info?=?$_GET['info'];//?存入redis隊列$redis?=?new?Redis();$redis->connect('xx',?6379);$redis->auth("password");//?加上時間戳存入隊列$now_time?=?date("Y-m-d?H:i:s");$redis->rPush("call_log",?$interface_info?.?"%"?.?$now_time);$redis->close();/*?vim:?set?ts=4?sw=4?sts=4?tw=100?*/?>三:數據定時批量入庫。
定時讀取redis消息隊列里面的數據,批量入庫。
<?php/***?獲取redis消息隊列中的腳本,拼接sql,批量入庫。*?@update?2015-11-07?添加失敗消息隊列回滾機制?**?@Author:cuihuan*?2015-11-06*?*///?init?redis$redis_xx?=?new?Redis();$redis_xx->connect('ip',?port);$redis_xx->auth("password");//?獲取現有消息隊列的長度$count?=?0;$max?=?$redis_xx->lLen("call_log");//?獲取消息隊列的內容,拼接sql$insert_sql?=?"insert?into?fb_call_log?(`interface_name`,?`createtime`)?values?";//?回滾數組$roll_back_arr?=?array();while?($count?<?$max)?{????$log_info?=?$redis_cq01->lPop("call_log");????$roll_back_arr?=?$log_info;????if?($log_info?==?'nil'?||?!isset($log_info))?{????????$insert_sql?.=?";";????????break;}????//?切割出時間和info$log_info_arr?=?explode("%",$log_info);????$insert_sql?.=?"?('".$log_info_arr[0]."','".$log_info_arr[1]."'),";????$count++; }//?判定存在數據,批量入庫if?($count?!=?0)?{????$link_2004?=?mysql_connect('ip:port',?'user',?'password');????if?(!$link_2004)?{????????die("Could?not?connect:"?.?mysql_error());}????$crowd_db?=?mysql_select_db('fb_log',?$link_2004);????$insert_sql?=?rtrim($insert_sql,",").";";????$res?=?mysql_query($insert_sql);????//?輸出入庫log和入庫結果;echo?date("Y-m-d?H:i:s")."insert?".$count."?log?info?result:";????echo?json_encode($res);????echo?"</br>\n";????//?數據庫插入失敗回滾if(!$res){???????foreach($roll_back_arr?as?$k){???????????$redis_xx->rPush("call_log",?$k);}}?//?釋放連接mysql_free_result($res);mysql_close($link_2004); }//?釋放redis$redis_cq01->close();?>四:離線天級統計和清理數據腳本
?php/** *?static?log?:每天離線統計代碼日志和刪除五天前的日志 * *?@Author:cuihuan *?2015-11-06 *?*///?離線統計$link_2004?=?mysql_connect('ip:port',?'user',?'pwd');if?(!$link_2004)?{????die("Could?not?connect:"?.?mysql_error()); }$crowd_db?=?mysql_select_db('fb_log',?$link_2004);//?統計昨天的數據$day_time?=?date("Y-m-d",?time()?-?60?*?60?*?24?*?1);$static_sql?=?"get?sql";$res?=?mysql_query($static_sql,?$link_2004);//?獲取結果入庫略//?清理15天之前的數據$before_15_day?=?date("Y-m-d",?time()?-?60?*?60?*?24?*?15);$delete_sql?=?"delete?from?xxx?where?createtime?<?'"?.?$before_15_day?.?"'";try?{????$res?=?mysql_query($delete_sql); }catch(Exception?$e){????echo?json_encode($e)."\n";????echo?"delete?result:".json_encode($res)."\n"; }mysql_close($link_2004);?>五:代碼部署
主要是部署,批量入庫腳本的調用和天級統計腳本,crontab例行運行。
#?批量入庫腳本*/2?*?*?*?*?/home/cuihuan/xxx/lamp/php5/bin/php?/home/cuihuan/xxx/batchLog.php?>>/home/cuihuan/xxx/batchlog.log#?天級統計腳本0?5?*?*?*?/home/cuihuan/xxx/php5/bin/php?/home/cuihuan/xxx/staticLog.php?>>/home/cuihuan/xxx/staticLog.log總結:相對于其他復雜的方式處理高并發,這個解決方案簡單有效:通過redis緩存抗壓,mysql批量入庫解決數據庫瓶頸,離線計算解決統計數據,通過定期清理保證庫的大小。
轉載于:https://blog.51cto.com/ww123/1873114
總結
以上是生活随笔為你收集整理的redis队列缓存 + mysql 批量入库 + php离线整合的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sass1:
- 下一篇: ETL AUTOMATION介绍