據(jù)分析與可視化系統(tǒng):SpringBoot+Vue+MySQL實(shí)戰(zhàn))
做數(shù)據(jù)分析的朋友應(yīng)該都有共識看趨勢曲線不難難的是當(dāng)數(shù)據(jù)變成高頻、短時、密集的流量請求時時間窗口內(nèi)的一點(diǎn)點(diǎn)抖動都可能直接影響業(yè)務(wù)判斷。最近我在整理一套短流量數(shù)據(jù)分析與可視化ABO信息管理系統(tǒng)技術(shù)棧是SpringBoot Vue MySQL。這套系統(tǒng)的核心價值在于——它不只是一張報表而是把短周期流量數(shù)據(jù)的采集、聚合、分析、可視化以及ABO業(yè)務(wù)對象的管理全部串在了一起最終以可視化看板的方式呈現(xiàn)給業(yè)務(wù)方。先解釋一下ABO這個代號。在很多實(shí)際工程里ABO不是一個通用標(biāo)準(zhǔn)詞而是項(xiàng)目組內(nèi)部定義的管理對象集合Automated Business Object自動化業(yè)務(wù)對象它把參與流量業(yè)務(wù)的主體——例如推廣活動、落地頁、渠道來源、目標(biāo)轉(zhuǎn)化事件——統(tǒng)一抽象成可配置、可追蹤的信息管理對象。你可以理解成一套給業(yè)務(wù)對象做標(biāo)記、建檔、配置和監(jiān)控的底座。這樣短流量分析做的就不只是看數(shù)據(jù)而是圍繞業(yè)務(wù)對象去看數(shù)據(jù)這也是系統(tǒng)區(qū)別于普通流量統(tǒng)計工具的關(guān)鍵點(diǎn)。這套系統(tǒng)適合誰來參考如果你正在做流量分析類的課程設(shè)計、企業(yè)內(nèi)部的數(shù)據(jù)可視化大屏項(xiàng)目或者想弄清楚SpringBoot后端如何和Vue前端配合、MySQL又如何支撐高頻寫入與聚合查詢那么這篇文章就是給你準(zhǔn)備的。文中我會重點(diǎn)講清楚項(xiàng)目拆分邏輯、核心表結(jié)構(gòu)、聚合接口的設(shè)計思路、前端ECharts看板的實(shí)現(xiàn)方式以及最后從源碼到直接運(yùn)行需要處理的那些容易翻車的細(xì)節(jié)。1. 短流量數(shù)據(jù)平臺的核心需求拆解ABO為什么需要一套專屬系統(tǒng)1.1 短流量到底短在哪定義數(shù)據(jù)窗口的粒度先說一個容易混淆的點(diǎn)。很多初學(xué)者一聽到數(shù)據(jù)分析第一反應(yīng)是拉一張大寬表跑一跑年度銷售額、月度環(huán)比。但短流量分析完全不是這個套路它關(guān)注的是秒級、分鐘級、小時級窗口內(nèi)的流量密度、集中度和波動特征。舉個例子一個電商秒殺活動上線后最開始3分鐘內(nèi)的用戶請求會形成一個尖峰如果系統(tǒng)按小時聚合這個尖峰會被平均掉看起來毫無波瀾但實(shí)際上后端服務(wù)可能已經(jīng)出現(xiàn)延遲反過來某條推廣鏈接的流量可能集中在晚上9點(diǎn)到10點(diǎn)半你按天聚合永遠(yuǎn)看不到這個規(guī)律。所以短不是指數(shù)據(jù)量小而是指分析窗口小、頻率高、實(shí)時性強(qiáng)。這套ABO系統(tǒng)的數(shù)據(jù)采集設(shè)計目標(biāo)就是要把單位時間窗口內(nèi)的PV、UV、請求數(shù)、轉(zhuǎn)化次數(shù)等指標(biāo)以分鐘為單位記錄并向上聚合。具體落到產(chǎn)品形態(tài)上我把它分為三層第一層是明細(xì)層存原始請求記錄第二層是分鐘聚合層把同一分鐘、同一活動、同一渠道的記錄壓縮成一行統(tǒng)計結(jié)果第三層是小時/天聚合層給看板的大趨勢圖提供數(shù)據(jù)。這三層數(shù)據(jù)逐級向上既保證了細(xì)節(jié)可查又保證了查詢速度。1.2 管理對象抽象把數(shù)據(jù)掛到業(yè)務(wù)實(shí)體上光有流量數(shù)據(jù)還不夠業(yè)務(wù)方想知道的是哪個活動帶來了流量哪個渠道效果好。這就是ABO管理對象存在的意義。在我的設(shè)計里ABO對象包含三類核心實(shí)體活動Campaign一次推廣、一場直播、一個落地頁活動渠道Channel來源分類比如抖音、朋友圈、直客、搜索引擎事件Event關(guān)鍵轉(zhuǎn)化動作比如注冊、加購、下單、支付這三類實(shí)體通過配置表關(guān)聯(lián)再和流量指標(biāo)表做關(guān)聯(lián)。每次采集到的流量數(shù)據(jù)都帶有活動ID、渠道ID和事件類型這樣可視化時就能按任意維度切片。比如業(yè)務(wù)方想對比抖音渠道進(jìn)入A活動的轉(zhuǎn)化情況前端只需要把兩個篩選條件一選后端接口自動完成過濾和聚合。這種對象化管理還有一個額外好處新增業(yè)務(wù)場景不需要改表結(jié)構(gòu)。假如團(tuán)隊(duì)多了一個直播間掛載小黃車的新渠道在渠道表插一條記錄、在活動表關(guān)聯(lián)一下前端下拉列表就能直接選到代碼層面完全不用動。1.3 系統(tǒng)模塊拆分與數(shù)據(jù)閉環(huán)整個系統(tǒng)我拆成了五個模塊數(shù)據(jù)接入模塊、指標(biāo)聚合模塊、ABO對象管理模塊、可視化查詢模塊、系統(tǒng)配置模塊。數(shù)據(jù)接入模塊負(fù)責(zé)接收前端上報或MQ推送的流量明細(xì)指標(biāo)聚合模塊負(fù)責(zé)將明細(xì)數(shù)據(jù)按時間窗口聚合ABO對象管理模塊負(fù)責(zé)維護(hù)活動和渠道等基礎(chǔ)對象可視化查詢模塊對外提供多維聚合接口系統(tǒng)配置模塊管理用戶權(quán)限和指標(biāo)閾值。數(shù)據(jù)閉環(huán)是這樣的客戶端上報流量明細(xì) → 接入層解析并落庫 → 聚合任務(wù)按分鐘/小時生成指標(biāo) → 前端看板請求查詢接口 → 接口從聚合表中讀取并返回 → 圖表渲染。這樣的好處是明細(xì)表和聚合表分離查詢性能有保證同時保留了鉆取到明細(xì)的能力。有一次業(yè)務(wù)方反饋某渠道數(shù)據(jù)異常偏大我臨時寫了一條SQL從明細(xì)表里按user_id查了前100條記錄很快就定位到是一個爬蟲腳本在反復(fù)刷接口這就是明細(xì)存儲的價值——聚合表只能讓你看見異常明細(xì)表才能讓你定位異常。2. 數(shù)據(jù)庫方案短流量指標(biāo)的建模與分析表設(shè)計2.1 核心表結(jié)構(gòu)與字段說明MySQL在這套系統(tǒng)里同時承擔(dān)明細(xì)存儲和聚合結(jié)果存儲。因?yàn)槎塘髁繑?shù)據(jù)的特點(diǎn)是寫入頻繁、聚合查詢多我建議明細(xì)表和聚合表分開建。這里給出核心表的設(shè)計流量明細(xì)表flow_log字段類型說明idbigint主鍵activity_idbigintABO活動IDchannel_idbigint渠道IDevent_typevarchar(32)事件類型request_timedatetime請求發(fā)生時間device_typevarchar(16)設(shè)備類型ipvarchar(64)IP地址user_idvarchar(64)用戶標(biāo)識匿名時可空extrajson擴(kuò)展字段分鐘級聚合表flow_stat_minute字段類型說明idbigint主鍵activity_idbigint活動IDchannel_idbigint渠道IDstat_datedate日期stat_hourvarchar(2)小時stat_minutevarchar(2)分鐘pvint訪問量uvint獨(dú)立訪客event_countint事件數(shù)update_timedatetime更新時間ABO活動表abo_campaign字段類型說明idbigint主鍵namevarchar(128)活動名稱start_timedatetime開始時間end_timedatetime結(jié)束時間statustinyint狀態(tài)1啟用0停用config_jsonjson擴(kuò)展配置加上渠道表、事件配置表整體5張表就能支撐整套系統(tǒng)。對一個小型可視化分析系統(tǒng)來說這個規(guī)模剛好既不會因?yàn)楸硖鄬?dǎo)致理解成本高又能把業(yè)務(wù)對象和指標(biāo)數(shù)據(jù)清晰地關(guān)聯(lián)起來。2.2 索引與分區(qū)分頁策略短流量明細(xì)表最大的問題就是數(shù)據(jù)膨脹。我實(shí)測過如果按每分鐘采集100條、一天24小時不停單表一天會有14萬條左右一個月就是400多萬條。這個量級在MySQL里完全能扛但前提是索引要建對。我最常用的三套索引組合是這樣的idx_request_time (request_time)用于按時間范圍查詢明細(xì)idx_activity_time (activity_id, request_time)用于按活動維度查詢idx_channel_time (channel_id, request_time)用于按渠道維度查詢同時聚合表要建聯(lián)合唯一索引uk_act_cha_time (activity_id, channel_id, stat_date, stat_hour, stat_minute)這個索引不僅用于去重還能讓INSERT ... ON DUPLICATE KEY UPDATE的寫入效率明顯提升。按月分表是更穩(wěn)妥的做法。我把flow_log設(shè)計成按月分表表名形如flow_log_202501在寫入和查詢時根據(jù)當(dāng)前月份拼接表名。這樣做的好處是單表體積可控、清理歷史數(shù)據(jù)可以直接DROP表缺點(diǎn)是跨月查詢要UNION。對于這套系統(tǒng)的使用場景來說絕大多數(shù)分析窗口都在一個月內(nèi)所以按月分表利大于弊。2.3 為什么聚合表不用視圖而是實(shí)表有朋友問過能不能直接對明細(xì)表做GROUP BY查詢何必多維護(hù)一張聚合表我之前的項(xiàng)目里試過。當(dāng)數(shù)據(jù)量到了幾十萬條以上前端每次切時間范圍都觸發(fā)一次大查詢MySQL的CPU瞬間飆高一個圖表接口耗時超過5秒體驗(yàn)基本沒法看。聚合表的價值就是把計算提前把結(jié)果存下來查詢時零計算毫秒級返回。還有一點(diǎn)聚合任務(wù)可以做成增量補(bǔ)償機(jī)制。即使某分鐘因?yàn)橄到y(tǒng)故障漏聚合了也可以通過重跑任務(wù)把缺失窗口的數(shù)據(jù)補(bǔ)上這是實(shí)時聚合方案很難做到的。我實(shí)際部署的時候遇到過服務(wù)器半夜重啟導(dǎo)致任務(wù)中斷的情況第二天一早發(fā)現(xiàn)凌晨1點(diǎn)到2點(diǎn)的聚合表全空了后來加了補(bǔ)償邏輯每次任務(wù)啟動時自動檢測最近2小時是否有缺失窗口有就補(bǔ)跑再沒出過類似問題。3. SpringBoot后端接口設(shè)計、聚合查詢與臟數(shù)據(jù)治理3.1 項(xiàng)目結(jié)構(gòu)與分層后端我采用的是經(jīng)典三層結(jié)構(gòu)Controller層負(fù)責(zé)參數(shù)校驗(yàn)和結(jié)果返回Service層負(fù)責(zé)業(yè)務(wù)邏輯和聚合計算Mapper層用MyBatis-Plus操作數(shù)據(jù)庫。之所以用MyBatis-Plus而不是手寫大量XML是因?yàn)檫@套系統(tǒng)里單表CRUD很多MyBatis-Plus的BaseMapper能直接省掉大半模板代碼復(fù)雜的聚合查詢和數(shù)據(jù)更新再單獨(dú)寫SQL。pom.xml里核心依賴是這些dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency版本選擇上我建議SpringBoot 2.7.x原因很簡單它對JDK 8的支持最穩(wěn)定MyBatis-Plus和各種工具類的兼容性不用折騰。如果你用SpringBoot 3.x要注意JDK必須17以上且javax包要替換成jakarta很多老代碼直接跑不起來。3.2 流量數(shù)據(jù)的聚合查詢實(shí)現(xiàn)聚合接口是整個系統(tǒng)的核心它要解決的查詢是給定活動ID和渠道ID的可選條件返回指定日期范圍內(nèi)按分鐘/小時/天聚合的PV、UV、事件數(shù)曲線。我的實(shí)現(xiàn)思路是前端傳一個granularity參數(shù)后端根據(jù)它決定按什么粒度聚合。SQL使用動態(tài)拼接的方式處理Override public ListMapString, Object queryTrend(QueryTrendDTO dto) { LambdaQueryWrapperFlowLog wrapper new LambdaQueryWrapper(); if (dto.getActivityId() ! null) { wrapper.eq(FlowLog::getActivityId, dto.getActivityId()); } if (dto.getChannelId() ! null) { wrapper.eq(FlowLog::getChannelId, dto.getChannelId()); } // 所有查詢必須帶上時間范圍避免全表掃描 wrapper.between(FlowLog::getRequestTime, dto.getStartTime(), dto.getEndTime()); wrapper.select(FlowLog::getRequestTime, FlowLog::getEventType); ListFlowLog logs flowLogMapper.selectList(wrapper); // 內(nèi)存中按聚合粒度統(tǒng)計 MapString, StatItem resultMap new LinkedHashMap(); SimpleDateFormat sdf decideFormatter(dto.getGranularity()); for (FlowLog log : logs) { String key sdf.format(log.getRequestTime()); StatItem item resultMap.computeIfAbsent(key, k - new StatItem()); item.setPv(item.getPv() 1); if (log.getUserId() ! null) { item.getUvSet().add(log.getUserId()); } } // 將uvSet.size()填入結(jié)果 return formatAsChartData(resultMap); }這段邏輯夠直觀但對生產(chǎn)環(huán)境來說不夠高效。更推薦的方式是直接把聚合邏輯下沉到SQL里讓MySQL去分組SELECT DATE_FORMAT(request_time, %Y-%m-%d %H:%i) AS time_point, COUNT(*) AS pv, COUNT(DISTINCT user_id) AS uv FROM flow_log_202501 WHERE activity_id ? AND request_time BETWEEN ? AND ? GROUP BY DATE_FORMAT(request_time, %Y-%m-%d %H:%i) ORDER BY time_point;這條SQL在數(shù)據(jù)量十萬級左右的表現(xiàn)都不錯核心原因是時間范圍過濾已經(jīng)通過索引把掃描范圍卡得很小。需要注意的是COUNT(DISTINCT user_id)在大數(shù)據(jù)量下會比較慢如果后續(xù)數(shù)據(jù)量繼續(xù)膨脹UV就應(yīng)該改成用精確去重或近似去重HyperLogLog來維護(hù)。3.3 定時聚合任務(wù)與補(bǔ)償機(jī)制為了不讓聚合結(jié)果完全依賴接口實(shí)時計算我加了一個Scheduled定時任務(wù)每分鐘跑一次增量聚合。實(shí)現(xiàn)上采用掃描 按窗口寫入的做法Component Slf4j public class FlowAggregationJob { Resource private FlowLogMapper flowLogMapper; Resource private FlowStatMinuteMapper flowStatMinuteMapper; Scheduled(cron 0 * * * * ?) public void intervalAggregation() { // 處理上一分鐘的流量記錄 LocalDateTime endTime LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES); LocalDateTime startTime endTime.minusMinutes(1); // 按 (activity_id, channel_id) 分組寫入聚合表 ListFlowStatMinute stats flowLogMapper.selectMinuteStat(startTime, endTime); for (FlowStatMinute stat : stats) { flowStatMinuteMapper.insertOrUpdate(stat); } log.info(聚合任務(wù)完成窗口: {} ~ {}, startTime, endTime); } }這個任務(wù)邏輯本身不復(fù)雜但有一個經(jīng)常被忽略的坑任務(wù)執(zhí)行時刻和日志到達(dá)時刻的延遲。數(shù)據(jù)上報往往不是準(zhǔn)時的實(shí)際生產(chǎn)下可能晚到幾分鐘所以定時任務(wù)不能只處理上一分鐘的窗口至少要保留一個30分鐘左右的可重跑窗口或者做成按最近一端時間拉取并冪等寫入的方式。冪等寫入通常用剛才提到的唯一索引配合INSERT ... ON DUPLICATE KEY UPDATE實(shí)現(xiàn)這樣重復(fù)跑任務(wù)不會產(chǎn)生重復(fù)數(shù)據(jù)。3.4 臟數(shù)據(jù)治理空IP、未知渠道、時間異常短流量數(shù)據(jù)最不缺的就是臟數(shù)據(jù)。我在這套系統(tǒng)里專門做了一層清洗邏輯在數(shù)據(jù)接入的時候就把明顯的臟數(shù)據(jù)攔截掉。具體做了三件事對空IP和空活動ID的記錄直接丟棄不做冗余存儲對request_time超出當(dāng)前時間前后5分鐘范圍的記錄打上abnormal標(biāo)記單獨(dú)存到異常表既不影響正常分析也便于復(fù)盤對未知渠道ID做映射補(bǔ)齊比如通過渠道表查不到名字的統(tǒng)一歸為other渠道清洗邏輯聽起來像小事情但對后續(xù)聚合結(jié)果的影響非常大。我見過不少項(xiàng)目圖表出來之后莫名其妙出現(xiàn)一個巨大的未分類排查半天發(fā)現(xiàn)是渠道字段有大小寫不一致或者帶空格而這些臟數(shù)據(jù)在清洗時只要做一次trim和lower就能避免。4. Vue前端可視化從一張大屏到交互式看板4.1 可視化選型為什么用ECharts Vue前端部分我選擇的是Vue 2.7 Element UI ECharts。這套組合雖然不是最新但勝在穩(wěn)定、生態(tài)成熟、資料多。ECharts在流量可視化場景下的表現(xiàn)非常成熟折線圖、柱狀圖、餅圖、熱力圖、散點(diǎn)圖全覆蓋而且支持按需引入打包體積可控。組件結(jié)構(gòu)上我建議按容器組件 圖表組件來拆分容器組件負(fù)責(zé)拉取數(shù)據(jù)、處理加載狀態(tài)和篩選條件圖表組件只接收數(shù)據(jù)源并負(fù)責(zé)渲染不關(guān)心數(shù)據(jù)從哪來。這樣圖表組件可以復(fù)用比如折線趨勢圖既用在總覽頁面也用在活動詳情頁。4.2 看板布局與核心圖表設(shè)計整個看板我分成三行第一行是核心指標(biāo)卡片總PV、總UV、當(dāng)前活動數(shù)、轉(zhuǎn)化率第二行是趨勢圖和渠道占比圖第三行是事件分布餅圖和流量Top10排行榜。這個布局是數(shù)據(jù)分析里很經(jīng)典的先總后分、先趨勢后構(gòu)成模式用戶一眼就能抓住核心。趨勢圖的核心代碼邏輯如下template div refchartRef stylewidth: 100%; height: 400px/div /template script import * as echarts from echarts; export default { props: { chartData: { type: Object, default: () ({ timePoints: [], uv: [], pv: [] }) } }, watch: { chartData: { handler() { this.renderChart(); }, deep: true } }, methods: { renderChart() { const chart echarts.init(this.$refs.chartRef); chart.setOption({ tooltip: { trigger: axis }, legend: { data: [PV, UV] }, xAxis: { type: category, data: this.chartData.timePoints }, yAxis: { type: value }, series: [ { name: PV, type: line, smooth: true, data: this.chartData.pv }, { name: UV, type: line, smooth: true, data: this.chartData.uv } ] }); } } }; /script有一點(diǎn)要提醒前端調(diào)用ECharts的init之后組件銷毀時必須調(diào)用chart.dispose()否則快速切換頁面會不斷累積瀏覽器內(nèi)存最終導(dǎo)致頁面卡死。很多可視化項(xiàng)目用著用著變卡的根源就在這里。4.3 前后端聯(lián)調(diào)與數(shù)據(jù)格式約定頁面和接口之間的數(shù)據(jù)格式我采用了一個約定俗成的結(jié)構(gòu){ code: 200, message: success, data: { timePoints: [2025-01-10 09:00, 2025-01-10 09:01], pvSeries: [1024, 980, 1203], uvSeries: [520, 488, 600] } }后端封裝統(tǒng)一返回對象前端在axios的響應(yīng)攔截器里做統(tǒng)一處理業(yè)務(wù)代碼只需要關(guān)注data字段。格式約定統(tǒng)一了前后端聯(lián)調(diào)就不容易出現(xiàn)參數(shù)對不上、字段名不匹配的扯皮??缬騿栴}也是聯(lián)調(diào)時最常見的坑。我開發(fā)環(huán)境的Vue配置了代理// vue.config.js module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } };這樣前端請求/api/trend時實(shí)際是代理到后端的http://localhost:8080/trend繞開了跨域限制。生產(chǎn)環(huán)境則把前端打包后的靜態(tài)文件放到SpringBoot的static目錄下或者用Nginx反向代理同源部署完全不需要處理跨域。5. MySQL連接與部署運(yùn)行從源碼到直接運(yùn)行的關(guān)鍵環(huán)節(jié)5.1 初始化數(shù)據(jù)庫與調(diào)整MySQL參數(shù)拿到源碼后第一件事不是啟動項(xiàng)目而是先把數(shù)據(jù)庫建好。系統(tǒng)自帶的init.sql會創(chuàng)建數(shù)據(jù)庫和所有表并插入一批測試活動數(shù)據(jù)。MySQL這邊有兩個參數(shù)我強(qiáng)烈建議先調(diào)一下不然后續(xù)寫入高頻的時候會非常痛苦# my.cnf max_connections 200 innodb_buffer_pool_size 1Gmax_connections默認(rèn)是151當(dāng)數(shù)據(jù)接入任務(wù)和后臺定時任務(wù)并發(fā)執(zhí)行時很容易觸頂innodb_buffer_pool_size建議設(shè)置為物理內(nèi)存的50%-70%這個值直接決定MySQL在頻繁寫入和查詢時的緩存效率。另外連接串里的characterEncodingutf8mb4要確認(rèn)是對的否則中文活動名在控制臺查詢時會出現(xiàn)亂碼spring.datasource.urljdbc:mysql://localhost:3306/abo_flow?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.passwordyourpassword5.2 SpringBoot與Vue的編譯打包步驟先說后端。用Maven打包時最常踩的坑是本地JDK版本和項(xiàng)目編譯版本不一致導(dǎo)致UnsupportedClassVersionError。建議統(tǒng)一用JDK 8并且在pom.xml里顯式指定properties java.version1.8/java.version maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties打包命令就一條mvn clean package -DskipTests生成的jar包在target/目錄下運(yùn)行java -jar abo-flow-system-1.0.0.jar前端Vue部分安裝依賴時如果網(wǎng)絡(luò)不好容易卡在node-sass上建議使用npm鏡像源如果項(xiàng)目用的是dart-sass則沒這個問題。構(gòu)建命令npm install --registryhttps://registry.npmmirror.com npm run build構(gòu)建產(chǎn)物在dist/目錄。最省事的運(yùn)行方式是把dist目錄下的靜態(tài)資源復(fù)制到SpringBoot的src/main/resources/static/再重新打包這樣整個系統(tǒng)就是一個jar包任何機(jī)器裝了JDK8和MySQL就能跑。5.3 常見啟動失敗與排查清單我把自己實(shí)際運(yùn)行過程中遇到過的幾個典型問題整理成了一張排查清單算是給后來人排雷現(xiàn)象根因處理方式啟動報Access denied for user rootlocalhostMySQL賬號密碼或權(quán)限不正確檢查application.yml連接串用命令行登錄測試啟動報Communications link failureMySQL未啟動或端口不是3306確認(rèn)mysql -uroot -p能登錄查看端口占用前端啟動后白屏按F12看到404路由為history模式且未配置后端支持改用hash模式或配置后端頁面跳轉(zhuǎn)與靜態(tài)資源映射頁面能開但圖表數(shù)據(jù)為空后端接口數(shù)據(jù)源連錯庫或聚合表沒數(shù)據(jù)確認(rèn)庫名、賬號執(zhí)行一條統(tǒng)計SQL驗(yàn)證jar包啟動后端口被占用8080被其他進(jìn)程占用lsof -i:8080或netstat -ano查看進(jìn)程換端口5.4 關(guān)于可直接運(yùn)行的誠實(shí)提醒市場上很多源碼標(biāo)題寫著可直接運(yùn)行但真按步驟走一遍還是會遇到零零散散的小問題。這套項(xiàng)目大多數(shù)情況下可以把問題壓縮到配置數(shù)據(jù)庫連接串這一個環(huán)節(jié)已經(jīng)很理想了。我的建議是拿到代碼后先做三件事全局搜索localhost和3306確認(rèn)所有連接配置都已經(jīng)指向本機(jī)環(huán)境確認(rèn)MySQL版本在5.7及以上并執(zhí)行一遍init.sql后端啟動成功后先在瀏覽器訪問http://localhost:8080/api/ping能返回ok再啟動前端這三步做完整個鏈路基本就通了。6. 短流量可視化看板的進(jìn)階優(yōu)化與個人經(jīng)驗(yàn)補(bǔ)充6.1 從看板走向告警閾值規(guī)則的接入當(dāng)短流量數(shù)據(jù)的波動直接影響到業(yè)務(wù)時只做可視化是不夠的。我在迭代這套系統(tǒng)的過程中加了一層簡單的告警規(guī)則對每個ABO活動可以配置PV閾值和UV閾值聚合任務(wù)每跑完一次就對比一次超過閾值就觸發(fā)告警記錄同時在看板的指標(biāo)卡片上高亮。這個功能本質(zhì)上是對聚合數(shù)據(jù)的二次消費(fèi)成本很低但價值很明顯業(yè)務(wù)方能第一時間感知流量異常不用一直盯著圖表看。實(shí)現(xiàn)上就是兩張表的事一張alert_rule存規(guī)則配置一張alert_record存觸發(fā)記錄。聚合任務(wù)跑完后順手調(diào)一個checkAlert()方法對比pv rule.max_pv就插入一條告警。再配合后端定時推送郵件或釘釘機(jī)器人告警鏈路就完整了。6.2 數(shù)據(jù)接入模擬沒有真實(shí)流量時怎么演示很多同學(xué)拿到源碼后第一反應(yīng)是沒有真實(shí)流量數(shù)據(jù)看板是不是空的。這套系統(tǒng)里我內(nèi)置了一個模擬數(shù)據(jù)模塊通過一個開關(guān)控制打開后SpringBoot的定時任務(wù)每秒生成若干條隨機(jī)流量數(shù)據(jù)活動ID、渠道ID、事件類型都是按配置表的真實(shí)記錄隨機(jī)分配的。這樣看板隨時都有數(shù)據(jù)在動演示效果非常好。模擬數(shù)據(jù)的生成要注意一點(diǎn)不要生成純均勻的隨機(jī)數(shù)否則曲線太平滑看著假。我通常會疊加一個時間熱度系數(shù)比如上午10點(diǎn)和晚上8點(diǎn)的時段概率高一些凌晨4點(diǎn)概率低一些這樣曲線看起來才像真實(shí)的流量波動。6.3 個人實(shí)操中的幾個體會踩過幾次坑之后我最大的體會是短流量數(shù)據(jù)分析系統(tǒng)的難點(diǎn)從來不在會不會寫接口或會不會畫圖表而在于數(shù)據(jù)窗口的設(shè)計和臟數(shù)據(jù)的治理。接口寫得再漂亮如果聚合窗口沒定義好出來的曲線就會失真圖表畫得再炫如果底層數(shù)據(jù)有臟數(shù)據(jù)看板就成了精致的垃圾。所以建議在做這類項(xiàng)目時花40%的時間在數(shù)據(jù)接入和清洗上花30%的時間在聚合任務(wù)上剩下30%再給前端和部署這個比例是非常值得參考的。另外還有一個小技巧前端圖表組件配一個刷新間隔的下拉選項(xiàng)默認(rèn)關(guān)閉但可以選擇每5分鐘或每1分鐘自動刷新一次數(shù)據(jù)。這個功能在投屏場景下非常實(shí)用操作起來就是在前端容器組件里加一個setInterval定時重新請求接口幾乎不增加開發(fā)量但演示效果立刻提升一個檔次。短流量數(shù)據(jù)分析與可視化這條路說到底就是高頻數(shù)據(jù) 合理聚合 直觀呈現(xiàn)三件事。把這套鏈路跑通了無論你后續(xù)接的是短視頻流量、接口調(diào)用流量還是業(yè)務(wù)埋點(diǎn)處理思路都是相通的。我目前這套系統(tǒng)還在持續(xù)迭代下一步打算把異常檢測的規(guī)則從固定閾值升級成動態(tài)基線讓告警更聰明一些。如果你正在折騰類似的項(xiàng)目可以從數(shù)據(jù)庫設(shè)計和聚合任務(wù)這兩個環(huán)節(jié)入手把基礎(chǔ)打扎實(shí)后面的可視化工作自然會順很多。