InnoDB數據頁結構

来源:https://www.cnblogs.com/joeysh/archive/2019/04/16/10720192.html
-Advertisement-
Play Games

前言 ​ 關於資料庫我們知道是通過記憶體對磁碟進行操作的,也知道數據會落實到磁碟上,但是數據在磁碟上的存儲結構可能大家還不是很清楚。 ​ MySQL伺服器上負責對錶中的數據的讀取和寫入的工作的部分是存儲引擎,而關於伺服器會支持不同類型的伺服器,如:InnoDB、MyISAM、Memory...... ...


前言

​ 關於資料庫我們知道是通過記憶體對磁碟進行操作的,也知道數據會落實到磁碟上,但是數據在磁碟上的存儲結構可能大家還不是很清楚。

​ MySQL伺服器上負責對錶中的數據的讀取和寫入的工作的部分是存儲引擎,而關於伺服器會支持不同類型的伺服器,如:InnoDB、MyISAM、Memory......

​ 不同的存儲引擎都是為了實現不同的特性進行開發的,真實數據的存儲在不同的存儲引擎中存放的格式一般是不同的,有的存儲引擎比如Memory都不用磁碟來存儲數據,就跟NoSQL一樣,伺服器關閉後數據就不見了。InnoDB是MySQL的預設儲存引擎,也是我們大家常用的存儲引擎。

​ Mysql把頁作為管理存儲空間的基本單位,一個頁的大小一般是16KB,大家知道記錄其實是被儲存在頁中的,本文將詳細的帶大家看一下InnoDB儲存引擎中頁的結構。


引用

​ 參考文章:InnoDB數據頁結構


InnoDB頁

簡介

InnoDB是一個將表中的數據存儲到磁碟上的存儲引擎,所以即使關機後重啟我們的數據還是存在的。而真正處理數據的過程是發生在記憶體中的,所以需要把磁碟中的數據載入到記憶體中,如果是處理寫入或修改請求的話,還需要把記憶體中的內容刷新到磁碟上。而我們知道讀寫磁碟的速度非常慢,和記憶體讀寫之間的差距就不再多說,所以當我們想從表中獲取某些記錄時,InnoDB存儲引擎需要一條一條的把記錄從磁碟上讀出來麽?不,那樣會慢死,InnoDB採取的方式是:將數據劃分為若幹個頁,以頁作為磁碟和記憶體之間交互的基本單位,InnoDB中頁的大小一般為 16KB。也就是在一般情況下,一次最少從磁碟中讀取16KB的內容到記憶體中,一次最少把記憶體中的16KB內容刷新到磁碟中。

頁結構

​ 頁的本質介紹一個大小為16KB大小的存儲空間,頁有很多種類型的,不同的類型有不同的作用;

​ 用於存儲記錄的頁被稱為數據頁 ,大小也為16KB,但是這16KB大小的存儲空間被劃分為多個部分,不同的部分當然有著不同的功能,結構如下:

​ 從上面的圖可以看到,InnoDB的頁結構分為七個部分,下麵用表格說明一下各個部分對應的作用:

名稱 中文名 占用空間大小 簡單描述
File Header 文件頭 38位元組 描述頁的信息
Page Header 頁頭 56位元組 頁的狀態信息
Infimum + SupreMum 最小記錄和最大記錄 26位元組 兩個虛擬的行記錄(後面會說明)
User Records 用戶記錄 不確定 實際存儲的行記錄內容
Free Space 空閑空間 不確定 頁中尚未使用的空間
Page Directory 頁目錄 不確定 頁中的記錄相對位置
File Trailer 文件結尾 8位元組 結尾信息

​ 下麵會詳細介紹他們的作用


頁中的存儲

​ 當我們在存儲數據的時候,記錄會存儲到User Records部分 。但是在一個頁新形成的時候是不存在User Records 這個部分的,每當我們在插入一條記錄的時候,都會從Free Space中去申請一塊大小符合該記錄大小的空間並劃分到User Records,當Free Space的部分空間全部被User Records部分替換掉之後,就意味著當前頁使用完畢,如果還有新的記錄插入,需要再去申請新的頁,過程如下:

記錄頭

​ 對於User Records中的每一條記錄的管理,MySQL做了很多的處理,究竟做出了什麼處理呢,這需要從每條記錄裡面的記錄的額外信息部分中的記錄頭信息說起 這是有關行格式的知識,關於行格式(指的就是一條記錄的存儲結構,有多種格式),有興趣的可以去看一下InnoDB記錄存儲結構 這篇文章。

​ 首先,創建一個表:

mysql> CREATE TABLE page_demo(
    ->     c1 INT,
    ->     c2 INT,
    ->     c3 VARCHAR(10000),
    ->     PRIMARY KEY (c1)
    -> ) CHARSET=ascii ROW_FORMAT=Compact;
Query OK, 0 rows affected (0.03 sec)
mysql>

​ 如上所示,表中有三列,c1和c2用來存儲整數的,c3用來存儲字元串的。因為指定了主鍵為c1,所以MySQL就不會去創建那個隱藏的 row_id 列。指定了ascii字元集以及Compact的行格式,所以裡面的每一條記錄的行格式如下:

​ 先看一下行格式中每個屬性代表的意思:

名稱 大小(單位:bit) 描述
預留位1 1 沒有使用
預留位2 1 沒有使用
delete_mask 1 標記該記錄是否被刪除
min_rec_mask 1 標記該記錄是否為B+樹的非葉子節點中的最小記錄(索引時用到)
n_owned 4 表示當前槽管理的記錄數
heap_no 13 表示當前記錄在記錄堆的位置信息
record_type 3 表示當前記錄的類型,0表示普通記錄,1表示B+樹非葉節點記錄,2表示最小記錄,3表示最大記錄
next_record 16 表示下一條記錄的相對位置

​ 由於這裡只是描述在User Records中記錄頭的作用,所以下麵只會說明一些相關的屬性以及c1c2c3列的信息(其他信息沒畫不代表它們不存在,只是為了理解上的方便省略了~),簡化後的行格式示意圖就是這樣:

​ 我們往表中插入幾條數據:

mysql> INSERT INTO page_demo VALUES(1, 100, 'aaaa'), (2, 200, 'bbbb'), (3, 300, 'cccc'), (4, 400, 'dddd');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql>

​ 下麵看看幾條記錄在頁中的User Records是以何種形式進行體現的,為了方便理解,下麵的圖中把記錄中的頭信息和實際的數據都用的十進位進行的表示(其實都是二進位):

​ 下麵說說,記錄頭中的各個部分代表的含義:

delete_mask

​ 這個屬性說的是當前這條記錄是否被刪除,當值為0的時候代表著沒有被刪除,為1的時候標志著被刪除了。

是的,您沒看錯,當您執行刪除一個記錄的操作的時候,被刪除的記錄還存在頁中,您對它進行了刪除,它會把的

記錄頭中的這個屬性設置為1,只是打了個標記。

原因

這些被刪除的記錄之所以不立即從磁碟上移除,是因為移除它們之後把其他的記錄在磁碟上重新排列需要性能消耗,所以只是打個刪除標記而已,而且這部分存儲空間之後還可以重用,也就是說之後如果有新記錄插入到表中的話,可能把這些被刪除的記錄占用的存儲空間覆蓋掉。

如果您想徹底的從磁碟上移除這些被刪除的記錄,可以使用這個語句:

optimize table '表名';

執行這個命令後伺服器會重新規劃表中記錄的存儲方式,把被標記為刪除的記錄從磁碟上移除。

min_rec_mask

​ 有關索引的,暫時不說,後面說到索引會說明;

n_owned

​ 下麵會講

heap_no

​ 這個屬性是表示的當前記錄在當前頁中的位置,上面的一張圖如果您仔細看了的話,會發現它們的位置分別是2、3、4、5,那麼問題來了? 0和1呢?

