[20230908]Oracle Index Range Scan with LIKE Condition on Wildcard '_'.txt

来源:https://www.cnblogs.com/lfree/archive/2023/09/21/17720923.html
-Advertisement-
Play Games

1. 為什麼需要加鎖 在日常生活中,如果你心情不好想靜靜,不想被比別人打擾,你就可以把自己關進房間里,並且反鎖。這就是生活中的加鎖。 同理,對於MySQL資料庫來說的話,一般的對象都是一個事務一個事務來說的。所以,如果一個事務內,一個SQL正在更新某條記錄,我們肯定不想它被別的事務影響到嘛?因此,數 ...


[20230908]Oracle Index Range Scan with LIKE Condition on Wildcard '_'.txt

--//昨天看鏈接:http://ksun-oracle.blogspot.com/2023/09/oracle-index-range-scan-with-like.html,當時一下子沒有反應過來,
--//作者這樣查詢怎麼會有這麼大區別呢?仔細看題目才明顯原來查詢的字元串裡面存在_,解析為任何字元,這樣索引的掃描範圍不同,
--//導致出現邏輯讀很大的不同。我重覆測試看看。

1.環境:
SCOTT@test01p> @ver1
PORT_STRING                    VERSION        BANNER                                                                               CON_ID
------------------------------ -------------- -------------------------------------------------------------------------------- ----------
IBMPC/WIN_NT64-9.1.0           12.2.0.1.0     Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production              0

2.建立測試例子:
--//drop table test_tab purge;

create table test_tab (
    id           number
   ,grp          number
   ,tstr         varchar2(14)
   ,name1        varchar2(100)
   ,name2        varchar2(100)
   ,name3        varchar2(100)
);

create unique index test_tab#p on test_tab(id);
alter table test_tab add constraint test_tab#p primary key (id);
create index test_tab#i#name1 on test_tab (name1);
create index test_tab#i#name2 on test_tab (name2);
create index test_tab#i#name3 on test_tab (name3);
--//作者建立索引的風格與我的不同。
 
insert into test_tab
with sq as (
select level id, mod(level, 300) grp
      ,to_char((date'2021-11-22' + rownum/86400), 'YYYYMMDDHH24MISS')           ts
      ,decode(mod(level, 3), 0, 'ONE_PART', 1, 'TWO_PART', 2, 'THREE_PART')     part
  from dual connect by level <= 3*1e5)
select id, grp, ts
      ,part ||'.'||grp  ||'.'||ts     name1
      ,grp  ||'.'||part ||'.'||ts     name2
      ,ts   ||'.'||part ||'.'||grp    name3
 from sq;
commit;

exec dbms_stats.gather_table_stats('', 'TEST_TAB', cascade=>true);

col name1 for a30 new_value n1
col name2 for a30 new_value n2
col name3 for a30 new_value n3
SCOTT@test01p> select id, name1, name2, name3 from test_tab m where id = trunc(dbms_random.value(1, 3*1e5));

        ID NAME1                          NAME2                          NAME3
---------- ------------------------------ ------------------------------ ------------------------------
    244018 TWO_PART.118.20211124194658    118.TWO_PART.20211124194658    20211124194658.TWO_PART.118
--//主:3個字元串3種風格。最後一個順序遞增,並且字元串_出現的位置最靠後。

select count(*)  lect count(*)
      ,count(distinct substr(name1, 1, instr(name1, '_')-1)) name1_prefix_cntx_cnt
      ,count(distinct substr(name2, 1, instr(name2, '_')-1)) name2_prefix_cntx_cnt
      ,count(distinct substr(name3, 1, instr(name3, '_')-1)) name3_prefix_cntx_cnt
  from test_tab;
  COUNT(*) NAME1_PREFIX_CNT NAME2_PREFIX_CNT NAME3_PREFIX_CNT
---------- ---------------- ---------------- ----------------
    300000                3              300           300000
--//字元'_'出現的位置前面重覆的部分計數如上。

create or replace procedure test_tab_proc (p_case number, p_cnt number) as
  l_start       number;
  l_start_cr    number;
  l_end_cr      number;
  l_name1       varchar2(100);
  l_name2       varchar2(100);
  l_name3       varchar2(100);
  l_ret         varchar2(200);
