1. 程式人生 > >我的物聯網項目(七)前期線上事故

我的物聯網項目(七)前期線上事故

推廣策略 width data 字段 ram throw tco 建立 寫代碼

一 MQTT連接數報警

項目上線一個月左右,投放出去的搖搖車數量大概在200量左右,平均每天在線數(聽說有些商家精打細算,有小孩需要坐車了才插電,平時都不插電,還有些幹脆一直仍在角落懶的管)也就維持在100左右,當時在阿裏雲購買的MQTT配置是連接數上限2000(MQTT是按連接數購買的),像目前的搖搖車投放數用當時的配置綽綽有余了,連續一個月以來,都是正常化(現在想來,當初的推廣策略不成熟,每天投放的搖搖車數量也是要麽一天3,4臺,要麽連續好幾天才推廣3,4臺),所以問題並沒有暴露出來,不過出來混遲早要還的。

有天下午快下班的時候,突然MQTT不斷報警,手機上5秒一次收到報警短信,提示MQTT連接數已經超標(用阿裏雲的產品感覺這塊的預警功提示的還是蠻及時),因為當初也有一些搖搖車在做測試,頻繁的使用到MQTT,所以當時也沒太在意,叫測試人員先停一下在做測試(這個裏面很尷尬,搖搖車掃碼啟動用到的測試環境和線上環境是同一個MQTT,這個後面再詳情描述原因)以為過一會連接數會釋放下去,但是手機收到報警短信越來越猛,當時第一時間想到的整體事故點應該在MQTT業務應用層這一端(手機掃碼是通過http請求到MQTT應用層,MQTT應用層再扔消息到阿裏雲MQTT服務器),當初是2臺MQTT應用層在做負載均衡集群,我登錄2臺服務器,分別用top命令查看兩臺服務器一臺CPU在80%左右,另外一臺CPU在60%左右,頓時覺得很詫異,這麽點搖搖車數據請求不至於導致應用服務器承受不了,當時第一時間想到的是看看TCP的目前的連接數,結果一查嚇了跳(使用命令:netstat -natp|awk ‘{print $7}‘ |sort|uniq -c|sort -rn),兩臺服務器的當前連接數都接近快1W多,還在持續上升(因為當時投放出去的搖搖車還在不斷有人在使用消費),這個時候基本定位問題:手機掃碼發送http請求到MQTT應用層,MQTT應用層每次仍消息到阿裏雲MQTT服務器,都需要建立連接。所以問題很有可能是沒有釋放連接,由於當初的代碼邏輯比較簡單,所以直接找到寫這個代碼的開發人員,一起喵了眼代碼,果然如此,修改代碼後,重新發包一切正常,手機報警短信立馬停了。

	public void sendMsgMqtt(String productId, String deviceId, String scontent, String topic){
		String subTopic = getSubTopic(productId, deviceId, topic);
		String clientId = getClientId();
		MemoryPersistence persistence = new MemoryPersistence();
		try {
			 final MqttClient sampleClient = new MqttClient(GlobalConstant.BROKER, clientId, persistence);
             final MqttConnectOptions connOpts = getConnOpts(clientId);
             log.info("Coin Connecting to broker: " + GlobalConstant.BROKER);
             sampleClient.setCallback(new MqttCallback() {
             public void connectionLost(Throwable throwable) {
                 log.info("mqtt connection lost");
                 throwable.printStackTrace();
                 while(!sampleClient.isConnected()){
                     try {
                         sampleClient.connect(connOpts);
                     } catch (MqttException e) {
                         e.printStackTrace();
                     }
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }
             public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
                 log.info("coin messageArrived:" + topic + "------" + new String(mqttMessage.getPayload()));
             }
             public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
                 log.info("coin deliveryComplete:" + iMqttDeliveryToken.getMessageId());
             }
          });
             sampleClient.connect(connOpts);
             try{
            	 scontent = scontent.replace("[", "").replace("]", "");
            	 final MqttMessage message = new MqttMessage(scontent.getBytes());
                 message.setQos(1);
                 log.info("pushed at "+new Date()+" "+ scontent);
                 sampleClient.publish(subTopic, message);
                 log.info("-------send end---------");
             }catch(Exception ex){
            	 ex.printStackTrace();
             }finally{
               	 sampleClient.disconnect();
                 log.info("-------client disConnect()---------"); 
             }
		}catch(Exception ex){
			ex.printStackTrace();
		}
	}

二 數據庫CPU100%

