SQL SERVER大話存儲結構(3)_數據行的行結構

来源:http://www.cnblogs.com/xinysu/archive/2017/05/18/6868901.html
-Advertisement-
Play Games

一行數據是如何來存儲的呢? 變長列與定長列,NULL與NOT NULL,實際是如何整理存放到 8k的數據頁上呢? 對錶格進行增減列,修改長度,添加預設值等DDL SQL,對行存儲結構又會有怎麼樣的影響呢? 什麼是大對象,什麼是行溢出,存儲引擎是如何處理它們呢? 如果轉載,請註明博文來源: www.c ...


        一行數據是如何來存儲的呢?     變長列與定長列,NULL與NOT NULL,實際是如何整理存放到 8k的數據頁上呢?     對錶格進行增減列,修改長度,添加預設值等DDL SQL,對行存儲結構又會有怎麼樣的影響呢?     什麼是大對象,什麼是行溢出,存儲引擎是如何處理它們呢?      

      如果轉載,請註明博文來源: www.cnblogs.com/xinysu/   ,版權歸 博客園 蘇家小蘿蔔 所有。望各位支持!   

 

 1 引入  

    在一個DB內,每一個table都能在sys.sysobjects中找到對應的描述,每一個列,都能從sys.columns中找到說明。     這裡發個SQL是日常管理中使用到的,用於描述一個表格的數據結構情況。
 1 SELECT
 2 
 3       表名 = CASE WHEN A.COLORDER=1 THEN D.NAME ELSE '' END,
 4       表說明 = CASE WHEN A.COLORDER=1 THEN ISNULL(F.VALUE,'') ELSE '' END,
 5       列序列號 = A.COLORDER,
 6       列名 = A.NAME,
 7       標識 = CASE WHEN COLUMNPROPERTY( A.ID,A.NAME,'ISIDENTITY')=1 THEN ''ELSE '' END,
 8       約束 = CASE WHEN EXISTS(
 9                                SELECT 1
10                                FROM SYSOBJECTS
11                                WHERE XTYPE='PK' AND PARENT_OBJ=A.ID AND NAME IN (
12                                                                                   SELECT
13                                                                                         NAME
14                                                                                   FROM SYSINDEXES
15                                                                                   WHERE INDID IN( SELECT INDID FROM SYSINDEXKEYS WHERE ID = A.ID AND COLID=A.COLID )
16                                                                                  )
17                              ) THEN 'PK'
18                  WHEN EXISTS (
19                                SELECT 1 FROM sys.foreign_key_columns
20                                WHERE parent_object_id=A.ID AND parent_column_id=A.COLID
21                              ) THEN 'FK'+'('+(SELECT OBJECT_NAME(referenced_object_id)+'.'+COL_NAME(referenced_object_id,referenced_column_id)+')' FROM sys.foreign_key_columns WHERE parent_object_id=A.ID AND parent_column_id=A.COLID)
22             ELSE '' END,
23       數據類型 = CASE WHEN B.NAME IN ('CHAR','NCHAR','VARCHAR','NVARCHAR') THEN B.NAME+'('+ISNULL(CAST(case when COLUMNPROPERTY(A.ID,A.NAME,'PRECISION')=-1 then null else COLUMNPROPERTY(A.ID,A.NAME,'PRECISION') end AS VARCHAR(10)),'MAX')+')'
24                       WHEN B.NAME ='DECIMAL' THEN B.NAME+'('+CAST(COLUMNPROPERTY(A.ID,A.NAME,'PRECISION') AS VARCHAR(10))+','+CAST(ISNULL(COLUMNPROPERTY(A.ID,A.NAME,'SCALE'),0) AS VARCHAR(10))+')'
25                       ELSE B.NAME END,
26       占用位元組長度 = A.LENGTH,
27       --長度 = COLUMNPROPERTY(A.ID,A.NAME,'PRECISION'),
28       --小數位數 = ISNULL(COLUMNPROPERTY(A.ID,A.NAME,'SCALE'),0),
29       允許空 = CASE WHEN A.ISNULLABLE=1 THEN ''ELSE '' END,
30       預設值 = case when E.TEXT is not null then
31 
32                                                    case when substring(e.text,1,2)='((' then substring(e.text,3,len(e.text)-4)
33                                                                                        when substring(e.text,1,1)='(' then substring(e.text,2,len(e.text)-2)
34                                                                                   else e.text end
35                                   else '' end ,
36       列說明 = ISNULL(G.[VALUE],'')
37 FROM SYSCOLUMNS A LEFT JOIN SYSTYPES B ON A.XUSERTYPE=B.XUSERTYPE
38       INNER JOIN SYSOBJECTS D ON A.ID=D.ID AND D.XTYPE='U' AND D.NAME<>'DTPROPERTIES'
39       LEFT JOIN SYSCOMMENTS E ON A.CDEFAULT=E.ID
40       LEFT JOIN sys.extended_properties G ON A.ID=G.major_id AND A.COLID=G.minor_id
41       LEFT JOIN sys.extended_properties F ON D.ID=F.major_id AND F.minor_id=0
42 WHERE D.NAME IN ('area','','')
43 ORDER BY A.ID,A.COLORDER
查詢表結構SQL

