新聞中心
?作者 | 董哲

需求背景
數(shù)據(jù)探查上線之前,數(shù)據(jù)驗(yàn)證都是通過寫 SQL 方式進(jìn)行查詢的,從編寫 SQL,到解析運(yùn)行出結(jié)果,不僅時(shí)間長,還會(huì)反復(fù)消耗計(jì)算資源,探查上線后,只需要一次探查,就可以得到整張表的探查報(bào)告,但后續(xù)我們還發(fā)現(xiàn)了一些問題,主要有三點(diǎn):
- 無法看到探查的數(shù)據(jù)明細(xì)以及關(guān)聯(lián)的行詳情,無法對(duì)數(shù)據(jù)進(jìn)行預(yù)處理操作。
- 探查還是需要資源調(diào)度,等待時(shí)長平均分鐘級(jí)。
- 與質(zhì)量監(jiān)控沒有打通,探查數(shù)據(jù)的后續(xù)走向不明確。
針對(duì)這些問題,我們進(jìn)一步開發(fā)了動(dòng)態(tài)探查需求,解決的問題如下:
- 基于大數(shù)據(jù)預(yù)覽的探查,支持對(duì)數(shù)據(jù)進(jìn)行函數(shù)級(jí)別的預(yù)處理。
- 探查結(jié)果秒級(jí)更新,實(shí)時(shí)響應(yīng)。
- 與數(shù)據(jù)監(jiān)控打通,探索 SQL 的生成模式。
本文主要介紹動(dòng)態(tài)探查的應(yīng)用場景和相關(guān)的技術(shù)實(shí)現(xiàn)。
應(yīng)用場景
探查主要應(yīng)用在元數(shù)據(jù)管理,數(shù)據(jù)研發(fā),數(shù)倉的開發(fā)以及數(shù)據(jù)治理,可為對(duì)數(shù)據(jù)質(zhì)量有需求的場景提供數(shù)據(jù)質(zhì)量的發(fā)現(xiàn)和識(shí)別能力。目標(biāo)用戶除了研發(fā)同學(xué),也包含不是以 SQL 研發(fā)為主的群體,比如算法建模和數(shù)據(jù)挖掘等領(lǐng)域。
探查可以有效的打通三個(gè)閉環(huán):
- 元數(shù)據(jù)管理 -> 探查 -> 數(shù)據(jù)預(yù)覽探查(庫表的質(zhì)量報(bào)告)
- 數(shù)據(jù)監(jiān)控 <-> 數(shù)據(jù)探查
- 動(dòng)態(tài)探查 -> SQL -> 數(shù)據(jù)開發(fā) -> 調(diào)試 -> 探查報(bào)告(質(zhì)量分析)
名詞解釋
- 全量探查:基于庫表的全量探查,后端引擎執(zhí)行,展示探查后列的統(tǒng)計(jì)分布結(jié)果。
- 動(dòng)態(tài)探查:基于抽樣的部分?jǐn)?shù)據(jù)探查,展示字段明細(xì),可以使用操作對(duì)數(shù)據(jù)進(jìn)行預(yù)處理,并實(shí)時(shí)動(dòng)態(tài)的展示統(tǒng)計(jì)分布結(jié)果。數(shù)據(jù)獲取后的過程都由前端執(zhí)行。
兩者的對(duì)比示意圖
技術(shù)實(shí)現(xiàn)
除了數(shù)據(jù)的抽樣部分在后端做,其他的都是前端實(shí)現(xiàn)的。包括大數(shù)據(jù)展示,探查計(jì)算,卡片聯(lián)動(dòng),操作棧交互,以及未來要做的函數(shù)編輯器以及 SQL 生成。
技術(shù)架構(gòu)
1)抽樣能力:對(duì)數(shù)據(jù)進(jìn)行基于質(zhì)量分布特征的抽取。
目前做的是隨機(jī)抽樣,后續(xù)嘗試基于特征來抽樣。
2)數(shù)據(jù)展現(xiàn):大容量的數(shù)據(jù)載體,支持對(duì)數(shù)據(jù)處理的實(shí)時(shí)展現(xiàn)。
前端目前是基于虛擬滾動(dòng) Table 做的,后續(xù)打算遷移到 canvas table 上。
3)前端探查:實(shí)時(shí)探查,可視化展現(xiàn)數(shù)據(jù)分布,突出質(zhì)量指標(biāo)。
4)數(shù)據(jù)處理能力:函數(shù)處理能力(GroupBy..)
5)操作棧:需要對(duì)數(shù)據(jù)操作進(jìn)行管理和回溯
基于 immutable 和操作流實(shí)現(xiàn)操作棧。
6)編輯器:提供完整函數(shù)的功能,需要:詞法解析,智能提醒,語法高亮。
基于編輯器實(shí)現(xiàn)函數(shù)的功能,antlr4 實(shí)現(xiàn)詞法解析,配合 monaco editor 實(shí)現(xiàn)一些智能提醒和語法高亮。
7)生成 SQL:將可視化的交互式操作轉(zhuǎn)換成可執(zhí)行的 SQL。
目前 sql generator 有以下幾種方式:
- 基于鏈?zhǔn)秸{(diào)用生成
- 基于標(biāo)簽?zāi)0迳?/li>
- 基于 AST(抽象語法樹)去做
關(guān)鍵技術(shù)及實(shí)現(xiàn)
大數(shù)據(jù)渲染
由于動(dòng)態(tài)探查場景下前端需要支持最大 5000 條數(shù)據(jù)的展示和交互,所以在渲染這塊存在比較大的壓力,主要集中在探查卡片和數(shù)據(jù)預(yù)覽兩個(gè)部分。
探查卡片包含了特定列的部分關(guān)鍵信息匯總,比如 0 值、Null 值、枚舉值等,如下圖紅框部分:
探查卡片部分由于存在較多定制化內(nèi)容,所以采用了虛擬列表方案進(jìn)行渲染,支持收起狀態(tài)和展開狀態(tài):
數(shù)據(jù)預(yù)覽部分展示的是探查的全部數(shù)據(jù)集合,可以快速查看原始數(shù)據(jù)的詳細(xì)內(nèi)容,由于內(nèi)容同質(zhì)化比較高,所以數(shù)據(jù)預(yù)覽采用的是基于團(tuán)隊(duì)內(nèi)部維護(hù)的 canvas 版本 Table 方案進(jìn)行渲染,如下圖紅框部分:
卡片聯(lián)動(dòng)
由于卡片和數(shù)據(jù)預(yù)覽列的寬度差異較大,并且上下兩部分滑動(dòng)是獨(dú)立的,造成在選擇查看某個(gè)具體列的時(shí)候,上下對(duì)齊位置會(huì)比較麻煩,為了解決這個(gè)問題,這塊增加了自動(dòng)定位功能,演示效果如下:
這部分需要解決的問題有兩個(gè):卡片中間點(diǎn)坐標(biāo)計(jì)算和自動(dòng)定位邏輯。
中間點(diǎn)坐標(biāo)計(jì)算邏輯如下:
// 計(jì)算卡片中點(diǎn)坐標(biāo) index是卡片序號(hào),adsorbSider表示是否吸邊
getCardCenter(index: number, adsorbSider?: boolean) {
...
// 獲取卡片信息
const cardBox: IBaseBox = this.cardList[index];
// 獲取列信息
const colBox: IBaseBox = this.colList[index];
const clientWidth = getClientWidth();
if(adsorbSider) {
// 吸邊處理
if(cardBox.offset < this.cardScroll) {
return cardBox.offset;
}
if(cardBox.offset + cardBox.width - this.cardScroll > clientWidth) {
return cardBox.offset + cardBox.width - clientWidth;
}
return this.cardScroll;
}
return getTargetPosition(colBox, this.tableScroll, cardBox);
}
// 獲取滾動(dòng)目標(biāo)位置
// originBox: 滾動(dòng)起始對(duì)象
// originScroll: 滾動(dòng)起始左側(cè)scroll
// targetBox: 滾動(dòng)結(jié)束對(duì)象
const getTargetPosition = (originBox: IBaseBox, originScroll: number, targetBox: IBaseBox) => {
const clientWidth = getClientWidth();
if(!originBox || !targetBox) return 0;
let offsetLeftSider = Math.max(originBox?.offset - originScroll, 0);
if(offsetLeftSider + targetBox.width >= clientWidth) {
if(targetBox.offset + targetBox.width > clientWidth) {
// 此處容易出現(xiàn)吸邊
return targetBox.offset + targetBox.width - clientWidth;
} else {
return 0;
}
}
const scroll = targetBox?.offset - offsetLeftSider + (targetBox.width - originBox.width) / 2;
return Math.max(
Math.min(targetBox.offset, scroll),
0
);
}
獲取到中點(diǎn)坐標(biāo)后,自動(dòng)定位需要符合如下規(guī)則:
- 選中卡片后,表格要自動(dòng)滾動(dòng)定位到下方居中對(duì)齊,無法滿足對(duì)齊標(biāo)準(zhǔn)的,盡量靠近選中卡片位置。
- 選中表格列后,卡片要自動(dòng)滾動(dòng)定位到上方居中對(duì)齊,無法滿足對(duì)齊標(biāo)準(zhǔn)的,盡量靠近選中表格位置。
- 搜索選中列后,卡片和表格要自動(dòng)滿足上面兩個(gè)規(guī)則,并滾動(dòng)到可視區(qū)域內(nèi)。
規(guī)則中有幾種邊界情況,參考下圖:
居中對(duì)齊是對(duì)于卡片和列寬在 scroll 距離允許情況下的理想對(duì)齊方式,貼邊對(duì)齊是針對(duì)卡片在起始和結(jié)束位置 scroll 不足以滿足居中對(duì)齊要求時(shí)候的對(duì)齊方式,除此之外還有一種是卡片的寬度遠(yuǎn)大于列寬,并且不是起始或者結(jié)束位置的時(shí)候所采取的對(duì)齊方式,如下如卡片 B 因?yàn)闊o法滾動(dòng),卡片 A 的寬度又占據(jù)了底部第二列的一部分,所以此時(shí)卡片 B 只能高亮和底部的列進(jìn)行對(duì)齊。
操作棧
動(dòng)態(tài)探查支持了對(duì)于探查結(jié)果的基礎(chǔ)分析能力,比如列刪除、過濾、排序等,如下圖紅框部分:
用戶對(duì)于探查結(jié)果的每一次操作都會(huì)被記作一次操作,多次操作串聯(lián)起來形成操作棧,可以自由的修改或者刪減操作棧里的操作,并實(shí)時(shí)查看最新結(jié)果,以過濾操作演示效果如下:
操作棧部分需要處理的問題主要有以下幾點(diǎn):
1)如何管理多種操作進(jìn)行串行計(jì)算
這里把所有操作都抽象成了Input + Logic = Ouput的結(jié)構(gòu),Input 是輸入?yún)?shù),此處可以是指某一列的數(shù)據(jù)、上一步操作的結(jié)果或者其他計(jì)算值,Logic 是操作的具體邏輯,負(fù)責(zé)根據(jù) Input 轉(zhuǎn)換生成 Output,Output 可以作為最終結(jié)果進(jìn)行渲染,也可以再次進(jìn)入下一環(huán)節(jié)參與計(jì)算,拿列刪除操作舉個(gè)栗子,下面是大體代碼實(shí)現(xiàn):
class ColDelOpt {
run = (params: IOptEngineMetaInfo) => {
// 操作Input部分
const {
columns = [],
dataSourceMap = {}
} = params;
const {
fields = []
} = this.params;
// 操作Logic部分
const nextColumns = columns.filter((item) => !fields.includes(item.name));
// 操作的Output
return {
columns: nextColumns,
dataSourceMap
}
}
}可以看到 ColDelOpt 內(nèi)部有一個(gè) run 方法,該方法支持傳入一個(gè)包含了列信息 columns 和數(shù)據(jù)集 dataSourceMap 的 params 對(duì)象,此處 params 即被抽象的外部輸入?yún)?shù) Input,run 方法內(nèi)部的邏輯部分即被抽象的 Logic 部分,最后方法返回值包含了最新的 columns 和 dataSourceMap,即為 Output 部分?;谶@種結(jié)構(gòu),用戶所有的操作都可以被初始化成不同的 Opt 實(shí)例,由操作引擎統(tǒng)一調(diào)用實(shí)例的 run 方法,并傳入所需的參數(shù),最終得到計(jì)算結(jié)果。
2)某個(gè)操作被修改后如何進(jìn)行二次計(jì)算
操作棧的計(jì)算是由計(jì)算引擎來完成的,引擎負(fù)責(zé)根據(jù)外部事件,來自動(dòng)執(zhí)行現(xiàn)有操作的數(shù)據(jù)處理工作,引擎執(zhí)行流程和大體代碼如下:
// 操作引擎
class OptEngine {
// 操作列表
private optList: IOptEngineItem[] = [];
// 原始數(shù)據(jù)
private metaData: IOptEngineMetaInfo = {
columns: [],
dataSourceMap: {},
};
// 執(zhí)行算子
optRun = () => {
let {
columns = [],
dataSourceMap = {}
} = this.metaData;
if(!this.optList.length) return {
columns,
dataSourceMap
};
for(let index = 0; index < this.optList.length; index++) {
// 讀取操作算子
const optItem = this.optList[index];
let startTime = performance.now();
try {
// 執(zhí)行算子計(jì)算
const result = optItem.run({
columns,
dataSourceMap
});
// 更新算子結(jié)果
columns = result.columns || [];
dataSourceMap = result.dataSourceMap || {};
} catch(e) {
// 報(bào)錯(cuò)后直接直接返回
return {
columns,
dataSourceMap,
// 裝填報(bào)錯(cuò)信息
errorInfo: {
key: optItem.key || '',
message: e.message
}
}
}
}
return {
columns,
dataSourceMap,
}
}
autoRun = (
metaInfo: IOptEngineMetaInfo,
optList: IOptItem[],
callback: (params: IAutoRunResult) => void
) => {
// 裝填數(shù)據(jù)
this.setupMetaData(metaInfo);
// 裝填操作棧
this.setupOptList(optList.map((item) => {
// 行過濾
if(item.type === OPT_TYPE.FILTER) {
return new FilterOpt({
key: item.key,
params: item.params
})
}
// 其余類型操作
...
// 默認(rèn)原值返回
return new IdentityOpt({
key: item.key,
})
}));
// 執(zhí)行操作計(jì)算
const result = this.optRun();
// 返回?cái)?shù)據(jù)
return {
// 計(jì)算列
columns: result.columns,
// 執(zhí)行結(jié)果
dataSource: Object.entries(result.dataSourceMap).map(([key, value]) => ({
field: key,
value
})),
// 操作棧執(zhí)行異常信息
errorInfo: result.errorInfo
};
}
}
應(yīng)用實(shí)踐
以一個(gè)小例子來演示下動(dòng)態(tài)探查的使用。前端開發(fā)過程中,有一個(gè)真實(shí)的場景,我們?yōu)榱伺挪橐粋€(gè)豎屏顯示器的 bug(1080*1920),想找到關(guān)聯(lián)的用戶,看其分布情況,就可以很方便的用動(dòng)態(tài)探查去尋找。
后續(xù)計(jì)劃
關(guān)注動(dòng)態(tài)探查的操作豐富性以及之后的數(shù)據(jù)走向,比如離線數(shù)據(jù)導(dǎo)出,和生成 SQL 等,技術(shù)方向上主要放在以下幾個(gè)方面:
更多的探查類型和圖表支持
動(dòng)態(tài)探查目前支持空值,枚舉值,零值,數(shù)據(jù)統(tǒng)計(jì)等基礎(chǔ)的探查功能,未來會(huì)計(jì)劃支持包括 map,json,time,sql 語句等類型的識(shí)別和探查。同時(shí)提供更豐富的圖表支持。
操作棧的編輯器體驗(yàn)
動(dòng)態(tài)探查目前還是以類 Excel 的操作為主,未來主要提供編輯器級(jí)別的操作體驗(yàn),可以提供 HSQL 支持的大部分函數(shù),包括支持多表 join 功能。
操作流程的 SQL 生成
動(dòng)態(tài)探查目前的 SQL 能力還未建設(shè)完成,會(huì)在未來結(jié)合編輯器級(jí)別的操作,并支持多表,配合詞法解析功能,提供更精準(zhǔn)的生成 SQL 能力。
當(dāng)前標(biāo)題:數(shù)據(jù)質(zhì)量動(dòng)態(tài)探查及相關(guān)前端實(shí)現(xiàn)
文章源于:http://m.fisionsoft.com.cn/article/djhsogh.html


咨詢
建站咨詢
