Hive執行計劃之hive依賴及許可權查詢和常見使用場景

来源:https://www.cnblogs.com/lubians/archive/2023/06/07/17464519.html
-Advertisement-
Play Games

[TOC] ## 概述 Hive查看執行計劃的命令中還有兩個不怎麼常用但很重要的命令,接下來詳細介紹一下。 有一個問題:**如何在hiveSQL執行之前就探查到這段邏輯的血緣依賴關係?** hive血緣是很多生產級數倉必須要提供的功能,大多數解決方案都是**使用hive hooks的方法通過SQL執 ...


目錄

概述

Hive查看執行計劃的命令中還有兩個不怎麼常用但很重要的命令,接下來詳細介紹一下。

有一個問題:如何在hiveSQL執行之前就探查到這段邏輯的血緣依賴關係?

hive血緣是很多生產級數倉必須要提供的功能,大多數解決方案都是使用hive hooks的方法通過SQL執行後解析得到hive表的依賴關係

這個方案能細粒度到欄位級依賴,屬於很完善的一個解決方案,但有很多場景我們需要在SQL執行之前就得到依賴關係,那麼如何解決的呢?

1.explain dependency的查詢與使用

explain dependency 提供了這樣的一個解決方案,它可以查詢一段SQL需要的數據來源,以JSON的形式展現結果數據。裡面主要包含兩部分內容:

  • input_tables:描述一段SQL依賴的數據來源表,裡面存儲的是hive表名的列表,格式如下:

    {"tablename":"庫名@表名","tabletype":"表的類型(外部表/內部表)"}

  • input_partitions:描述一段SQL依賴的數據來源表分區,裡面存儲的是分區名稱的列表,格式如下:

    {"partitionName":"庫名@表名@分區列=分區列的值"}

    如果查詢的表為非分區表,則顯示為空。

可以通過以下例子來進行比對,其中例1是查詢非分區普通表SQL的explain dependency,例2是查詢分區表SQL的explain dependency。

例1 使用explain dependency查看SQL非分區普通表。

explain dependency
-- 統計年齡小於30歲各個年齡里,昵稱裡帶“小”的人數
select age,count(0) as num from temp.user_info_all_no
where age < 30 and nick like '%小%'
group by age;

輸出結果內容:

{"input_tables":[{"tablename":"temp@user_info_all_no","tabletype":"MANAGED_TABLE"}],"input_partitions":[]}

例2 使用explain dependency查看SQL查詢分區表。

explain dependency
-- 統計年齡小於30歲各個年齡里,昵稱裡帶“小”的人數,其中ymd欄位為分區欄位
select age,count(0) as num from temp.user_info_all where ymd >= '20230501'
and age < 30 and nick like '%小%'
group by age;

輸出結果內容:

{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"}]}

2.藉助explain dependency解決一些常見問題

explain dependency的使用場景有以下幾個:

場景一,快速排除。快速排除因為讀不到相應分區的數據而導致任務數據輸出異常。例如,在一個以天為分區的任務中,上游任務因為生產過程不可控因素出現異常或者空跑,導致下游任務引發異常。通過這種方式,可以快速查看SQL讀取的分區是否出現異常。

場景二,理清表的輸入,幫助理解程式的運行,特別是有助於理解有多重子查詢,多表連接的依賴輸入。

場景三,提前通過解析hiveSQL腳本進行血緣依賴解析,用於一些定製化數據平臺工具開發中的血緣構建。

explain dependency的使用能幫助開發者解決哪些問題呢?

2.1.識別看似等價的SQL代碼實際上是不等價的:

對於接觸SQL不久的程式員來說,很多人容易將

select * from a left join b on a.no=b.no and a.f>1 and a.f<3;

這段邏輯等價於 select * from a left join b on a.no=b.no where a.f>1 and a.f<3;

這兩段的邏輯的區別是在多表left join的時候where 後加條件是否等價與on後面加條件。

我們通過實例來看看其中的區別:

例3 使用explain dependency識別看似等價的SQL代碼。

-- 代碼1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd and a.ymd >= '20230501' and a.ymd <= '20230502';

-- 代碼2
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd 
where a.ymd >= '20230501' and a.ymd <= '20230502';

輸出結果內容:

// 代碼1輸出結果
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230503"},{"partitionName":"temp@user_act_info@ymd=20230606"}]}

// 代碼2輸出結果
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}

通過以上輸出結果可以看出,上面例子里的兩段SQL其實並不等價。在left join(left outer join)的連接條件中加入非等值的過濾條件後,這裡特指作用於a表,也就是連接的基表,並沒有將左外連接的左右兩個表按照過濾條件進行過濾,左外連接在執行時會讀取所有分區數據,然後進行關聯數據過濾操作。

left outer join 針對左表非等值條件on和where查詢數據on條件查詢數據大於where條件查詢數據。

下麵查看left outer join對右表的過濾條件實例:

例4 使用explain dependency識別left outer join 右表過濾非等值條件區別