2 數據行

2.1 數據行結構

    數據行在數據頁面的存儲結構詳見下表,分為幾個部分:基礎信息4位元組、定長列相關、變長列相關及null點陣圖。詳見下表。這部分的內容具體參考《SQL server技術內幕:存儲引擎》第6章。    參考下圖,一行數據的大小是這麼計算的:Row_Size=Fixed_Data_Size+Variable_Data_Size+Null_Bitmap+4 。             各個部分其實都比較好理解,狀態B位未使用,狀態A位,詳細描述如下。
  • 狀態位A:表示行屬性的點陣圖,1位元組,8bit
    • Bit 0 位,版本信息
    • Bits 1-3 位,行記錄類型
      • 0,primary record,主記錄
      • 1,forwarded record
      • 2,forwarding stub
      • 3,index record,索引記錄
      • 4,blob或者行溢出數據
      • 5,ghost索引記錄
      • 6,ghost數據記錄
    • Bit 4 位,NULL點陣圖
    • Bit 5 位,表示行中有變長列
    • Bit 6 位,保留
    • Bit 7 位,ghost record(幽靈記錄)
  • 列偏移矩陣
    • 如果一個表格,沒有變長列,那麼這個表格則不需要列偏移矩陣
    • 一個變長列,有一個列偏移矩陣,一個列偏移矩陣2個位元組,用於表示變長列中每個列的結束位置。

2.2 特殊情況(大對象、行溢出及forword)

2.2.1 大對象

     text, ntext, image, nvarchar(max), varchar(max), varbinary(max), and xml這種數據列,稱為大對象列, 註意,變長數據類型nvarchar,varchar,varbinary只有當存儲內容大於8k才變為大對象列。    行不能跨頁,但是行的部分可以移出行所在的頁,因此行實際可能非常大。頁的單個行中的最大數據量和開銷是 8,060 位元組 (8 KB)。考慮大對象列極為占用空間,所以在一行數據的主記錄中,是不存儲大對象列的,僅存儲 16位元組 指向 大對象列實際存儲到LOB data頁面的位置。     比如,一個大對象列text,text列存儲5000的字元,其他列占用50個字元,如果是放在一起存儲的話,10行數據就需要10個page,掃描就需要10次IO;而如果不放在一次,一個IN-ROW-DATA page就能存儲這10行數據,text列單獨存放在 LOB data列,那麼,掃描這10行的主記錄,僅需要1次IO。所以,大對象列是不跟主記錄存儲在一起。       這樣,一個8k的數據頁,就能儘可能多的存儲主記錄,可以在查詢的時候,避免 大對象列占用主記錄空間,導致IO次數增增加。

