MySQL8.0 DDL原子性特性

来源:https://www.cnblogs.com/cchust/archive/2019/07/01/11099450.html
-Advertisement-
Play Games

1. DDL原子性概述 8.0之前並沒有統一的數據字典dd,server層和引擎層各有一套元數據,sever層的元數據包括(.frm,.opt,.par,.trg等),用於存儲表定義,分區表定義,觸發器定義等信息;innodb層也有自己一套元數據,包括表信息,索引信息等,這兩套元數據並沒有機制保證一 ...


1. DDL原子性概述

8.0之前並沒有統一的數據字典dd,server層和引擎層各有一套元數據,sever層的元數據包括(.frm,.opt,.par,.trg等),用於存儲表定義,分區表定義,觸發器定義等信息;innodb層也有自己一套元數據,包括表信息,索引信息等,這兩套元數據並沒有機制保證一致性,這就導致了在異常情況下可能存在元數據不一致問題,一種典型場景下,刪表操作,sever層的frm已經成功刪除了,但引擎層數據字典並沒有更新,導致再建重名錶失敗的問題。同樣的,比如drop table t1,t2;可能出現只刪除了t1,而t2仍然存在等問題。

8.0的一個重要工作是將數據字典統一,獨立了DD(數據字典)模塊,廢棄了server層的元數據,將innodb的元數據抽象出一條DD介面供server層和innnodb層公用。在DD的基礎上,引入了DDL的原子性特性,確保DDL操作要麼全做,要麼全不做的能力。實現這一套邏輯的關鍵點在於將ddl涉及到的修改,包括dd數據字典修改,引擎層的修改(創建文件,初始化tablespace,創建btree等)和寫binlog作為一個“事務”利用事務的原子性特點來保證ddl操作的原子性。

2.DDL原子性實現原理

實現原子性的關鍵在於確保dd數據字典修改,引擎層的修改和寫binlog是一個事務。MySQL已有的XA事務機制能有效保證DML事務和binlog的一致性。而ddl數據字典也是通過innodb引擎存儲,因此做到dd數據字典修改和binlog一致是容易的;那麼還需要解決的一個問題是,dd數據字典和引擎層修改的一致性,引擎層的修改並不都是記redo的,比如創建文件,rename文件名,或者清理cache等,無法簡單地通過XA機制解決問題,因此8.0還引入了一套DDL_LOG機制。具體而言,就是將不記redo的一些操作,通過記日誌的方式寫入到ddl_log表中,而這個表是innodb引擎表,通過保證ddl_log數據與dd數據字典修改達成一致,而最終解決dd數據字典修改,引擎層的修改和寫binlog一致性問題。

3.DD引入前後對比

image.png                                           

image.png                                           

4.DDL操作實現邏輯

引入ddl_log表後,ddl操作在原有的基礎上有一些變化,主要有兩點,一點是在執行ddl的過程中,會記錄ddl操作到ddl_log表中;另一點是新增了一個post_ddl階段,ddl事務提交後,做一些ddl的收尾動作,比如drop-table,真正的刪除物理文件是在post-ddl階段做的。post-ddl做的事情主要就是,讀取ddl-log內容,進行回放執行。ddl操作類型如下:

enum class Log_Type : uint32_t {

/** Smallest log type */
SMALLEST_LOG = 1,

/** Drop an index tree */
FREE_TREE_LOG = 1,

/** Delete a file */
DELETE_SPACE_LOG,

/** Rename a file */
RENAME_SPACE_LOG,

/** Drop the entry in innodb_dynamic_metadata */
DROP_LOG,

/** Rename table in dict cache. */
RENAME_TABLE_LOG,

/** Remove a table from dict cache */
REMOVE_CACHE_LOG,

/** Alter Encrypt a tablespace */
ALTER_ENCRYPT_TABLESPACE_LOG,

/** Biggest log type */
BIGGEST_LOG = ALTER_ENCRYPT_TABLESPACE_LOG
};

通過innodb_print_ddl_logs開關,可以看到ddl過程中寫入到innodb_ddl_log表中的內容。下麵會以幾個典型的ddl操作產生的ddl_log來說明如何保證ddl的原子性。

4.1 create table

語句:create table dd_tt(id int primary key, c1 int);  

 [InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=352, thread_id=23, space_id=71, old_file_path=./mysql/dd_tt.ibd]
 [InnoDB] DDL log delete : 352
 [InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=353, thread_id=23, table_id=1128, new_file_path=mysql/dd_tt]
 [InnoDB] DDL log delete : 353
 [InnoDB] DDL log insert : [DDL record: FREE, id=354, thread_id=23, space_id=71, index_id=231, page_no=4]
 [InnoDB] DDL log delete : 354
 [InnoDB] DDL log post ddl : begin for thread id : 23
 [InnoDB] DDL log post ddl : end for thread id : 23 

