個人物聯網項目(七)前期線上事故

一 MQTT鏈接數報警

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

有天下午快下班的時候,忽然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服務器,都須要創建鏈接。因此問題頗有多是沒有釋放鏈接,因爲當初的代碼邏輯比較簡單,因此直接找到寫這個代碼的開發人員,一塊兒喵了眼代碼,果真如此,修改代碼後,從新發包一切正常,手機報警短信立馬停了。mysql

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%

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

晚上大概7點左右(晚點7點到9點是搖搖車的使用高峯,白天通常訂單數據較少,到了晚上數據成倍的增漲),收到反饋,說平臺系統打開很慢很慢,並且手機也不斷收到MQTT短信預警,說堆積了大量未消費的訂單,第一時間的反應迅速登陸雲數據庫管理平臺看到數據庫CPU這項指標嚴重標紅,並顯示使用率達到100%。當初數據庫用的是通用型4核8G的,訂單表數據將近40W,打開數據庫性能管理界面查看(若是沒有這種管理界面,也能夠經過命令show processlist來查看正在執行的SQL和explain來分析執行計劃查看慢SQL),發現積累了大量的慢SQL,有些SQL的平均執行時間超過將近1分鐘,很明顯作了數據庫的全局掃描,再加上這段時間的高峯時期,慢SQL堆積,致使CPU資源順序消耗完。typescript

當時也正是業務的使用的高峯期,再加上客戶投訴和上面不少人在盯着這個事情,因此我這邊要在短期內恢復數據庫的正常,當初有幾種選擇能夠迅速恢復,將數據庫切換到備數據庫(數據庫是高可用的)或者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的坑和作的相關優化。

相關文章
相關標籤/搜索