2.2.2 行溢出

    超過 8,060 位元組的行大小限制可能會影響性能,因為 SQL Server 仍保持每頁 8 KB 的限制。當合併 varchar、nvarchar、varbinary、sql_variant 或 CLR 用戶定義類型的列超過此限制時,SQL Server 資料庫引擎 將把最大寬度的記錄列移動到 ROW_OVERFLOW_DATA 分配單元的另一頁上,然後在主記錄記錄一個24位元組的指針,用與描述 被移出的列 實際存儲位置。比如,一行數據總大小超過8k,那麼在insert的過程中,會把最大寬度的記錄移動到另外的數據頁面。       如果更新操作使記錄變長,大型記錄將被動態移動到另一頁。如果更新操作使記錄變短,記錄可能會移回 IN_ROW_DATA 分配單元中的原始頁。此外,執行查詢和其他選擇操作(例如,對包含行溢出數據的大型記錄進行排序或合併)將延長處理時間,因為這些記錄將同步處理,而不是非同步處理。     一行數據(不包括大對象列)總長度超過了8k,則會把最大寬度的列內容移動到ROW_OVERFLOW_DATA頁面上,主記錄上留下一個24位元組的指針 描述 被溢出挪走的列內容 實際存儲位置,這個稱為行溢出。

2.2.3 forword

    在一堆表內的一個數據頁面,存儲了N行數據,現在,其中一行數據的某一列發生修改,導致其列的長度加大,而剩餘的頁面空間無法存儲該列數據,那麼這個時候,就會把該列數據移動到新的 IN_ROW_DATA 頁面上,在主記錄留下一個 9個位元組的 指針,指向實際列的存儲位置,這個稱之為 forword。     forward的條件是:堆表、變長列、更新操作及其數據頁面剩餘空間不足存儲新列內容。     為什麼一定要是堆表呢?因為如果是聚集索引表格,遇到這種情況,數據頁會split,把一半的內容另外存儲到新的數據頁,由於聚集索引上的非聚集索引鍵值查詢根據是主鍵,所以split操作不會影響到非聚集索引,但是堆表的非聚集索引結構查找行是根據RID,如果也split,那麼所有非聚集索引都需要修改鍵值RID,故在堆表上,使用了forword。     為什麼是更新操作呢?因為如果是INSERT操作,一開始就出現空間不足的情況,它老早就跑路到新的數據頁上了,不會再空間不足的數據頁面坐INSERT操作。      比如,一行數據原本存儲在一個數據頁面中,但是update某一列,增大其存儲內容,發現該數據頁沒有空閑的空間可以存儲該列內容,該列則會forword到另外的數據頁IN_ROW_DATA存儲,主記錄留下一個9位元組的指針。

3 測試存儲情況

   測試思路
  1. 先建立一個只有2列非空定長列的堆表,然後INSERT一行數據,檢查page頁面存儲內容
  2. 添加主鍵,檢查存儲頁面內容
  3. 增加一列:可空變長列
  4. 增加一列:非空變長列+預設值(分大對象和非大對象)
  5. 刪除無數據的列
  6. 刪除有數據的列
  7. 行溢出
  8. forword

3.1 堆表分析

  create table tbrow(id int not null identity(1,1),name char(20) not null)   insert into tbrow(name) select 'xinysu';   dbcc traceon(3604) dbcc ind('dbpage','tbrow',-1)  --根據返回結果,判斷324為數據頁,如果不理解,請查看本系列第一篇博文   dbcc page('dbpage',1,324,3)  

    查看 `消息` 內容,可以看到 slot 0 存儲的行數據大小為21位元組,由於現在的 tbrow表格中,只有兩列 int 跟 char ,由於都是定長列,所有變長列的存儲模塊均為空,但是註意一點,即使整個表格都沒有允許Null的列,Null點陣圖仍然會占用一個位元組。     所以 該行記錄的長度=狀態A+狀態B+定長欄位長度+定長欄位內容+總烈屬+null點陣圖=1+1+2+(4+10)+2+1= 21 bytes。     根據行的16進位記錄:10001200 01000000 78696e79 73752020 2020020000,來詳細分析這行數據的存儲情況。先把這串字元按照位元組數區分,其中註意部分需要反序後再轉換十進位。詳細分析及推導見下圖。

3.2 添加主鍵