​ 這是因為在每次創建的一頁裡面會自動的加入兩條記錄,這被稱為偽記錄 或者 虛擬記錄 (因為不是我們自己插入的);

​ 這兩條偽記錄一個代表著最小記錄,一個代表著最大記錄

​ 記錄大小的比較是通過主鍵值來比較的。在上面我們插入的幾條記錄中的從小到大的順序就是:1 < 2 < 3 < 4,

這標志著這4條記錄的大小依次遞增。

​ 不管我們插入了什麼數據,頁中的最小記錄最大記錄 都是頁生成時候的那兩條偽記錄。這兩條偽記錄的結構頁相對簡單,如下:

​ 還記得頁結構組成的七部分中一個部分叫Infimum + SupreMum ,這個部分用來存儲最小記錄和最大記錄的,沒錯,就是這兩條偽記錄。

原因:由於這兩條記錄不是我們自己定義的記錄,所以它們並不存放在User Records部分,他們被單獨放在一個稱為Infimum + Supremum的部分

​ 由上面的圖可以看出,最小記錄和最大記錄的heap_no的值分別為0和1,也就是說它們的位置最靠前。

record_type

​ 這個屬性表示當前記錄的類型,一共有4種類型的記錄,0表示普通記錄,1表示B+樹非葉節點記錄,2表示最小記錄,3表示最大記錄。從圖中我們也可以看出來,我們自己插入的記錄就是普通記錄,它們的record_type值都是0,而最小記錄和最大記錄的record_type值分別為23 ,關於1暫且不說;

next_record

​ 這個屬性表示這從當前記錄真實數據到下一條記錄的真實數據的地址偏移量

​ 假如有一條記錄的next_record 的值為12,就標志著從這條記錄的真實數據的地址往後找12個位元組就是下一條記錄的真實數據(鏈表)。也就是說頁中的數據之間的聯繫是一個根據大小比較後從小指到大的單向鏈表

​ 規定 最小記錄 的下一條記錄就本頁中主鍵值最小的記錄,而本頁中主鍵值最大的記錄的下一條記錄就是 最大記錄(最大的那條偽記錄) ,為了更形象的表示一下這個next_record起到的作用,我們用箭頭來替代一下next_record中的地址偏移量:

​ 從上面可以看出,最大記錄next_record 的值為0,代表著最大記錄的下一條記錄是不存在的,它也是鏈條中的最後一個節點

​ 當我們從頁中刪除一條數據後可以看看鏈表會發生那些變化:

mysql> DELETE FROM page_demo WHERE c1 = 2;
Query OK, 1 row affected (0.02 sec)

mysql>

​ 刪掉第2條記錄後的示意圖就是:

​ 從上面可以看到:

當我們刪除第二條記錄後,鏈表中的變化最明顯的就是各個節點之間的聯繫,它會把被刪除數據的上一條記錄和被刪除數據的下一條數據進行關聯(這條數據還是存在的,之前說的那個刪除標記別忘了哦)。

  • 第2條記錄並沒有從存儲空間中移除,而是把該條記錄的delete_mask值設置為1
  • 第2條記錄的next_record值變為了0,意味著該記錄沒有下一條記錄了。
  • 第1條記錄的next_record指向了第3條記錄。
  • 還有一點您可能忽略了,就是最大記錄n_owned值從5變成了4,關於這一點的變化我們稍後會詳細說明的。

所以得到:不論我們怎麼對頁中的記錄做增刪改操作,InnoDB始終會維護一條記錄的單鏈表,鏈表中的各個節點是按照主鍵值由小到大的順序連接起來的

​ 下麵我們再做一個操作,把刪除的記錄再次插入:

mysql> INSERT INTO page_demo VALUES(2, 200, 'bbbb');
Query OK, 1 row affected (0.00 sec)

mysql>

​ 我們來看看發生了什麼變化:

​ 很明顯的可以看到,InnoDB並沒有因為新記錄的插入而為它申請新的存儲空間,而是直接復用了原來被刪除記錄的存儲空間

Page Directory