-- 代碼1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd and b.ymd >= '20230501' and b.ymd <= '20230502';

-- 代碼2
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd 
where b.ymd >= '20230501' and b.ymd <= '20230502';

輸出結果內容:

// 代碼1輸出結果,on後跟非等值條件
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}

// 代碼2輸出結果,where後跟非等值條件
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230503"},{"partitionName":"temp@user_act_info@ymd=20230606"}]}

可以看到left outer join 針對右表非等值條件on和where查詢數據左表都是全表掃描,右表on條件是條件過濾,where條件是全表掃描。

接下來對inner join,right outer join,full outer join進行測試。會發現

inner join 的類似針對左右表非等值條件on和where查詢數據是等價的。

right outer join和left join相反。

full outer join都是全表掃描。

那麼可以很好的判斷出一下兩段SQL的過濾條件數據讀取範圍是完全不一樣的。就不貼執行結果了。

例5 left outer join下的對左表和右表不等值條件過濾。

-- 代碼1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd and a.ymd >= '20230501' and a.ymd <= '20230502';

-- 代碼2
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd and b.ymd >= '20230501' and b.ymd <= '20230502';

以上不同join類型數據查詢範圍不一致主要原因和hive對join和where的謂詞下推支持不同有關。通過explain dependency可以直接驗證hive對join和where進行謂詞下推規則的驗證。

謂詞下推可詳細查看什麼是謂詞下推,看這一篇就夠了

2.2 通過explain dependency驗證將過濾條件在不同位置的查詢區別

如果要使用外連接並需要對左右兩個表進行條件過濾,做好的方式是將過濾條件放到就近處,即如果已經知道表數據過濾篩選條件,那麼在使用該表前,就先用過濾條件進行過濾,然後進行其他操作。

一些SQL內置優化器會做一些過濾下推優化,但部分條件還是不會進行下推。所以我們在寫SQL時儘量養成先過濾而後進行其他操作(聚合,關聯)的習慣。

可以看如下實例:

例6 left outer join對左表過濾數據的優化對比。

-- 代碼1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd 
where a.ymd >= '20230501' and a.ymd <= '20230502';

-- 代碼2
explain dependency
select a.uid from (
	select uid,ymd from temp.user_info_all
  -- 在子查詢內部進行過濾
  where ymd >= '20230501' and ymd <= '20230502'  
) a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd;

-- 代碼3
explain dependency
select a.uid from (
	select uid,ymd from temp.user_info_all
  -- 在子查詢內部進行過濾
  where ymd >= '20230501' and ymd <= '20230502'  
) a
left outer join (
	select uid,ymd from temp.user_act_info
  where ymd >= '20230501' and ymd <= '20230502'
) b
on a.uid = b.uid and a.ymd = b.ymd;

執行結果:

//代碼1,左右表都進行了過濾
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}