alter table tbrow add constraint pk_tbrow primary key(id)   dbcc traceon(3604) dbcc ind('dbpage','tbrow',-1)     可以看到,表格的IAM頁及數據頁全部都改變了,因為當一個堆表添加主鍵變為聚集索引表格的時候,需要重新組織數據頁,按照聚集索引的鍵值順序存儲,所以看到,整個數據頁存儲情況發生了變化。如果是一個大堆表添加聚集索引,那麼這是一個非常耗時及耗費IO、CPU的操作,並且會鎖表直到操作結束,需謹慎操作。     再次來分析現在的行記錄。   dbcc page('dbpage',1,311,3)     可以看到,數據行的內容並沒有發生變化,添加主鍵(聚集唯一索引),會重組整個表格的存儲順序,但是不會影響到行內的數據情況。

3.3 增加一列:可空變長列

alter table tbrow add constraint pk_tbrow primary key(id)   dbcc traceon(3604) dbcc ind('dbpage','tbrow',-1)   dbcc page('dbpage',1,311,3)

    這裡開始有趣了,發現,添加了一列可空可null的列後,行記錄16進位並沒有發生變化。對比如下。   /* 第一個行為堆表行記錄 第二個行為添加主鍵後的行記錄 第三個行為添加可空變長列後的行記錄   10001200 01000000 78696e79 73752020 2020020000 10001200 01000000 78696e79 73752020 2020020000 10001200 01000000 78696e79 73752020 2020020000 */         即使表格有為null的列,有變長的列,但是,只有這些列上沒有值,是不會影響這一行的數據記錄的,這非常重要!因為意味著,給一個表格添加可為空的列時,存儲引擎不需要去修改表格內的行記錄存儲情況,只需要在數據字典上添加做變動即可,這需要獲取到表格的架構鎖,然後執行,這個執行速度非常快。       這一點的處理,跟MySQL的處理極為不一樣,雖然5.6添加了OnLine DDL,避免了DDL期間對錶格鎖表影響,但是處理添加列的時候,涉及表結構變動,需要新建臨時文件來存儲frm跟ibd文件,這是一個耗費IO的處理方式,詳細可查看之前博文:MySQL Online DDL的改進與應用 。

3.4 增加一列:非空變長列+預設值

3.4.1 非大對象列

alter table tbrow add task varchar(20) not null default 'all A' ;   dbcc traceon(3604) dbcc ind('dbpage','tbrow',-1)   dbcc page('dbpage',1,311,3)       查看16進位的行記錄:10001200 01000000 78696e79 73752020 2020020000,發現與之前的是一樣的,查看表格內容,設置了NOT NULL帶預設值的列後,實際上,查詢出來 task列是有值存儲的,存儲內容為 'all A',但是查看16進位內容的時候,卻發現,這個數據頁內的行記錄存儲內容並沒有發生變化。     這是一個神奇的處理方式!為啥呢?     仔細查看page的解析內容,發現 :Slot 0 Column 4 Offset 0x0 Length 5 Length (physical) 0 。該列數據長度為5,但是,實際存儲長度為0,也就是這一列壓根沒有存儲在數據頁面中。     個人推測:當添加了NOT NULL列+預設值(非大對象列)的情況下,不對以往數據存儲記錄發生修改,但是在查詢的時候,會判斷該列是否有存儲數據,如果沒有則使用預設值顯示。 這樣有一個非常大的好處:節約存儲空間,不變更行記錄,DDL期間,無需對以往記錄做處理,僅需修改數據字典即可。   3.4.2 大對象列        alter table tbrow add descriptions text not null default 'i love sql server' ;   dbcc traceon(3604) dbcc ind('dbpage','tbrow',-1)       單薄的表格,一行的記錄,因為添加了大對象列,來了個 LOB data的IAM頁 以及 LOB data的數據頁 。不過,這次僅分析主記錄數據頁面pageid=311。   --主記錄數據頁面pageid=311 dbcc page('dbpage',1,311,3)       依舊來分析下這行存儲記錄,原先長度都是21,為啥添加了一個 text帶預設值的列,長度就增加為50bytes呢?       這裡註意兩個地方:原先的 task列跟 description列。task列之前是實際不存儲數據內容的,但是現在存儲了數據內容,description大對象列並沒有存儲數據在主記錄中,而是存儲在另外的lob data數據頁中,在主記錄僅存儲 描述 該列具體位置內容,占16bytes。       所以 該行記錄的長度=狀態A+狀態B+定長欄位長度+定長欄位內容+總列數+null點陣圖+變長列數量+列偏移矩陣+變長數據內容=1+1+2+(4+10)+2+1+2+2*3+(5+16)= 50 bytes。       來看看這個16進位的字元串:30001200 01000000 78696e79 73752020 20200500 0403001d 00220032 80616c6c 20410000 d1070000 00004b01 00000100 0000,詳細分析這行數據的存儲情況。先把這串字元按照位元組數區分,詳細分析及推導見下圖。

 

    由此可以得到幾個推論:大對象的列NOT NULL+預設值,是在數據頁上實際存儲預設值的,而且會對錶格中的其他原本不存儲預設值的列造成影響,整個表格變成了把預設值實際存儲到數據頁面中去。當一個大表,需要增加一列大對象列NOT NULL+預設值時,會影響到表格裡面的每一行記錄,每行記錄都要增加一個16位元組的來描述 大對象列的存儲位置,同時,原本不存儲預設值的列,也會實際存儲預設值到數據頁面中,這是一個鎖表久耗費IO的操作,對於一個大表來說。     是不是發現自己 添加一個大對象列+預設值是一件可怕的事情?如果真有這種需求,而且還是個大表,請謹慎考慮。