begin
  select name1, name2, name3 into l_name1, l_name2, l_name3
    from test_tab where id = trunc(dbms_random.value(1, 3*1e5));

  l_start  := dbms_utility.get_time;
  select value into l_start_cr from v$mystat s, v$statname n where s.statistic#=n.statistic# and name = 'consistent gets';
  case p_case
    when 1 then
      for i in 1..p_cnt loop
        select  /*+ index_rs_asc(t (name1)) */ name1 into l_ret from test_tab t where name1 like l_name1;
      end loop;
      dbms_output.put_line('--------- Index: name1 like '||l_name1||' --------- ');
    when 2 then
      for i in 1..p_cnt loop
        select  /*+ index_rs_asc(t (name2)) */ name2 into l_ret from test_tab t where name2 like l_name2;
      end loop;
      dbms_output.put_line('--------- Index: name2 like '||l_name2||' --------- ');
    when 3 then
      for i in 1..p_cnt loop
        select  /*+ index_rs_asc(t (name3)) */ name3 into l_ret from test_tab t where name3 like l_name3;
      end loop;
      dbms_output.put_line('--------- Index: name3 like '||l_name3||' --------- ');
    end case;
    select value into l_end_cr from v$mystat s, v$statname n where s.statistic#=n.statistic# and name = 'consistent gets';
    dbms_output.put_line('Total Elapsed MS = '||round((dbms_utility.get_time-l_start)*10)||
                       ', Total CR gets= '    ||(l_end_cr-l_start_cr)||
                       ', Per Exec MS = '     ||round((dbms_utility.get_time-l_start)*10/p_cnt, 2)||
                       ', Per Exec CR gets ='||round((l_end_cr-l_start_cr)/p_cnt));
end;
/

3.測試:
SCOTT@test01p> @ init
SCOTT@test01p> set serveroutput on
SCOTT@test01p> alter session set tracefile_identifier = 'sql_trc_1';
Session altered.

SCOTT@test01p> alter session set events '10046 trace name context forever, level 12';
Session altered.

SCOTT@test01p> exec test_tab_proc(1, 100);
--------- Index: name1 like THREE_PART.167.20211122130747 ---------
Total Elapsed MS = 4210, Total CR gets= 97503, Per Exec MS = 42.1, Per Exec CR gets = 975

PL/SQL procedure successfully completed.

SCOTT@test01p> exec test_tab_proc(2, 100);
--------- Index: name2 like 49.TWO_PART.20211122044049 ---------
Total Elapsed MS = 100, Total CR gets= 1203, Per Exec MS = 1, Per Exec CR gets = 12

PL/SQL procedure successfully completed.

SCOTT@test01p> exec test_tab_proc(3, 100);
--------- Index: name3 like 20211122023738.THREE_PART.158 ---------
Total Elapsed MS = 40, Total CR gets= 303, Per Exec MS = .4, Per Exec CR gets = 3
PL/SQL procedure successfully completed.

SCOTT@test01p> set serveroutput off
--//與他的測試基本一致。

SCOTT@test01p> select index_name, clustering_factor, leaf_blocks, blevel from dba_indexes v where table_name = 'TEST_TAB';
INDEX_NAME           CLUSTERING_FACTOR LEAF_BLOCKS     BLEVEL
-------------------- ----------------- ----------- ----------
TEST_TAB#P                        4717         562          1
TEST_TAB#I#NAME1                299999        2765          2
TEST_TAB#I#NAME2                299999        2773          2
TEST_TAB#I#NAME3                  4717        1478          2

--//作者的解析:
Before '_', there are 3 different values ('ONE','TWO','THREE'). So name1 is divided into three parts. TEST_TAB#I#NAME1
has 2766 leaf blocks, TEST_TAB#I#NAME1 index range scan makes 2766/3, which is 902 CR gets per execution (it performs
like an index partition full scan).

4. Index Meta Data
SCOTT@test01p> select object_name, object_id, to_char(object_id, 'xxxxxxxx') object_id_hex from dba_objects t where object_name like 'TEST_TAB#%';
OBJECT_NAME                     OBJECT_ID OBJECT_ID
------------------------------ ---------- ---------
TEST_TAB#I#NAME1                    30257      7631
TEST_TAB#I#NAME2                    30258      7632
TEST_TAB#I#NAME3                    30259      7633
TEST_TAB#P                          30256      7630

SCOTT@test01p> select column_name, avg_col_len from  dba_tab_columns where table_name = 'TEST_TAB';
COLUMN_NAME          AVG_COL_LEN
-------------------- -----------
ID                             5
GRP                            4
TSTR                          15
NAME1                         29
NAME2                         29
NAME3                         29
6 rows selected.