//代碼2,右表進行了全表掃描
{"input_tables":[{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230503"},{"partitionName":"temp@user_act_info@ymd=20230606"}]}

//代碼3,左右表都進行了過濾
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}

可以看到left outer join對左表過濾數據的優化中代碼1片段等價於代碼3片段,即兩表都在就近處都過濾。

例7 left outer join對右表過濾數據的優化對比。

-- 代碼1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd 
where b.ymd >= '20230501' and b.ymd <= '20230502';

-- 代碼2
explain dependency
select a.uid from (
	select uid,ymd from temp.user_info_all
  -- 在子查詢內部進行過濾
  where ymd >= '20230501' and ymd <= '20230502'  
) a
left outer join (
	select uid,ymd from temp.user_act_info
  where ymd >= '20230501' and ymd <= '20230502'
) b
on a.uid = b.uid and a.ymd = b.ymd;

-- 代碼3
explain dependency
select a.uid from temp.user_info_all a
left outer join (
	select uid,ymd from temp.user_act_info
  where ymd >= '20230501' and ymd <= '20230502'
) b
on a.uid = b.uid and a.ymd = b.ymd;

執行結果內容:

// 代碼1 ,左右表都進行了全表掃描
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230503"},{"partitionName":"temp@user_act_info@ymd=20230606"}]}

//代碼2,左右表都進行了過濾
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}

//代碼3,右表都進行了過濾
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"}]}

可以看到left outer join對右表過濾數據的優化中代碼2是最優,代碼3次之,代碼1最差。

3.查看SQL操作涉及到的相關許可權信息

通過explain authorization可以知道當前SQL訪問的數據來源(INPUTS) 和數據輸出(OUTPUTS),以及當前Hive的訪問用戶 (CURRENT_USER)和操作(OPERATION)。

可以看以下實例:

例8 使用explain authorization查看許可權相關信息。

explain authorization
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd 
where a.ymd >= '20230501' and a.ymd <= '20230502';

執行結果:

INPUTS: 
  temp@user_info_all
  temp@user_act_info
  temp@user_info_all@ymd=20230501
  temp@user_info_all@ymd=20230502
  temp@user_act_info@ymd=20230501
  temp@user_act_info@ymd=20230502
OUTPUTS: 
  hdfs://nameservice1/tmp/hive/hdfs/a88cc133-c310-4129-bfa0-28011ac23904/hive_2023-06-07_19-42-55_464_2777807904847671424-1/-mr-10000
CURRENT_USER: 
  hdfs
OPERATION: 
  QUERY
AUTHORIZATION_FAILURES: 
  Permission denied: Principal [name=hdfs, type=USER] does not have following privileges for operation QUERY [[SELECT] on Object [type=TABLE_OR_VIEW, name=temp.user_act_info], [SELECT] on Object [type=TABLE_OR_VIEW, name=temp.user_info_all]]

從上面的信息可知:

上面案例的數據來源是temp資料庫中的 user_info_all表和user_act_info表;

數據的輸出路徑是hdfs://nameservice1/tmp/hive/hdfs/a88cc133-c310-4129-bfa0-28011ac23904/hive_2023-06-07_19-42-55_464_2777807904847671424-1/-mr-10000;

當前的操作用戶是hdfs,操作是查詢(QUERY);

觀察上面的信息我們還會看到AUTHORIZATION_FAILURES信息,提示對當前的輸入沒有查詢許可權,但如果運行上面的SQL的話也能夠正常運行。為什麼會出現這種情況?Hive在預設不配置許可權管理的情況下不進行許可權驗證,所有的用戶在Hive裡面都是超級管理員,即使不對特定的用戶進行賦權,也能夠正常查詢。

通過上面對explain相關參數的介紹,可以發現explain中有很多值得我們去研究的內容,讀懂 explain 的執行計劃有利於我們優化Hive SQL,同時也能提升我們對SQL的掌控力。

下一期:Hive執行計劃之什麼是hiveSQL向量化模式及優化詳解

按例,歡迎點擊此處關註我的個人公眾號,交流更多知識。

後臺回覆關鍵字 hive,隨機贈送一本魯邊備註版珍藏大數據書籍。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • > > 傳統桌面客戶端的遠程調試相比`UWP`,`ASP`等項目來說,配置比較麻煩,因為它是非部署的應用程式,原理是複製編譯的文件到遠程電腦,通過網路來連接和`VS`的通信,本文主要講述`WPF`,`WinForm`應用程式的遠程調試。 ![](https://learn.microsoft.co ...
  • ## 前言 使用 C# 作為開發語言已經 15 個年頭了,受惠於 C# 的不斷更新,伴隨著大量的新特性與大量語法糖,讓我更加容易寫出簡潔、高效的代碼。日常中大量特性早已信手拈來,當然從未嘗試過的特性更是難以盡數,但是每每回憶代碼中的特性究竟是哪個版本引入的,卻頗為含糊。索性簡單整理記錄下來,用以備忘 ...
  • 大家好,我是god23bin。歡迎來到《**一分鐘學一個 Linux 命令**》系列,今天需要你花兩分鐘時間來學習下,因為今天要講的是兩個命令,`mv` 和 `cp` 命令。 ...
  • # CentOS7 本地光碟鏡像rpm包 ## 一、前言 > rpm包的下載方式 > > - 通過本地光碟鏡像下載rpm,centos7.iso鏡像文件,內置了絕大多數軟體的rpm包(本文章即演示如何配置本地rpm) > > - 線上下載rpm包,有很多軟體的官網,以及第三方軟體倉庫,會提供下載功能 ...
  • 基本語法格式: Location block 的基本語法形式是: location [=|~|~*|^~|@] pattern { ... } [=|~|~*|^~|@] 被稱作 location modifier ,這會定義 Nginx 如何去匹配其後的 pattern ,以及該 pattern ...
  • MCU:STM32F429ZIT6 開發環境:STM32CubeMX+MDK5 外購了一個SPI介面的SD Card模塊,想要實現SD卡存儲數據的功能。 首先需要打開STM32CubeMX工具。輸入開發板MCU對應型號,找到開發板對應封裝的MCU型號,雙擊打開(圖中第三)。 此時,雙擊完後會關閉此界 ...
  • MCU:STM32F103VET6 開發環境:STM32CubeMX+MDK5 實現USB的虛擬串口不需要去理解USB的底層驅動,只需要STM32CubeMX去配置生成工程即可。在野火的指南者中,是沒有這一類的視頻和示例的,博主使用這款開發板實現USB虛擬串口。 首先需要打開STM32CubeMX工 ...
  • ## 前言 本篇文章主要介紹的關於本人從剛工作到現在使用Sql一些使用方法和經驗,從最基本的SQL函數使用,到一些場景的業務場景SQL編寫。 ## SQL基礎函數使用 ### 1.欄位轉換 CASE WHEN 意義: If(a==b) a=c; 用法: 1, CASE 欄位 WHEN 欄位結果1 T ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...