3.5 刪除無數據的列 

--根據之前的查詢結果,skill這一列是沒有存儲數據的 alter table tbrow drop column skill   dbcc traceon(3604) dbcc ind('dbpage','tbrow',-1)   dbcc page('dbpage',1,311,3)     可以發現,刪除這一列,對實際數據存儲並沒有影響,但是該列會有一個標識值 DROPPED=[NULL]表明該列已被刪除,註意,這個表示只並不是存儲在每一行數據中,而是資料庫存儲引擎記錄。     截取數據頁面裡邊的16進位內容:30 00 1200 01000000 78696e79737520202020 0500 04 0300 1d0022003280 616c6c2041 0000d107000000004b01000001000000,發現與刪除前的是一樣的,對比如下:   /*   第一個行記錄為刪除前 第二個行記錄為刪除後   30 00 1200 01000000 78696e79737520202020 0500 04 0300 1d0022003280 616c6c2041 0000d107000000004b01000001000000 30 00 1200 01000000 78696e79737520202020 0500 04 0300 1d0022003280 616c6c2041 0000d107000000004b01000001000000   */       得出結論:刪除一行無數據的列時,不需要修改行內數據存儲情況,僅需要修改涉及的數據字典跟刪除期間持有架構鎖,這是一個非常快的過程(但是如果表格一直被其他用戶進行操作,那麼申請架構鎖也會出現等待情況)。

3.6 刪除有數據的列

--根據之前的查詢結果,skill這一列是沒有存儲數據的 alter table tbrow drop column name   dbcc traceon(3604) dbcc ind('dbpage','tbrow',-1)   dbcc page('dbpage',1,311,3)       分析到這裡,可以發現,SQL SERVER在處理刪除列這一塊處理的非常巧妙,最大程度的減少了對錶格可用性的影響,無論帶不帶數據,刪除的時候,只處理數據字典類相關內容,標識該列已被刪除,但是實際上沒有去到每一個頁面中去刪除數據,而是把這些列占用的空間在邏輯上修改為不存在,允許以後寫覆蓋。       作為一名小小的DBA,個人覺得在行數據的存儲結構這一塊,針對於增加列或者刪除列的處理,SQL SERVER 設計非常巧妙及高效!相對與 MySQL改進後的Online DDL,SQL SERVER將表格的可用性大大提高以及降低對系統資源的影響。(僅討論列的增加刪除DDL這一塊)

