數(shù)據(jù)開發(fā)工程師工資(數(shù)據(jù)開發(fā)工程師招聘)
在前面的文章【如何選擇數(shù)據(jù)應(yīng)用開發(fā)語言和環(huán)境】中我們建議使用SQL來作為主要數(shù)據(jù)開發(fā)語言,并且,通常我們需要對(duì)標(biāo)準(zhǔn)的SQL進(jìn)行增強(qiáng),以便可以更好的支持復(fù)雜的數(shù)據(jù)開發(fā)。一些典型的需要新增的特性可以是變量、控制語句、模板等。
增強(qiáng)SQL固然是可以解決我們的數(shù)據(jù)開發(fā)問題,但是它也會(huì)給我們帶來一些其他的不便。第一個(gè)煩惱可能就是,標(biāo)準(zhǔn)的SQL可以在很多數(shù)據(jù)工具中運(yùn)行,比如Superset的SQL查詢器、Hive的查詢控制臺(tái)等,而使用增強(qiáng)語法的SQL編寫的代碼則不行。由于我們將標(biāo)準(zhǔn)的SQL增強(qiáng)了,而SQL周邊生態(tài)工具卻無法感知這樣的增強(qiáng),這時(shí)各種不便就隨之而來了。
支持?jǐn)?shù)據(jù)開發(fā)過程
如何解決這個(gè)問題呢?想要在周邊工具中進(jìn)行SQL擴(kuò)展不是一件簡單的事情,可能需要花費(fèi)大量的精力和時(shí)間。我們只能另尋他法。
從軟件開發(fā)的視角來看這個(gè)問題,可以發(fā)現(xiàn),我們現(xiàn)在有了編程語言,也有了編程語言的執(zhí)行環(huán)境,基本的開發(fā)流程確實(shí)是打通了,但是還缺少的是對(duì)開發(fā)過程的支持。一般而言,開發(fā)過程支持完善與否將很大程度上決定團(tuán)隊(duì)開發(fā)效率的高低。下面我們一起來看看如何完善對(duì)于開發(fā)過程的支持。
主要的開發(fā)過程一般包括代碼編輯、調(diào)試、測(cè)試三個(gè)步驟。下面我們來看看如何支持這些數(shù)據(jù)開發(fā)過程。
支持代碼編輯
代碼編輯在當(dāng)前還不會(huì)成為一個(gè)問題,因?yàn)椋?/span>
- 我們只是在標(biāo)準(zhǔn)SQL語法的基礎(chǔ)上進(jìn)行了增強(qiáng),現(xiàn)有的編輯器的大部分現(xiàn)有功能還是可以照常使用的
- 大部分的語法增強(qiáng)是通過SQL語法的注釋功能來實(shí)現(xiàn)的,可以兼容標(biāo)準(zhǔn)SQL語法
- 大部分編輯器其實(shí)只是提供SQL語法高亮和格式化的功能,新增的語法不會(huì)產(chǎn)生很大的影響
支持代碼調(diào)試
命令行調(diào)試器
現(xiàn)在我們來看調(diào)試過程。事實(shí)上,使用周邊的SQL執(zhí)行工具來快速驗(yàn)證SQL這個(gè)過程本身就是代碼調(diào)試的過程。
有了增強(qiáng)SQL的語法,我們要如何做呢?回顧增強(qiáng)SQL的語法,我們?cè)谄渲兄С至硕鄠€(gè)步驟,每個(gè)步驟可以是執(zhí)行SQL,定義變量或者調(diào)用外部函數(shù)。如果可以一個(gè)步驟一個(gè)步驟運(yùn)行,并且可以在每個(gè)步驟之后查看當(dāng)前的變量或SQL執(zhí)行結(jié)果,那將是一件不錯(cuò)的事。這其實(shí)也就是一般的程序調(diào)試過程。
事實(shí)上,有了增強(qiáng)SQL的執(zhí)行器(即前文提到的驅(qū)動(dòng)器),要實(shí)現(xiàn)一個(gè)具備基本功能的增強(qiáng)SQL調(diào)試器并不困難。按照上面的描述,我們只需要在某一個(gè)步驟執(zhí)行完成之后,先暫停執(zhí)行,并提供接口查詢當(dāng)前上下文的數(shù)據(jù)即可。在程序暫停時(shí),一般還可以允許運(yùn)行一些代碼,這也不難,提供接口執(zhí)行SQL即可。這就是一個(gè)命令行的程序調(diào)試器雛形。
對(duì)應(yīng)到一般在IDE里面進(jìn)行調(diào)試的交互流程上,打斷點(diǎn)的過程,就是指定需要在哪一個(gè)步驟暫停,至于查看斷點(diǎn)時(shí)的狀態(tài)和在斷點(diǎn)時(shí)執(zhí)行代碼就跟上面的過程完全一致了。
根據(jù)前面的分析,我們可以設(shè)計(jì)一個(gè)命令行調(diào)試器類Debugger,它可以具有這些接口:
class debugger:
# 查詢執(zhí)行狀態(tài)
def is_started(...):
def is_inprogress(...):
def is_finished(...):
# 查詢執(zhí)行步驟信息
def step(...):
def current_step(...):
def next_step(...):
def last_step(...):
def left_step_count(...):
def print_steps(...):
# 查看或設(shè)置執(zhí)行過程中的變量
def vars(...):
def set_vars(...):
def templates(...):
def tempviews(...):
def showdf(...):
# 執(zhí)行某一步驟,實(shí)現(xiàn)暫停、繼續(xù)等流程控制功能
def step_on(...):
def step_to(...):
def run(...):
def run_to(...):
def restart(...):
# 在斷點(diǎn)過程中執(zhí)行sql
def sql(...):
上面這些接口借助增強(qiáng)SQL的執(zhí)行器不難實(shí)現(xiàn)。有了Debugger類,一個(gè)典型的調(diào)試過程就變成:
- 在任意SQL編輯器中編輯代碼
- 打開IPython命令行
- 創(chuàng)建Debugger對(duì)象: d = create_debugger(sql_file=…, …)
- 打印所有步驟:d.print_steps()
- 執(zhí)行到某一步:d.run_to(3)
- 在SQL編輯器中修改代碼
- 重起調(diào)試:d.restart()
- …
打印代碼執(zhí)行報(bào)告
為了輔助數(shù)據(jù)開發(fā)人員更清楚的理解增強(qiáng)SQL的執(zhí)行過程,我們最好能打印每一步驟的執(zhí)行情況,比如實(shí)際執(zhí)行的SQL、執(zhí)行開始時(shí)間、結(jié)束時(shí)間、當(dāng)前步驟在整個(gè)執(zhí)行過程中耗時(shí)百分比等信息。
一個(gè)簡單的報(bào)告可以設(shè)計(jì)如下:
===================== REPORT FOR step-1 ==================
config: StepConfig(target=..., condition=None, line_no=1)
sql: select 1 as a
status: SUCCEEDED
start time: 2021-04-10 10:05:30, end time: 2021-04-10 10:05:33, execution time: 2.251653s - 8.14%
messages:
===================== REPORT FOR step-2 ==================
config: StepConfig(target=log.a, condition=None, line_no=4)
sql: select '1' as a
status: SUCCEEDED
start time: 2021-04-10 10:05:33, end time: 2021-04-10 10:05:33, execution time: 0.069165s - 0.25%
messages:
a='1'
...
為此,我們可以定義一個(gè)執(zhí)行報(bào)告搜集器(ReportCollector),每當(dāng)一個(gè)步驟開始或結(jié)束執(zhí)行時(shí),SQL執(zhí)行器應(yīng)當(dāng)通知報(bào)告搜集器搜集該步驟的執(zhí)行信息。在整個(gè)流程執(zhí)行完成之后,SQL執(zhí)行器可以調(diào)用報(bào)告搜集器打印整個(gè)過程中搜集到的執(zhí)行報(bào)告。
有了報(bào)告搜集器,我們就可以更清楚的了解增強(qiáng)SQL執(zhí)行過程中的細(xì)節(jié)了。由于我們的SQL執(zhí)行基于Spark實(shí)現(xiàn),有了這個(gè)報(bào)告搜集器,一些簡單的Spark程序優(yōu)化還可以直接通過查看報(bào)告來完成。
報(bào)告搜集器是一個(gè)十分好用的功能,當(dāng)然需要集成到調(diào)試器中了。通過在Debugger類中加入report()方法,我們?cè)谡{(diào)試過程中可以隨時(shí)打印程序執(zhí)行報(bào)告。
打印日志與執(zhí)行檢查
打印日志也是我們調(diào)試程序的常用手段,如何在增強(qiáng)SQL中支持日志打印呢?可以考慮定義一個(gè)任務(wù)類型為log,按照如下方式來使用:
-- target=log.some_info_about_this_log
select 1 as var_1, 2 as var_2
日志打印結(jié)果可以在上述任務(wù)報(bào)告中出現(xiàn),一個(gè)比較直觀的設(shè)計(jì)可以是:
===================== REPORT FOR step-1 ==================
config: StepConfig(target=log.some_info_about_this_log, condition=None, line_no=1)
sql: select 1 as var_1, 2 as var_2
status: SUCCEEDED
start time: 2021-04-10 10:05:33, end time: 2021-04-10 10:05:33, execution time: 0.069165s - 0.25%
messages:
var_1=1, var_2=2
很多編程語言都提供了assert語法,用以在開發(fā)過程中進(jìn)行及時(shí)的假設(shè)驗(yàn)證,我們也可以在增強(qiáng)SQL增加這樣的支持。可以考慮定義一個(gè)任務(wù)類型為check,按照如下方式來使用:
-- target=check.actual_should_equal_expected
select 1 as actual, 2 as expected
如果從結(jié)果集中的獲取的actual值與expected值不相等,則此任務(wù)會(huì)失敗,并打印錯(cuò)誤消息。同時(shí),這樣的錯(cuò)誤可以在上述任務(wù)報(bào)告中體現(xiàn),一個(gè)比較直觀的設(shè)計(jì)可以是:
===================== REPORT FOR step-1 ==================
config: StepConfig(target=check.actual_should_equal_expected, condition=None, line_no=10)
sql: select 1 as actual, 2 as expected
status: FAILED
start time: 2021-04-10 10:25:32, end time: 2021-04-10 10:25:32, execution time: 0.071442s - 0.52%
messages:
check [actual_should_equal_expected] failed! actual=1, expected=2, check_data=[...]
通過調(diào)試模式屏蔽調(diào)試過程中的副作用
有了調(diào)試器,現(xiàn)在可以愉快的寫代碼了。但很快我們就會(huì)發(fā)現(xiàn)另一個(gè)需要解決的問題,那就是調(diào)試過程可能導(dǎo)致寫入某些外部數(shù)據(jù)庫表。這將帶來一些風(fēng)險(xiǎn),因?yàn)槲覀冇锌赡茉谡{(diào)試的時(shí)候把一些不應(yīng)該被覆蓋的數(shù)據(jù)庫表給覆蓋了。
要解決這個(gè)問題也很簡單,我們可以在SQL執(zhí)行器中引入一個(gè)debug標(biāo)記來實(shí)現(xiàn)。有了debug標(biāo)記,在執(zhí)行某一步驟的時(shí)候,可以判斷是否是向外部數(shù)據(jù)庫表做寫操作,如果是且debug為true,則跳過寫操作,只是將該數(shù)據(jù)創(chuàng)建一個(gè)TempView而已。
上述寫表操作只是一個(gè)場(chǎng)景而已,有了debug標(biāo)記,我們還可以做很多事情,比如打印更多的調(diào)試信息等。
Debugger類在調(diào)用SQL執(zhí)行器時(shí),應(yīng)當(dāng)將debug標(biāo)記設(shè)置為true,這樣我們就不用擔(dān)心調(diào)試的時(shí)候產(chǎn)生任何不想發(fā)生的副作用了。
Web數(shù)據(jù)開發(fā)環(huán)境
在JupyterLab中調(diào)試代碼
有了上面這些功能,調(diào)試器看起來是不錯(cuò)了,但是要與IDE的交互體驗(yàn)比起來,命令行版本的還是過于簡單了。能不能想辦法增強(qiáng)一下呢?
數(shù)據(jù)分析師常用的用于運(yùn)行代碼的工具要算JupyterLab了。作為一個(gè)打開網(wǎng)頁就能用的開發(fā)環(huán)境,JupyterLab有非常多十分好用的功能,比如,可以一段一段的定義和執(zhí)行代碼,可以支持嵌入Markdown文檔,可以支持可視化結(jié)果展示,可以編輯多種語言代碼等等。
JupyterLab能不能作為我們的代碼編輯器使用呢?
查看JupyterLab最新版本,我們會(huì)發(fā)現(xiàn)JupyterLab提供了Code Console的功能,且可以支持多個(gè)編輯器分屏。其操作界面如下:

此時(shí),大家可能已經(jīng)想到了,可以借助這樣的交互來實(shí)現(xiàn)我們的代碼調(diào)試功能。JupyterLab不僅給我們提供了一個(gè)不錯(cuò)的編輯代碼的界面,利用Code Console還可以實(shí)現(xiàn)一邊寫代碼一邊調(diào)試。
在JupyterLab中配置好調(diào)試器后,一個(gè)典型的使用過程如下:

使用JupyterLab還有一系列的其他好處,比如:
- 開發(fā)人員無需安裝配置本地環(huán)境(這常常非常耗時(shí)),只需要一個(gè)瀏覽器即可開始編寫代碼。
- 可以直接配置JupyterLab連接到數(shù)據(jù)平臺(tái)集群環(huán)境,這樣我們就可以直接在集群環(huán)境中調(diào)試,執(zhí)行與生產(chǎn)時(shí)同樣的代碼,于是上線代碼就更有信心了。
在容器中啟動(dòng)JupyterLab
在基于Hadoop的大數(shù)據(jù)集群中進(jìn)行數(shù)據(jù)開發(fā)時(shí),常常還有一個(gè)不夠方便的地方,那就是客戶端環(huán)境的構(gòu)建。
我們常常需要集成了多種集群組件的客戶端,比如Spark, HDFS, Hive, HBase等,這些客戶端的配置需要保持和集群同步。如果自己去構(gòu)建這樣的客戶端,不僅耗時(shí),而且很容易出錯(cuò)。
Ambari可以幫助我們自動(dòng)配置集群節(jié)點(diǎn),如軟件安裝,配置同步等繁瑣的工作Ambari都可以幫我們搞定。當(dāng)需要使用集群的客戶端環(huán)境時(shí),常常也是通過Ambari配置的集群節(jié)點(diǎn)來實(shí)現(xiàn)。
使用Ambari配置的集群節(jié)點(diǎn)作為客戶端卻有另一個(gè)缺點(diǎn),那就是這樣的節(jié)點(diǎn)常常由于數(shù)量較少而在團(tuán)隊(duì)中間共享(由于資源占用問題,我們一般不會(huì)配置過多的客戶端節(jié)點(diǎn))。
既然是共享的節(jié)點(diǎn),大家都在節(jié)點(diǎn)上面操作,就容易發(fā)生沖突。比如,小A用自己的帳號(hào)登錄了(通過kinit),此時(shí)小B想要訪問集群,如果不使用其他的操作系統(tǒng)帳號(hào),小B就會(huì)直接用到小A的帳號(hào)權(quán)限來訪問系統(tǒng),這不是期望的行為。還比如,小A需要在客戶端中安裝某一個(gè)版本1依賴庫,而小B需要在客戶端中安裝同一個(gè)依賴庫的版本2,這就產(chǎn)生了沖突,需要小A和小B相互協(xié)調(diào)才行。
容器技術(shù)是解決此問題的一個(gè)很好的方式。容器可以提供必要的環(huán)境隔離,使得團(tuán)隊(duì)成員可以自由的在自己的環(huán)境中進(jìn)行操作,無需擔(dān)心對(duì)他人造成影響。
如何實(shí)現(xiàn)呢?其實(shí)我們只需要一個(gè)運(yùn)行了sshd的容器即可。通過暴露特定的端口,我們可以把運(yùn)行著sshd的容器作為一個(gè)節(jié)點(diǎn),注冊(cè)到Ambari中,然后利用Ambari幫我們安裝好相關(guān)的依賴軟件。
軟件安裝完成之后,我們可以通過docker save命令將這樣容器保存為一個(gè)基礎(chǔ)容器鏡像。然后通過運(yùn)行多個(gè)此容器,我們就擁有了多個(gè)此類客戶端了。由于容器運(yùn)行成本非常低,可以為每個(gè)需要編寫代碼的團(tuán)隊(duì)成員運(yùn)行一個(gè)容器作為他自己的客戶端使用。這樣一來,開發(fā)人員環(huán)境隔離問題就迎刃而解了。
在容器環(huán)境中運(yùn)行一個(gè)JupyterLab來支持開發(fā)是一個(gè)不錯(cuò)的主意。這樣一來,每個(gè)人都擁有了自己的一套獨(dú)立的用JupyterLab打造的開發(fā)環(huán)境了。
總結(jié)
前面的文章中我們提到使用增強(qiáng)SQL來進(jìn)行數(shù)據(jù)開發(fā),但是這帶來了一些額外的使用成本。本文討論了如何支持增強(qiáng)SQL的代碼編輯和調(diào)試功能。
通過實(shí)現(xiàn)一個(gè)增強(qiáng)SQL調(diào)試器,并在JupyterLab中運(yùn)行此調(diào)試器,我們可以打造了一個(gè)基于Web的輕量級(jí)數(shù)據(jù)開發(fā)環(huán)境,這能很大程度上提高數(shù)據(jù)開發(fā)的效率。為了更好的支持?jǐn)?shù)據(jù)開發(fā),我們還可以考慮在SQL執(zhí)行器中增加執(zhí)行報(bào)告搜集的功能,在調(diào)試器中隨時(shí)打印執(zhí)行報(bào)告對(duì)于數(shù)據(jù)開發(fā)是一件好事。除此之外,還可以在SQL執(zhí)行器中引入調(diào)試標(biāo)記,這可以用來避免調(diào)試過程的可能的副作用。
基于在多家企業(yè)多個(gè)項(xiàng)目的經(jīng)驗(yàn)總結(jié),我們沉淀出一套企業(yè)數(shù)據(jù)開發(fā)工作臺(tái),可以幫助團(tuán)隊(duì)高效交付數(shù)據(jù)需求,幫助企業(yè)快速構(gòu)建數(shù)據(jù)能力,工作臺(tái)地址:https://data-workbench.com/
我們開源了其中的核心模塊–ETL開發(fā)語言,開源項(xiàng)目地址:https://github.com/easysql/easy_sql


如若轉(zhuǎn)載,請(qǐng)注明出處:http://www.qjsdgw.cn/85547.html