SCOTT@test01p> select segment_name, header_file, header_block from dba_segments t where segment_name like 'TEST_TAB#%';
SEGMENT_NAME                   HEADER_FILE HEADER_BLOCK
------------------------------ ----------- ------------
TEST_TAB#P                              11         3874
TEST_TAB#I#NAME1                        11         3882
TEST_TAB#I#NAME2                        11         3890
TEST_TAB#I#NAME3                        11         3898

alter session set events 'immediate trace name treedump level 30257';
alter session set events 'immediate trace name treedump level 30258';
alter session set events 'immediate trace name treedump level 30259';

--// index TEST_TAB#I#NAME1
----- begin tree dump
branch: 0x2c00f2b 46141227 (0: nrow: 16, level: 2)
   branch: 0x2c010fe 46141694 (-1: nrow: 162, level: 1)
      leaf: 0x2c00f2c 46141228 (-1: row:107.107 avs:4041)
      leaf: 0x2c01d30 46144816 (0: row:108.108 avs:4004)
....
      leaf: 0x2c056bb 46159547 (164: row:136.136 avs:2832)
----- end tree dump

--// index TEST_TAB#I#NAME2
----- begin tree dump
branch: 0x2c00f33 46141235 (0: nrow: 17, level: 2)
   branch: 0x2c011d3 46141907 (-1: nrow: 149, level: 1)
      leaf: 0x2c00f34 46141236 (-1: row:107.107 avs:4041)
      leaf: 0x2c01c7f 46144639 (0: row:108.108 avs:4004)
...
      leaf: 0x2c056bb 46159547 (164: row:136.136 avs:2832)
----- end tree dump

--// index TEST_TAB#I#NAME3
----- begin tree dump
branch: 0x2c00f3b 46141243 (0: nrow: 5, level: 2)
   branch: 0x2c024fb 46146811 (-1: nrow: 368, level: 1)
      leaf: 0x2c00f3c 46141244 (-1: row:204.204 avs:16)
...
      leaf: 0x2c05b0f 46160655 (2: row:203.203 avs:0)
      leaf: 0x2c05b10 46160656 (3: row:51.51 avs:5979)
----- end tree dump
--//這是因為前面2個索引50-50 分裂,而最後遞增順序插入,這樣90-10分裂(實際上最後那條插入的條目在另外的新索引塊上)

-- index range scan gets index TEST_TAB#I#NAME1 blocks in ordered read (db file sequential read').
--   index range scan first reads index blocks from root block to the left most satisfied first leaf block along branch blocks,
--   then reads from first found leaf blocks till last satisfied leaf block (which are linked with each one points to next one).
--   One block per read, logically sequential.

-- index fast full scan gets index TEST_TAB#I#NAME1 blocks in unordered read ('db file scattered read').
--   index fast full scan reads all index blocks (brach/leaf) like full table scan.
--   Multiple blocks per read without considering any order.

5.如果過程改寫如下,應該基本相同時間完成。
create or replace procedure test_tab_proc1 (p_case number, p_cnt number) as
  l_start       number;
  l_start_cr    number;
  l_end_cr      number;
  l_name1       varchar2(100);
  l_name2       varchar2(100);
  l_name3       varchar2(100);
  l_ret         varchar2(200);
begin
  select replace(name1,'_','\_'), replace(name2,'_','\_'), replace(name3,'_','\_') into l_name1, l_name2, l_name3
    from test_tab where id = trunc(dbms_random.value(1, 3*1e5));

  l_start  := dbms_utility.get_time;
  select value into l_start_cr from v$mystat s, v$statname n where s.statistic#=n.statistic# and name = 'consistent gets';
  case p_case
    when 1 then
      for i in 1..p_cnt loop
        select  /*+ index_rs_asc(t (name1)) */ name1 into l_ret from test_tab t where name1 like l_name1 escape '\' ;
      end loop;
      dbms_output.put_line('--------- Index: name1 like '||l_name1||' --------- ');
    when 2 then
      for i in 1..p_cnt loop
        select  /*+ index_rs_asc(t (name2)) */ name2 into l_ret from test_tab t where name2 like l_name2 escape '\' ;
      end loop;
      dbms_output.put_line('--------- Index: name2 like '||l_name2||' --------- ');
    when 3 then
      for i in 1..p_cnt loop
        select  /*+ index_rs_asc(t (name3)) */ name3 into l_ret from test_tab t where name3 like l_name3 escape '\' ;
      end loop;
      dbms_output.put_line('--------- Index: name3 like '||l_name3||' --------- ');
    end case;
    select value into l_end_cr from v$mystat s, v$statname n where s.statistic#=n.statistic# and name = 'consistent gets';
    dbms_output.put_line('Total Elapsed MS = '||round((dbms_utility.get_time-l_start)*10)||
                       ', Total CR gets= '    ||(l_end_cr-l_start_cr)||
                       ', Per Exec MS = '     ||round((dbms_utility.get_time-l_start)*10/p_cnt, 2)||
                       ', Per Exec CR gets ='||round((l_end_cr-l_start_cr)/p_cnt));