必須要先說下,創業項目初期,當初的業務都是很不清晰的,基本屬於那種摸著石頭過河,走一步算一步,再回頭想想甚至看看市場的反應,然後再修改,所以這個階段更多的是檢驗模式,優化業務,當然資金也有限,而且要求開發叠代要求迅速,所以數據庫當初沒有做集群,就是一臺,玩單機,也正是因為玩單機,所以一些在集群環境下沒有這麽快爆發出來的問題很容易在單機裏面爆發出來。

晚上大概7點左右(晚點7點到9點是搖搖車的使用高峰,白天一般訂單數據較少,到了晚上數據成倍的增漲),收到反饋,說平臺系統打開很慢很慢,而且手機也不斷收到MQTT短信預警,說堆積了大量未消費的訂單,第一時間的反應迅速登錄雲數據庫管理平臺看到數據庫CPU這項指標嚴重標紅,並顯示使用率達到100%。當初數據庫用的是通用型4核8G的,訂單表數據將近40W,打開數據庫性能管理界面查看(如果沒有這種管理界面,也可以通過命令

show processlist來查看正在執行的SQL和explain來分析執行計劃查看慢SQL),發現積累了大量的慢SQL,有些SQL的平均執行時間超過將近1分鐘,很明顯做了數據庫的全局掃描,再加上這段時間的高峰時期,慢SQL堆積,導致CPU資源順序消耗完。

技術分享圖片

當時也正是業務的使用的高峰期,再加上客戶投訴和上面很多人在盯著這個事情,所以我這邊要在短時間內恢復數據庫的正常,當初有幾種選擇可以迅速恢復,將數據庫切換到備數據庫(數據庫是高可用的)或者kill掉一些慢SQL(通過show processlist查看state為Sending data的列,然後kill id),這些都有可能會影響到現在正在使用的業務,萬不得已的情況的不會第一時間去做。我看了下排在前面的幾條慢SQL,其中有些是第一時間可以迅速處理的,比如優化索引,我抱著試試的方法,將之前有些索引重新優化了下(後面會詳細描述),過了幾分鐘,CPU的使用率慢慢的降了下來(沒有優化索引之前大概3秒執行一個記錄,優化索引後1秒可以執行上千個記錄),業務正常了,給我後面做數據庫的優化有了大把時間思考,所以這次事件給我最大的感觸就是切勿頭腦發熱,需要冷靜最小化處理問題。

這次SQL優化也總結了些經驗並做了相關優化如下:

1.添加索引和優化索引,特別小心索引隱式轉換

一個表裏面如果只是設置了主鍵,然後其它索引一律不建不管,簡單業務如只涉及到按照主鍵查詢的業務是沒問題,但是設計到其它字段的查詢,在數據量稍大又加上業務高峰期,這種導致表全局查詢的SQL肯定會積累大量慢SQL,最終導致CPU持續上升,如果有條件的話,測試最好做一些大數據量的壓力測試是可以測試出來的,另外,建立了索引,也要註意到索引失效這種情況。如:select *from order where phone=13772556391; 平時寫代碼粗心大意,不仔細檢查再加上壓力測試沒測試到位,在高並發數據量稍微大點的業務場景裏面搞不好就出問題。數據庫表phone字段用的字符串類型,但是這個SQL裏面沒有加上引號,所以像這種情況下,索引是無效的。

2.分頁查詢優化

select * from orderwhere oid=100 limit 100000,5000,這種普通limit M,N的翻頁寫法,在越往後翻頁的過程中速度越慢,原因mysql會讀取表中的前M+N條數據,M越大,性能就越差。像這種SQL如果只是平時查詢看看記錄,感覺不到異常,就算稍微慢點,也忍了,但是在我剛才說的業務場景裏面,也會導致慢SQL查詢積累。優化寫法:select t1.* from order t1,(select id from order oid=100 limit 100000,5000) t2 where t1.id=t2.id,這種效率會高很好

3.分表

雖然做了上面的優化,執行效率比之前高了很多臺階,但是單表達的壓力依然存在。訂單表當時沒有做分表,盡管目前的條件沒有用分片集群,但是為解決燃眉之需也需要做物理分表(隨著投放出去的搖搖車越來越多,當時每天訂單大約1萬到2萬)。

技術分享圖片

SQL優化是一個長期的過程,最好是結合具體業務場景來做優化效率會更好一些,後面我會繼續羅列在這個項目實戰中出現的一些關於SQL的坑和做的相關優化。

我的物聯網項目(七)前期線上事故