​ 通過上面,我們知道到了頁中記錄是一個按照大小從下到大連續的單向鏈表,現在來想想,當我們根據主鍵查詢一條記錄的時候是怎樣進行的,我們來看看;

SELECT * FROM page_demo WHERE c1 = 3;

​ 上面是一條查詢語句,我們想想它的執行方式可能是:

​ 從最小記錄開始,沿著鏈表一直往後找,總有一天會找到(或者找不到),在找的時候還能投機取巧,因為鏈表中各個記錄的值是按照從小到大順序排列的,所以當鏈表的某個節點代表的記錄的主鍵值大於您想要查找的主鍵值時,如果這個時候還沒找到數據的話您就可以停止查找了(代表找不到),因為該節點後邊的節點的主鍵值都是依次遞增。

​ 上面的方式存在的問題就是,當頁中的存儲的記錄數量比較少的情況用起來也沒啥問題,但是如果一個頁中存儲了非常多的記錄,這麼查找對性能來說還是有損耗的,所以這個方式很笨啊。

​ 我們來看看InnoDB 的處理方式:InnoDB 的處理方式相當於我們平時看書的時候,想看那一章的時候不會傻到去一頁一頁的找,而是通過目錄去找到對應的頁數,直接就定位過去了。說說InnoDB 這樣處理的步驟吧:

​ 1. 將所有正常的記錄(包括最大和最小記錄,不包括標記為已刪除的記錄)劃分為幾個組。

​ 2. 每個組的最後一條記錄的頭信息中的n_owned屬性表示該組內共有幾條記錄。

​ 3. 將每個組的最後一條記錄的地址偏移量按順序存儲起來,每個地址偏移量也被稱為一個(英文名:Slot)。這些地址偏移量都會被存儲到靠近的尾部的地方,頁中存儲地址偏移量的部分也被稱為Page Directory

​ 比如說,現在表中有6條記錄,InnoDB會把它們分成兩組,第一組中只有一個最小記錄,第二組中是剩餘的5條記錄,看下邊的示意圖:

​ 從上面的圖中可以看到:

  • Page Directory中有兩個槽,也就是兩個組,槽0的值是90,代表最小記錄的地址偏移量;槽2的值是112,代表最大記錄的地址偏移量;
  • 註意記錄中的最小記錄和最大記錄,他們分別是1和5:
    • 最小記錄的n_owned 的值為1,代表著以最小記錄結尾的這個分組中只有1條記錄,就是最小記錄本身;
    • 同理,最大記錄的n_owned 的值為5,代表著以最大記錄結尾的這個分組中只有5條記錄,這5條記錄包括它本身,就是說除了它本身還有其它4條記錄;

​ 我們用圖來表示一下:

​ 上面的圖中為了方便理解,暫時沒管各條記錄在存儲設備上的排列方式了,單純從邏輯上看一下這些記錄和頁目錄的關係。真實的Page Directory 是在下麵的。

​ 再說說,為什麼最小記錄的n_owned值為1,而最大記錄的n_owned值為5呢?它們是怎麼分配的?

InnoDB 對每個分組中的記錄條數是有規定的,對於最小記錄所在的分組只能有 1 條記錄,最大記錄所在的分組擁有的記錄條數只能在 1~8 條之間,剩下的分組中記錄的條數範圍只能在是 4~8 條之間。所以分組是按照下邊的步驟進行的:

  • 初始情況下一個數據頁裡面只有最小記錄和最大記錄(偽記錄),它們屬於不同的分組,也就是兩個;
  • 之後插入的每一條記錄都會放到最大記錄所在的組,直到最大記錄所在組的記錄數等於8條
  • 當最大記錄所在組中的記錄數等於8條的時候,如果還有記錄插入的話,就會將最大記錄所在組平均分裂成2個組,這個時候最大記錄所在組就只剩下4條記錄,這裡再把這條記錄再放入最大記錄所在組;

​ 我們一口氣又往表中添加了12條記錄,現在就一共有16條正常的記錄了(包括最小和最大記錄),這些記錄被分成了5個組,如圖所示:

​ 上圖中,只保留了頭信息中的n_ownednext_record屬性,也省略了各個記錄之間的箭頭,沒畫不等於沒有!

​ 因為各個槽代表的記錄的主鍵值都是從小到大排序的,所以我們可以使用二分法來進行快速查找。4個槽的編號分別是:01234,所以初始情況下最低的槽就是low=0,最高的槽就是high=4。比方說我們想找主鍵值為5的記錄,現在我們再來看看查找一條記錄的步驟:

​ 1. 首先得到中間槽的位置:(0 + 4)/2 = 2 ,所以得到槽2,根據槽2的地址偏移量知道它的主鍵值是8,因為8>5,設置high=2low不變;

​ 2. 再次計算中間槽的位置:(0 + 2)/2 = 1 ,所以得到槽1,根據槽1的地址偏移量知道它的主鍵值是4, 因為4<5,設置low=1high不變;

​ 3. 因為high - low的值為1,所以確定主鍵值為5的記錄在槽1和槽2之間,接下來就是遍歷鏈表的查找了;

​ 所以在一個數據頁中查找指定主鍵值的記錄的過程分為兩步:

1. 通過二分法確定該記錄所在的槽。

2. 通過記錄的next_record屬性組成的鏈表遍歷查找該槽中的各個記錄。

​ 設計InnoDB的大叔們為了能得到一個數據頁中存儲的記錄的狀態信息,比如本頁中已經存儲了多少條記錄,第一條記錄的地址是什麼,Page Directory中存儲了多少個槽等等,特意在頁中定義了一個叫Page Header的部分,它是結構的第二部分,這個部分占用固定的56個位元組,專門存儲各種狀態信息,具體各個位元組都是幹嘛的看下表:

名稱 大小(單位:byte)
PAGE_N_DIR_SLOTS 2 在頁目錄中的槽數量
PAGE_HEAP_TOP 2 第一個記錄的地址
PAGE_N_HEAP 2 本頁中的記錄的數量(包括最小和最大記錄以及標記為刪除的記錄)
PAGE_FREE 2 指向可重用空間的地址(就是標記為刪除的記錄地址)
PAGE_GARBAGE 2 已刪除的位元組數,行記錄結構中delete_flag為1的記錄大小總數
PAGE_LAST_INSERT 2 最後插入記錄的位置
PAGE_DIRECTION 2 最後插入的方向
PAGE_N_DIRECTION 2 一個方向連續插入的記錄數量
PAGE_N_RECS 2 該頁中記錄的數量(不包括最小和最大記錄以及被標記為刪除的記錄)
PAGE_MAX_TRX_ID 2 修改當前頁的最大事務ID,該值僅在二級索引中定義
PAGE_LEVEL 2 當前頁在索引樹中的位置,高度
PAGE_INDEX_ID 8 索引ID,表示當前頁屬於哪個索引
PAGE_BTR 10 非葉節點所在段的segment header,僅在B+樹的Root頁定義
PAGE_LEVEL 10 B+樹所在段的segment header,僅在B+樹的Root頁定義

​ 如果大家認真看過前邊的文章,那麼大致能看明白這裡頭前邊一半左右的狀態信息的意思,剩下的狀態信息看不明白不要著急,飯要一口一口吃,東西要一點一點學。在這裡想強調以下PAGE_DIRECTIONPAGE_N_DIRECTION的意思。

  • PAGE_DIRECTION

    假如新插入的一條記錄的主鍵值比上一條記錄的主鍵值比上一條記錄大,我們說這條記錄的插入方向是右邊,反之則是左邊。用來表示最後一條記錄插入方向的狀態就是PAGE_DIRECTION

  • PAGE_N_DIRECTION

    假設連續幾次插入新記錄的方向都是一致的,InnoDB會把沿著同一個方向插入記錄的條數記下來,這個條數就用PAGE_N_DIRECTION這個狀態表示。當然,如果最後一條記錄的插入方向改變了的話,這個狀態的值會被清零重新統計。

File Header