end;
/
--//註意帶入的字元串以及最後的escape.

SCOTT@test01p> set serveroutput on
SCOTT@test01p> exec test_tab_proc1(1, 100);
--------- Index: name1 like ONE\_PART.153.20211122030733 ---------
Total Elapsed MS = 140, Total CR gets= 318, Per Exec MS = 1.4, Per Exec CR gets = 3

PL/SQL procedure successfully completed.

SCOTT@test01p> exec test_tab_proc1(2, 100);
--------- Index: name2 like 16.TWO\_PART.20211123054516 ---------
Total Elapsed MS = 90, Total CR gets= 303, Per Exec MS = .9, Per Exec CR gets = 3

PL/SQL procedure successfully completed.

SCOTT@test01p> exec test_tab_proc1(3, 100);
--------- Index: name3 like 20211123182827.ONE\_PART.207 ---------
Total Elapsed MS = 70, Total CR gets= 303, Per Exec MS = .7, Per Exec CR gets = 3
PL/SQL procedure successfully completed.

SCOTT@test01p> set serveroutput off
--//3個測試基本平均都是3個邏輯讀完成。

6.提醒註意:
--//這個測試最大的提示就是當採用like查詢時,查詢使用的綁定變數的字元串有_字元,邏輯讀可能出現異常,在實際的應用中應該引
--//起註意。

--//看一個具體的例子:
col name1 for a30 new_value n1
col name2 for a30 new_value n2
col name3 for a30 new_value n3

SCOTT@test01p> select id, name1, name2, name3 from test_tab m where id = 1;
        ID NAME1                          NAME2                          NAME3
---------- ------------------------------ ------------------------------ ------------------------------
         1 TWO_PART.1.20211122000001      1.TWO_PART.20211122000001      20211122000001.TWO_PART.1

SCOTT@test01p> variable v_name1 varchar2(100);
SCOTT@test01p> exec :v_name1 :='TWO_PART.1.20211122000001'
PL/SQL procedure successfully completed.

SCOTT@test01p> @ sl all
alter session set statistics_level = all;
Session altered.

SCOTT@test01p> select id, name1, name2, name3 from test_tab m where name1 like :v_name1;
        ID NAME1                          NAME2                          NAME3
---------- ------------------------------ ------------------------------ ------------------------------
         1 TWO_PART.1.20211122000001      1.TWO_PART.20211122000001      20211122000001.TWO_PART.1

SCOTT@test01p> @ dpc '' '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  6952nwup67x5k, child number 0
-------------------------------------
select id, name1, name2, name3 from test_tab m where name1 like :v_name1
Plan hash value: 2100491709
--------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name             | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                  |      1 |        |       |     5 (100)|          |      1 |00:00:00.06 |     898 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TEST_TAB         |      1 |      1 |    92 |     5   (0)| 00:00:01 |      1 |00:00:00.06 |     898 |
|*  2 |   INDEX RANGE SCAN                  | TEST_TAB#I#NAME1 |      1 |      1 |       |     3   (0)| 00:00:01 |      1 |00:00:00.06 |     897 |
--------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / M@SEL$1
   2 - SEL$1 / M@SEL$1
Peeked Binds (identified by position):
--------------------------------------
   1 - :1 (VARCHAR2(30), CSID=852): 'TWO_PART.1.20211122000001'
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("NAME1" LIKE :V_NAME1)
       filter("NAME1" LIKE :V_NAME1)
31 rows selected.
--//這樣邏輯讀高達898.

SCOTT@test01p> exec :v_name1 :='TWO\_PART.1.20211122000001'
PL/SQL procedure successfully completed.