3.7 行溢出

    行溢出這塊,不分析其16進位行記錄,著重在 行溢出的處理方式上。 #新表格測試 create table tbflow(id int not null ,cola varchar(6000),colb varchar(6000),colc varchar(6000)) INSERT INTO tbflow SELECT 1,replicate('1',1000),replicate('1',5000),replicate('1',3000)   dbcc traceon(3604) dbcc ind('dbpage','tbflow',-1) dbcc page('dbpage',1,334,3)       cola列1000個字元,colb列5000個字元,colc列3000個字元,不算其他位元組使用,光著3列長度之和就大於8k,按照行溢出的處理,可以推測出 是colb 被移動到 Row-overflow data列,所以,先分析page 334 ,看主記錄的存儲情況,實際情況與推測一致。

3.8 Forword

    Forword這塊,不分析其16進位行記錄,著重在Forword的處理方式上。   create table tbforword(id int not null ,cola varchar(6000),colb varchar(6000),colc varchar(6000)) insert into tbforword select 1,replicate('1',1000),replicate('1',500),replicate('1',500) insert into tbforword select 2,replicate('1',1000),replicate('1',500),replicate('1',500) insert into tbforword select 3,replicate('1',1000),replicate('1',500),replicate('1',500)   dbcc traceon(3604) dbcc ind('dbpage','tbforword',-1) #記錄 IAM是385,主記錄是384頁   update tbforword set colb=replicate('1',4500) where id=2   dbcc traceon(3604) dbcc ind('dbpage','tbflow',-1)     pageid=384數據頁面中,存儲3行記錄大概用了6k+的空間,這時候,把id=2的colb列修改為4.5k長度,超過了一個頁面8k的範圍,也就意味著,這個被修改的列會被forword,根據新增的數據頁386,可推測出 forword的列存儲在386中。現在分析 pageid 384來驗證推測。詳見截圖,發現與推測一致。   dbcc page('dbpage',1,384,3)

4 行結構與DDL

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

-Advertisement-
Play Games
更多相關文章
  • 1.獲得緩存大小和清除緩存 應用內數據的所有路徑: /data/data/com.xxx.xxx/cache - 應用內緩存(註:對應方法getCacheDir()) /data/data/com.xxx.xxx/databases - 應用內資料庫 /data/data/com.xxx.xxx/s ...
  • 文章參考自:http://www.runoob.com/w3cnote/android-tutorial-button-imagebutton.html Button是TextView的子類,所以TextView上很多屬性也可以應用到Button 上!我們實際開發中對於Button的,無非是對按鈕的 ...
  • 比較簡單的一個控制項,就是加些邏輯處理而已,以前貌似是直接監聽的,封裝起來方便點 public class AccountTxtView extends android.support.v7.widget.AppCompatEditText { private final char CUT = '-' ...
  • 點擊事件的必備條件:實現 介面,重寫 方法 以撥號簡單案例為例,如下圖效果: 邏輯流程: 1. 獲取點擊對象,獲取數據 2. 給對象設置監聽類 3. 實現 介面,重寫 方法 邏輯: 1. 獲取輸入內容:使用 對象的 方法,該方法返回值為 ,需要用 轉換成 類型 2. 判斷輸入是否為空, 方法判斷字元 ...
  • Origin: 目前在 debug 一隻手機 的 charger 部分, 手機的充電電流與預測有落差, 便想使用 vendor evb board 來驗證, 但是 Rsense 不一致, 便跑去零件行。 evb 上已有一顆 Rsense,但和手機相比,仍較小, 想說用並的, 於是 買了一顆 歐姆最接 ...
  • 上面的是修改UIAlertViewController的title和message字體的大小和顏色,採用的是修改attributedString其中的NSForegroundColorAttributeName顏色屬性和NSFontAttributeName字體大小屬性。UIAlertViewCon ...
  • 準備工作: 1.下載java sdk 配置好java 環境 2.下載android sdk 跟 android studio 並配置好環境 1.查看平臺支持,添加android平臺 首先命令提示符進入到所在項目文件夾下 查看系統所支持 的平臺 因為我已經配置好環境了,所以在這裡我們能看到 andro ...
  • 回到目錄 關於redis連接數過高的解釋 對於node.js開發環境里,使用傳統的redis或者使用ioredis都是不錯的選擇,而在處理大數據請求程中,偶爾出現了連接池( redis服務端的最大可用連接數,預設為1萬)不夠用的情況,一般的提示如下: 在redis-cli上輸入info命令也可以進行 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...