​ 如果說Page Header描述的是內的各種狀態信息,比方說頁裡頭有多少個記錄了呀,有多少個槽了呀,那麼File Header描述的就是外的各種狀態信息,比方說這個頁的編號是多少,它的上一個頁、下一個頁是誰啦。File HeaderInnoDB頁的第一部分,這個部分占用固定的38個位元組,下邊我們看看這個部分的各個位元組都是代表啥意思吧:

名稱 大小(單位:byte) 描述
FIL_PAGE_SPACE_OR_CHKSUM 4 頁的校驗和(checksum值)
FIL_PAGE_OFFSET 4 頁號
FIL_PAGE_PREV 4 上一個頁的頁號
FIL_PAGE_NEXT 4 下一個頁的頁號
FIL_PAGE_LSN 8 最後被修改的日誌序列位置(英文名是:Log Sequence Number)
FIL_PAGE_TYPE 2 該頁的類型(之前我們說的是數據頁)
FIL_PAGE_FILE_FLUSH_LSN 8 僅在系統表空間的一個頁中定義,代表文件至少被更新到了該LSN值,獨立表空間中都是0
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 4 頁屬於哪個表空間

​ 對照著這個表格,我們看幾個目前比較重要的部分:

  • FIL_PAGE_SPACE_OR_CHKSUM

    這個代表當前頁面的校驗和(checksum)。啥是個校驗和?就是對於一個很長很長的位元組串來說,我們會通過某種演算法來計算一個值,這個值就稱為校驗和。這樣在比較兩個很長的位元組串之前先比較這兩個長位元組串的校驗和,如果校驗和都不一樣兩個長位元組串肯定是不同的(hashCode和equals),所以省去了直接比較兩個比較長的位元組串的時間損耗(和後面的File Trailer裡面的那個相對應,看到後面您就明白了)。

  • FIL_PAGE_OFFSET

    每一個都有一個單獨的頁號,就跟您的身份證號碼一樣,InnoDB通過頁號來可以唯一定位一個

  • FIL_PAGE_TYPE

    這個代表當前的類型,我們前邊說過,InnoDB為了不同的目的而把頁分為不同的類型,本集中介紹的其實都是存儲記錄的數據頁,其實還有很多別的類型的頁:

  • FIL_PAGE_PREVFIL_PAGE_NEXT

    一張表中可以有成千上萬條記錄,一個頁只有16KB,所以可能需要好多頁來存放數據,FIL_PAGE_PREVFIL_PAGE_NEXT就分別代表本頁的上一個和下一個頁的頁號(雙向鏈表)。

    Page Header 的其它屬性就不說了;

File Trailer

​ 對於這個部分,我的理解比較簡單,我們知道InnoDB 會把數據從記憶體刷新到磁碟,中間交互的單位是 ,但是我們想想,假如再刷新到磁碟的時候出現了問題,這樣的話怎麼辦呢?

​ 這就是File Trailer 作用,這個部分由8個位元組組成,可以分成2個小部分:

  • 前四個位元組代表頁的檢驗和:
    • 這個部分是和File Header中的校驗和相對應的。每當一個頁面在記憶體中修改了,在同步之前就要把它的校驗和算出來,因為File Header在頁面的前邊,所以校驗和會被首先同步到磁碟,當完全寫完時,校驗和也會被寫到頁的尾部,如果完全同步成功,則頁的首部和尾部的校驗和應該是一致的,反之意味著同步中間出了錯;
  • 後四個位元組代表日誌序列位置(LSN)
    • 這個部分也是為了校驗頁的完整性的,可以先不用管這個屬性。

總結