說明:

1.所有insert操作都是一個單獨的事務,對應的逆向delete操作是整個ddl事務的一部分。

2.insert操作記錄的是文件操作的逆向操作,比如建table_space,逆向操作就是delete_space_log。

3.如果ddl事務最終成功,那麼所有逆向delete操作也最終生效,ddl_log日誌被正常清理;如果ddl事務執行過程中失敗(比如實例crash),那麼delete操作回滾,ddl_log表中殘留3條insert_log,recover時,replay這些ddl_log,即可以清理ddl過程中產生的垃圾。

4.crash-recovery時,若binlog已經落盤,則對應的ddl事務處於prepare狀態,那麼最終事務要提交,ddl_log被清理乾凈;若binlog沒有落盤,則ddl事務需要回滾,ddl_log表中殘留3條記錄,在故障恢復結束後,需要replay這些記錄,實際上就是建文件,創建btree等逆向操作,確保回滾後是乾凈的。

4.2 drop table

語句:drop table dd_tt;

[InnoDB] DDL log insert : [DDL record: DROP, id=355, thread_id=23, table_id=1128]
[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=356, thread_id=23, space_id=71, old_file_path=./mysql/dd_tt.ibd]
[InnoDB] DDL log post ddl : begin for thread id : 23
[InnoDB] DDL log replay : [DDL record: DELETE SPACE, id=356, thread_id=23, space_id=71, old_file_path=./mysql/dd_tt.ibd]
[InnoDB] DDL log replay : [DDL record: DROP, id=355, thread_id=23, table_id=1128]
[InnoDB] DDL log post ddl : end for thread id : 23

說明:對於drop操作而言,執行過程中只是操作ddl_log,並不做真正的drop物理表操作。在post-ddl階段,會讀取ddl_log表中的記錄並replay,做真正的刪除動作。如果執行過程中crash了,那麼整個ddl事務會回滾,這其中也包含ddl_log中的內容也會回滾,那麼整個drop操作就相當於沒發生一樣。

4.3  add index

語句:alter table dd_tt add index idx_c1(c1);

[InnoDB] DDL log insert : [DDL record: FREE, id=360, thread_id=23, space_id=72, index_id=233, page_no=5]         
[InnoDB] DDL log delete : 360
[InnoDB] DDL log post ddl : begin for thread id : 23                                                             
[InnoDB] DDL log post ddl : end for thread id : 23   

說明: 建索引與建表類似,insert操作部分是一個事務,單獨提交,配套會記錄一個delete操作,這個操作是整個ddl事務的一部分,事務如果最終提交,那麼ddl-log內容被刪除;如果事務最終回滾,那麼ddl-log中會殘留一條FREE-log,通過replay則可以清理建好的索引,達到回滾的效果。  

4.4  drop index

語句:alter table dd_tt drop index idx_c1; 

[InnoDB] DDL log insert : [DDL record: FREE, id=361, thread_id=23, space_id=72, index_id=233, page_no=5]
[InnoDB] DDL log post ddl : begin for thread id : 23
[InnoDB] DDL log replay : [DDL record: FREE, id=361, thread_id=23, space_id=72, index_id=233, page_no=5]
[InnoDB] DDL log post ddl : end for thread id : 23 

說明:

與drop table類似,執行過程中只記錄日誌,在post-ddl階段才進行真正的刪除操作。

4.5 add column

語句:alter table dd_tt add column c2 int;

[InnoDB] DDL log post ddl : begin for thread id : 23
[InnoDB] DDL log post ddl : end for thread id : 23

說明:

8.0加列是instant-ddl,只修改元數據,與dml事務類似,不依賴ddl-log保證原子性。

4.6 drop column

語句:alter table dd_tt drop column c2;

語句分解:

1.prepare階段:

create table #sql-ib1129-2815969725;