SCOTT@test01p> select id, name1, name2, name3 from test_tab m where name1 like :v_name1 escape '\';
        ID NAME1                          NAME2                          NAME3
---------- ------------------------------ ------------------------------ ------------------------------
         1 TWO_PART.1.20211122000001      1.TWO_PART.20211122000001      20211122000001.TWO_PART.1

SCOTT@test01p> @ dpc '' '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  byw8j1gn2d0yv, child number 0
-------------------------------------
select id, name1, name2, name3 from test_tab m where name1 like
:v_name1 escape '\'
Plan hash value: 2100491709
--------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name             | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                  |      1 |        |       |     5 (100)|          |      1 |00:00:00.01 |       5 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TEST_TAB         |      1 |      1 |    92 |     5   (0)| 00:00:01 |      1 |00:00:00.01 |       5 |
|*  2 |   INDEX RANGE SCAN                  | TEST_TAB#I#NAME1 |      1 |      1 |       |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       4 |
--------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / M@SEL$1
   2 - SEL$1 / M@SEL$1
Peeked Binds (identified by position):
--------------------------------------
   1 - :1 (VARCHAR2(30), CSID=852): 'TWO\_PART.1.20211122000001'
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("NAME1" LIKE :V_NAME1 ESCAPE '\')
       filter("NAME1" LIKE :V_NAME1 ESCAPE '\')
32 rows selected.
--//正常的業務很少寫like語句使用escape '\'的,即使寫了帶入的參數也很少寫成'TWO\_PART.1.20211122000001',要事先處理。
--//我估計許多開發可能根本不知道這個細節。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Dockcer上傳hub和配置國內鏡像源 1.Dockcer上傳hub 要將本地的Docker鏡像上傳到Docker鏡像倉庫,可以按照以下步驟操作: linux環境 1.創建用戶 首先,確保你已經在Docker Hub或其他容器註冊表上創建了一個賬戶,並且擁有上傳鏡像的許可權。 2.登錄用戶 在命令行 ...
  • 在.Net Framework時代,我們生成驗證碼大多都是用System.Drawing。 在.Net 6中使用也是沒有問題的。 但是,System.Drawing卻依賴於Windows GDI+。 為了實現跨平臺,我陷入了沉思!! 微軟推薦使用SkiaSharp 進行替代,所以就開始了,踩坑之旅 ...
  • 上篇文章講述了C#集合知識點,本文將介紹C#異常處理知識點。異常處理是.NET開發中至關重要的一部分,它允許開發者在程式出現錯誤或不正常情況時採取適當的措施,從而提高應用程式的穩定性和可靠性。本文將介紹C#異常處理知識點,異常的基本概念略過,請查看官網。 1、自定義異常 開發者可以創建自定義異常類, ...
  • 在學習C#中的記錄類型時,對出現的Equals和ReferenceEquals得到的不同結果表示不理解,隨即進行相關資料查找。 值類型 == : 比較兩者的“內容”是否相同,即“值”是否一樣Equals:比較兩者的“內容”是否相同,即“值”是否一樣ReferenceEquals:返回false,因為 ...
  • shell批量執行命令與文件傳輸腳本 需求: 對未進行主機信任操作的伺服器進行批量操作 實現: 由於ssh只能在交互模式中輸入伺服器密碼進行登錄登操作,不便於進行大批量伺服器進行巡檢或日誌採集。sshpass恰好又解決了這個問題,使用ssh -p passwd可以實現命令行輸入密碼操作,便於進行規模 ...
  • 一、目錄介紹 /:表示的是根的意思 /bin:(binary)存放的是一些二進位文件,但是在Linux中二進位文件是可以被執行的。這個目錄中的命令文件是給普通用戶使用(非超級管理員用戶)。 /etc:Linux下所有的配置文件都會存放到etc目錄。 /home:是所有非root用戶家目錄的一個集合。 ...
  • 測試伺服器CPU單核及多核SuperPI圓周率測試real和user值,SuperPI是利用CPU的浮點運算能力來計算出π(圓周率),測試系統穩定性和測試CPU計算完後特定位數圓周率所需的時間;及Unixbench單核及多核測試Index得分,測試方法如下: 類型 預期結果 測試步驟 SuperPI ...
  • 可擴展性對於物聯網管理系統的設計和開發非常重要,它直接影響著系統的性能、可靠性和能耗等方面,是評估一個系統優劣的重要因素之一。可擴展性對物聯網管理系統的影響主要體現在以下幾個方面: ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...