1. InnoDB為了不同的目的而設計了不同類型的頁,用於存放我們記錄的頁也叫做`數據頁`。
2. 一個數據頁可以被分為7個部分,分別是
 - `File Header`,表示文件頭,占固定的38位元組。
 - `Page Header`,表示頁里的一些狀態信息,占固定的56個位元組。
 - `Infimum + Supremum`,兩個虛擬的偽記錄,分別表示頁中的最小和最大記錄,占固定的`26`個位元組。
 - `User Records`:真實存儲我們插入的記錄的部分,大小不固定。
 - `Free Space`:頁中尚未使用的部分,大小不確定。
 - `Page Directory`:頁中的記錄相對位置,也就是各個槽在頁面中的地址偏移量,大小不固定,插入的記錄越多,這個部分占用的空間越多。
  1. 每個記錄的頭信息中都有一個next_record屬性,從而使頁中的所有記錄串聯成一個單向鏈表
  2. InnoDB會為把頁中的記錄劃分為若幹個組,每個組的最後一個記錄的地址偏移量作為一個,存放在Page Directory中,所以在一個頁中根據主鍵查找記錄是非常快的,分為兩步:
    • 通過二分法確定該記錄所在的槽。
    • 通過記錄的next_record屬性組成的鏈表遍歷查找該槽中的各個記錄。
  3. 每個數據頁的File Header部分都有上一個和下一個頁的編號,所以所有的數據頁會組成一個雙鏈表
  4. 為保證從記憶體中同步到磁碟的頁的完整性,在頁的首部和尾部都會存儲頁中數據的校驗和和LSN值,如果首部和尾部的校驗和和LSN值校驗不成功的話,就說明同步過程出現了問題。

最後

​ 本文的大部分內容都是參考並使用的原文中的內容,只是在中間加入了一些自己的理解,並希望把它更清楚的表達出來,大家也可以去看看原文:

InnoDB數據頁結構

​ 如果有地方理解的不對,還望指教。


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

-Advertisement-
Play Games
更多相關文章
  • 查看緩存的命令 free -m 清理緩存的命令 echo 1 > /proc/sys/vm/drop_caches echo 2 > /proc/sys/vm/drop_caches echo 3 > /proc/sys/vm/drop_caches echo 0 是不釋放緩存 echo 1 是釋放 ...
  • Windows Server(r12) 配置 MySQL 遠程訪問 工作主要為兩部分, 一部分是 Windows 防火牆, 一部分是 MySQL 自身 Windows 埠遠程訪問 其實就是在 Windows 防火牆中加一條特定埠的入站規則 1. 控制面板→系統和安全→Windows防火牆 2. ...
  • https://www.runoob.com/linux/Linux-intro.html 在上面鏈接學習的筆記: 1、通常伺服器使用:LAMP或LNMP(Linux+Nginx+MySQL+PHP)組合。 Nginx:Nginx是一個強大的高性能Web和反向代理服務,有很多非常優越特性: 在高併發 ...
  • 一、Oracle索引失效的原因 1使用否定關鍵字 !=, <> ,not in,not exist select * fromdrama where id <> 1,Mysql不會 2單獨使用不等式關鍵字 直接用>或<可能會失效,Mysql不會 3使用null關鍵字 is null或is not n ...
  • 1.安裝環境 操作系統:Red hat 6.5 記憶體:記憶體最低要求256M (使用:grep MemTotal /proc/meminfo 命令查看) 交換空間:SWAP交換空間大小根據記憶體大小決定(使用:grep SwapTotal /proc/meminfo 命令查看) 硬碟空間:不低於20G, ...
  • 第一步:先查看 mysql 服務是否停止 沒有停止就停止 第二步:用 管理員身份 運行 命令提示符 查看mysql 服務是否停止 : net stop mysql 卸載 : mysqld remove ...
  • 第一步:百度搜索 MySQL 點擊官網進入 或者複製鏈接進入下載頁面:https://downloads.mysql.com/archives/community/ 第二步:選擇自己需要的 版本和系統 下載 第三步:放入自己需要安裝的位置解壓 第四步:配置MySQL環境變數 註意解壓文件的路徑 第五 ...
  • 摘要:https://www.cnblogs.com/luoguixin/p/6291408.html 歡迎轉載,若需轉載請標明出處:https://www.cnblogs.com/luoguixin/p/6291408.html 如圖,如果通過一下代碼,添加服務後,使用 net start命令出現 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...