[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=362, thread_id=23, space_id=73, old_file_path=./mysql/#sql-ib1129-2815969725.ibd] 
[InnoDB] DDL log delete : 362
[InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=363, thread_id=23, table_id=1130, new_file_path=mysql/#sql-ib1129-2815969725]     
[InnoDB] DDL log delete : 363
[InnoDB] DDL log insert : [DDL record: FREE, id=364, thread_id=23, space_id=73, index_id=234, page_no=4]                                  
[InnoDB] DDL log delete : 364

2.peform階段:nothing about ddl-log

3.commit階段:

3.1 alter table dd_tt rename to #sql-ib1130-2815969726;

[InnoDB] DDL log insert : [DDL record: DROP, id=365, thread_id=23, table_id=1129]                  
[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=366, thread_id=23, space_id=72, old_file_path=./mysql/#sql-ib1130-2815969726.ibd, new_file_path=./mysql/dd_tt.ibd] [InnoDB] DDL log delete : 366 [InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=367, thread_id=23, table_id=1129, old_file_path=mysql/#sql-ib1130-2815969726, new_file_path=mysql/dd_tt] [InnoDB] DDL log delete : 367

逆向操作:alter table mysql/#sql-ib1130-2815969726 rename to dd_tt;

3.2 alter table #sql-ib1129-2815969725 rename to dd_tt;

[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=368, thread_id=23, space_id=73, old_file_path=./mysql/dd_tt.ibd, new_file_path=./mysql/#sql-ib1129-2815969725.ibd]
[InnoDB] DDL log delete : 368
[InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=369, thread_id=23, table_id=1130, old_file_path=mysql/dd_tt, new_file_path=mysql/#sql-ib1129-2815969725]
[InnoDB] DDL log delete : 369

逆向操作:alter table dd_tt rename to mysql/#sql-ib1129-2815969725;

[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=368, thread_id=23, space_id=73, old_file_path=./mysql/dd_tt.ibd, new_file_path=./mysql/#sql-ib1129-2815969725.ibd]
[InnoDB] DDL log delete : 368
[InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=369, thread_id=23, table_id=1130, old_file_path=mysql/dd_tt, new_file_path=mysql/#sql-ib1129-2815969725]
[InnoDB] DDL log delete : 369

僅僅記錄操作,在post-ddl階段才做清理。                                                               

post-ddl階段: 

drop table #sql-ib1130-2815969726;

[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=368, thread_id=23, space_id=73, old_file_path=./mysql/dd_tt.ibd, new_file_path=./mysql/#sql-ib1129-2815969725.ibd]
[InnoDB] DDL log delete : 368
[InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=369, thread_id=23, table_id=1130, old_file_path=mysql/dd_tt, new_file_path=mysql/#sql-ib1129-2815969725]
[InnoDB] DDL log delete : 369

說明:drop column是copy類型的ddl,基本邏輯是新建一張臨時表,拷貝數據,最後再進行一次rename操作。主要包括4個階段:

1.prepare階段:建臨時表的過程與建表過程的ddl-log操作類似,insert-log作為單獨事務直接提交,delete-log是整個事務的一部分。

這個階段如果出現異常,ddl-log表中殘留了逆操作記錄,crash-recovery時,可以在replay實現清理。

2.peform階段: 拷貝數據結束,實現online-ddl邏輯。

3.拷貝數據結束後,需要進行rename交換表名操作。

   1)DROP,刪除臨時表

   2)RENAME SPACE/TABLE 將./mysql/#sql-ib1130-2815969726.ibd 重命名為dd_tt.idb

   3)REANAME SPACE/TABLE 將dd_tt.idb重名為/#sql-ib1129-2815969725.idb

   4)記錄刪除舊表sql-ib1130-2815969726.ibd操作,post-ddl階段做真正的刪除。

如果這個階段出現異常,同樣的insert-log單獨一個事務,delete作為整個事務的一部分,insert-log會殘留在ddl-log表中,通過replay可以做清理,還原dd_tt的數據,並清理臨時表#sql-ib1130-2815969726.ibd。

4.post-ddl階段:

  1).物理刪除舊文件./mysql/#sql-ib1130-2815969726.ibd

  2).清理mysql.innodb_dynamic_metadata中相關信息。

需要註意的是,由於ddl-log表存放的內容實際上逆向操作,所以搜集ddl-log時,實際上是逆序搜集回放的。

4.7 truncate table

語句:truncate table  dd_tt;

語句分解:

1.rename dd_tt to #sql-ib1130-2815969727;

[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=372, thread_id=23, space_id=73, old_file_path=./mysql/#sql-ib1130-2815969727.ibd, new_file_path=./mysql/dd_tt.ibd
[InnoDB] DDL log delete : 372

2.drop table #sql-ib1130-2815969727;

[InnoDB] DDL log insert : [DDL record: DROP, id=373, thread_id=23, table_id=1130]                 
[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=374, thread_id=23, space_id=73, old_file_path=./mysql/#sql-ib1130-2815969727.ibd] 

3.create table dd_tt;

[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=375, thread_id=23, space_id=74, old_file_path=./mysql/dd_tt.ibd]                  
[InnoDB] DDL log delete : 375
[InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=376, thread_id=23, table_id=1131, new_file_path=mysql/dd_tt]                      
[InnoDB] DDL log delete : 376
[InnoDB] DDL log insert : [DDL record: FREE, id=377, thread_id=23, space_id=74, index_id=235, page_no=4]                                  
[InnoDB] DDL log delete : 377
[InnoDB] DDL log post ddl : begin for thread id : 23                                                                                      
[InnoDB] DDL log replay : [DDL record: DELETE SPACE, id=374, thread_id=23, space_id=73, old_file_path=./mysql/#sql-ib1130-2815969727.ibd] 
[InnoDB] DDL log replay : [DDL record: DROP, id=373, thread_id=23, table_id=1130]               
[InnoDB] DDL log post ddl : end for thread id : 23 

說明: 

1.將dd_tt重命名為sql-ib1130-2815969727

2.標記sql-ib1130-2815969727表刪除,post-ddl階段才真正刪除

3.新建表dd_tt,同樣的insert操作是作為單獨事務提交,delete操作是整個事務的一部分,如果回滾,最終殘留了insert操作,通過replay動作清理。  

5.DDL操作代碼堆棧

5.1 create-table

Sql_cmd_create_table::execute
-->mysql_create_table
  -->mysql_create_table_no_lock
     -->create_table_impl
        -->rea_create_base_table
           -->ha_create_table
              -->ha_create
                 -->ha_innobase::create
                    -->innobase_basic_ddl::create_impl
                       -->create_table_info_t::create_table
                       {
                          ......
                       }

  -->trans_commit_implicit
     -->ha_commit_trans
        -->MYSQL_BIN_LOG::prepare
           -->ha_prepare_low  //所有事務引擎prepare
              {
                binlog_prepare
                innobase_xa_prepare
              }
        -->MYSQL_BIN_LOG::commit
           -->MYSQL_BIN_LOG::ordered_commit
              -->MYSQL_BIN_LOG::process_flush_stage_queue
                 -->MYSQL_BIN_LOG::flush_thread_caches
                    -->binlog_cache_mngr::flush
                       -->binlog_cache_data::flush
                          -->MYSQL_BIN_LOG::write_gtid
                             -->Log_event::write
                                -->MYSQL_BIN_LOG::Binlog_ofile::write  //寫binlog-gtid

                          -->MYSQL_BIN_LOG::write_cache
                             --> MYSQL_BIN_LOG::do_write_cache
                                 -->Binlog_cache_storage::copy_to
                                    -->stream_copy
                                       -->Binlog_event_writer::write
                                          -->MYSQL_BIN_LOG::Binlog_ofile::write //寫binlog-ddl語句
              -->MYSQL_BIN_LOG::sync_binlog_file
              -->MYSQL_BIN_LOG::process_commit_stage_queue
                 -->ha_commit_low
                    {
                       binlog_commit
                       innobase_commit
                       -->trx_commit_for_mysql
                          -->trx_commit
                              -->trx_commit_low
                                 -->trx_commit_in_memory
                                    -->trx_undo_insert_cleanup
                    }

  -->innobase_post_ddl(ht->post_ddl(thd))
     -->Log_DDL::post_ddl
        -->replay_by_thread_id
-->create_table_info_t::create_table
  -->create_table_def
     -->dict_mem_table_create //構造innodb記憶體是字典記憶體對象
     -->row_create_table_for_mysql
        -->dict_build_table_def
           -->dict_build_tablespace_for_table
              -->新建xxx.idb文件
              -->Log_DDL::write_delete_space_log
              {
                 -->Log_DDL::insert_delete_space_log
                    -->trx_start_internal //內部開啟事務,單獨提交。
                    -->構造DDL_Record(DELETE_SPACE_LOG)
                    -->DDL_Log_Table::insert(寫入物理B-Tree)
                 -->Log_DDL:delete_by_id //刪除ddl_log操作,作為ddl事務的一部分。
               }
              -->fil_ibd_create
              -->初始化segment,extent,page
     -->Log_DDL::write_remove_cache_log
        -->Log_DDL::insert_remove_cache_log
        -->Log_DDL::delete_by_id
  -->create_index(主表,二級索引)
     -->dict_create_index_tree_in_mem
        -->btr_create
        -->Log_DDL::write_free_tree_log
           -->Log_DDL::insert_free_tree_log
           -->Log_DDL:delete_by_id
crash-recovery -->ha_post_recover -->post_recover_handlerton -->innobase_post_recover -->Log_DDL::recover -->Log_DDL::replay_all -->Log_DDL::replay { replay_delete_space_log replay_remove_cache_log replay_free_tree_log ...... } -->delete_by_ids -->DDL_Log_Table::remove

5.2 drop table

mysql_rm_table
    -->mysql_rm_table_no_locks
       -->drop_base_table
           -->ha_delete_table
              -—>handler::ha_delete_table
                 -->ha_innobase::delete_table
                   -->innobase_basic_ddl::delete_impl
                      -->row_drop_table_for_mysql
                         -->Log_DDL::write_drop_log     // 記錄刪innodb_dynamic_metadata日誌
                         -—>Log_DDL::write_delete_space_log       // 記錄刪ibd日誌
           -->dd::drop_table
               -->dd::cache::Dictionary_client::drop<dd::Table>
                   -->dd::cache::Storage_adapter::drop<dd::Table>
                        -->dd::sdi::drop
       -->innobase_post_ddl
           -->Log_DDL::post_ddl
              -->Log_DDL::replay_by_thread_id
                   -->Log_DDL::replay
                      —>Log_DDL::replay_delete_space_log // post-ddl 真正刪除innodb_dynamic_metadata
                      —>Log_DDL::replay_drop_log         // post-ddl 真正刪除ibd
                   -->delete_by_ids
                      -->DDL_Log_Table::remove

drop table時,只記錄刪除動作日誌,這些日誌作為事務的整體的一部分,如果最終事務提交,那麼post_ddl階段會讀取日誌真正刪除;如果事務回滾,那麼ddl_log也會作為事務的一部分而回滾。

參考文檔

https://dev.mysql.com/worklog/task/?id=9045

https://dev.mysql.com/worklog/task/?id=9173

https://dev.mysql.com/worklog/task/?id=9175

https://dev.mysql.com/worklog/task/?id=9525

https://dev.mysql.com/worklog/task/?id=9536


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

-Advertisement-
Play Games
更多相關文章
  • --創建序列create sequence deptno_seqstart with 50increment by 10maxvalue 70cache 3; --為了方便演示,創建了一個和dept表結構相同的deptnew表create table deptnewasselect * from d ...
  • 1:如果同一臺伺服器上安裝有多個MongoDB實例,telegraf.conf 中關於 MongoDB 如何配置?配置數據在【INPUT PLUGINS的[[inputs.mongodb]]】部分。 單個實例配置 錯誤的多實例配置(例如兩個實例); 重啟服務,查看服務狀態,提示錯誤信息如下; 正確的 ...
  • 結果: ...
  • --創建簡單視圖--建立用於查詢員工號、姓名、工資的視圖。create view emp_viewasselect empno,ename,sal from emp; --查詢視圖select * from emp_view; --創建視圖時指定視圖的列的別名create view emp_view ...
  • 鏈接:https://pan.baidu.com/s/1fhfMtmwM_hOAGgYOfNYlkw提取碼:r53a 學習pl/sql的同學,可以看看這本書,講解的很詳細,從入門到精通,大家有什麼不懂得地方也可以互相交流 。。 裡面講解的都有操作截圖,案例,以及源碼 第1章 瞭解Oracle 1.1 ...
  • 一下是安裝ogg過程中遇到的問題和解決辦法,絕對良心乾貨,抽空會寫更詳細的安裝教程。更多精彩內容請 "點擊" OGG 00685 begin time prior to oldest log in log history https://blog.csdn.net/kiwi_kid/article/ ...
  • 數據過濾 SQL的數據過濾, 可以減少不必要的數據行, 從而可以達到提升查詢效率的效果. 比較運算符 在SQL中, 使用WHERE子句對條件進行篩選, 篩選的時候比較運算符是很重要. 上面的比較運算符, 並不是說每個DBMS都支持, 這裡主要說MySQL, 不支持(! )和(! 6000; // 查 ...
  • #1Demo: 1.創建表 2.創建存儲過程 3.創建每分鐘執行一次的定時器 4.查看job狀態表 #2Oracle定時器擴展 1. 2.定時參數 1)每分鐘執行 Interval => TRUNC(sysdate,’mi’) + 1 / (24*60) 2) 每天定時執行 例如:每天的凌晨2點執行 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...