MongoDB索引操作和執行計劃Explain()詳解

来源:https://www.cnblogs.com/zaoyu/archive/2023/08/11/17623197.html
-Advertisement-
Play Games

GaussDB(DWS)支持多種相容模式,為了相容目標資料庫,各模式之間或多或少存在一些行為差異。這裡分享一個mysql相容模式下的表達式函數因不同寫法引發的結果差異案例。 ...


主要是講下Mongodb的索引的查看、創建、刪除、類型說明,還有就是Explain執行計劃的解釋說明。 

 

可以轉載,但請註明出處。  

之前自己寫的SpringBoot整合MongoDB的聚合查詢操作,感興趣的可以點擊查閱。

https://www.cnblogs.com/zaoyu/p/springboot-mongodb.html

數組相關的操作

https://www.cnblogs.com/zaoyu/p/mongodb_array_operator.html

 

一、索引操作

說明,下麵的內容舉例時,以"dailyTrip"collection為例。 欄位內容如下:

{
    "_id" : ObjectId("63ec5a971ddbe429cbeeffe3"),  // object id
    "car_type" : "Gett",  // string
    "date" : ISODate("2016-04-01T00:00:00.000+0000"),  //ISODate
    "trips" : 0.0,  // number
    "monthly" : "NA", // string
    "parent_type" : "Ride-hailing apps", // string
    "monthly_is_estimated" : true, // boolean
    "geo" : {  // object 
        "$concat" : [  // array 
            "$parent_type",
            "$grouping"
        ]
    }
}

執行看看有5.17萬條數據

1. 索引類型

  • 單列索引: 在一個欄位上創建的索引。

  • 複合索引:由多個欄位組合一起創建的索引。使用的時候要註意最左首碼原則,避免索引無法命中(失效)。

  • 文本索引(全文索引)和空間索引(GEO索引),不常用,這裡不講,具體自行網上查詢。

 

2. 查看

語法:

db.Collection.getIndexs(); 

返回內容說明:

[  // 返回一個數組,內容是所有索引
    {
        "v" : 2.0,  // 索引版本,可忽略
        "key" : {  // 索引加在哪個欄位,以及排序
            "_id" : 1.0  // _id 是欄位, 1.0是正序(升序), -1.0是倒序(倒序)
        },
        "name" : "_id_"  // 索引名,如果添加的時候沒有指定,Mongo會自動生成。 
    }
]

舉例:

可以看到返回一個name為 _id_的索引。 作用於 _id上, 升序排列。

3. 創建

語法:

db.collection.createIndex(keys, options)

// 組合索引 
db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )  // 其中 <fieldN>是欄位名 <typeN>是排序

註意:3.0.0 版本之前創建索引方法為 db.collection.ensureIndex()。 5.0之後ensureIndex() 已被移除。

舉例:

// 創建單欄位索引
db.dailyTrip.createIndex({"car_type":1});

// 返回結果 
{
    "numIndexesBefore" : 1.0,  // 新建索引前的索引數
    "numIndexesAfter" : 2.0,  // 新建索引後的索引數  
    "createdCollectionAutomatically" : false,   // 一般情況下,如果該集合存在,這裡就是返回false。如果指定的集合不存在,MongoDB 預設情況下會自動創建該集合,併在該集合上創建索引,此時返回true。
    "ok" : 1.0  // 創建結果 1.0則是成功。返回0則說明報錯了。
}

// 如果創建的欄位的索引已經存在,則返回如下
{
    "numIndexesBefore" : 2.0,
    "numIndexesAfter" : 2.0,
    "note" : "all indexes already exist", // 提示已經存在
    "ok" : 1.0
}

// 創建複合索引
db.dailyTrip.createIndex({"parent_type":1, "car_type":1})
// 也可以指定自定義索引名
db.dailyTrip.createIndex({"parent_type":1, "car_type":1}, { name: "inx_parantType_carType" })  // 這裡自己試驗

// 來自官方文檔 https://www.mongodb.com/docs/manual/reference/command/createIndexes/ 

 

 創建完畢後,再次查看當前索引情況

 

4. 刪除

語法:

刪除指定集合中所有索引:
db.collection.dropIndexes();
刪除指定集合中的具體索引:
db.collection.dropIndex("索引名稱");

舉例:

// 刪除指定索引
db.dailyTrip.dropIndex("car_type_1");

// 返回結果
{
    "nIndexesWas" : 3.0, // 指示在刪除索引之前集合中存在的索引數量。
    "ok" : 1.0
}

// 刪除所有,自行嘗試。 註意, 主鍵_id的索引是不會也不能被刪除的。 
db.collection.dropIndexes();

 

5. 修改

無法直接修改,如果需要調整索引欄位、名稱、組合索引的順序,需要先刪除原來索引,再新建索引。

6. 索引使用原則

  1. 選擇經常被查詢的欄位來創建索引。那些在查詢條件、排序和聚合操作中頻繁出現的欄位是最佳選擇。

  2. 選擇合適的、常用作查詢條件的欄位,綜合判斷是否可以使用複合索引,如果可以優先複合索引。 比如多數情況是按照欄位A、B、C或者A、B條件去查詢,那麼可以優先考慮基於欄位A、B、C創建複合索引。 使用複合索引來覆蓋多個查詢條件,以減少索引的數量。

  3. 儘量使用索引覆蓋,和MySQL中的索引覆蓋是類似的。 覆蓋索引是指索引包含了查詢所需的欄位,從而避免了查詢需要訪問實際文檔。這可以提高查詢性能,減少IO操作。 其實這裡和上面的複合索引是相關的。

  4. 其他的還有包括使用Explain去查看執行計劃、定期監控性能、刪除冗餘無用的索引之類的,不一而足。 上面是基於實際開發經驗得出的常用手段原則。

     

7. 索引失效的情況

  1. 查詢條件不滿足索引首碼: 例如,複合索引是 { a: 1, b: 1 },而查詢條件只包含 { b: 1 },則該複合索引將無法被使用。

  2. 數據類型不匹配: 如果查詢條件中的數據類型與索引欄位的數據類型不匹配,索引可能無法被使用。例如,如果索引欄位是數字類型,而查詢條件中使用了字元串類型的值,索引將無法被使用。

    // 類似Mysql中的隱式轉換會使索引失效。

  3. 索引選擇性低: 索引的選擇性是指索引中不同值的唯一性程度。如果索引的選擇性很低,即索引中的值幾乎都相同,那麼使用該索引可能不會帶來明顯的性能提升。

    // 這裡其實是說索引列的值的區分度,如果重覆度過高,那麼使用索引的性能可能不如不用,索引底層優化器可能不選擇使用索引。假如欄位gender只有2個值,male和female,其中一半數據是male,另一半是female,此時用gender索引,還不如不用。

  4. 使用不支持的操作符: 某些查詢操作符可能無法使用索引。例如,正則表達式查詢、模糊查詢(如 $text 操作符)等可能無法充分利用索引。

  5. 數據量較小: 當集合中的數據量較小時,MongoDB 可能會選擇全表掃描而不使用索引,因為全表掃描可能更快。

    // 類似mysql中,一般建議小於1000條就不加索引,因為索引有額外開銷。

  6. 索引過期或損壞: 如果索引過期或損壞,MongoDB 將無法使用該索引。

  7. 索引被禁用: 如果在查詢時禁用了索引,或者索引的存儲引擎不支持該查詢,索引將無法被使用。

  8. 索引尺寸過大: 如果索引的尺寸超過了 MongoDB 的限制,該索引可能無法被使用。

使用 explain() 方法可以查看查詢計劃和索引使用情況,幫助識別索引失效的原因,併進行相應的優化。

接下來對Explain方法做說明解釋。

 

 

二、執行計劃 Explain()

1. 什麼是Explain()執行計劃?

  1. explain() 是一個用於查詢解釋和性能分析的方法(函數)。

  2. 可以在find()aggregate()count() 等查詢操作的結果上調用 explain() 方法。

  3. 其作用:

    1. 有助於瞭解查詢的執行計劃、索引使用情況以及查詢性能的相關指標。

    2. 調用該方法後。MongoDB 會返回一條包含查詢執行計劃的文檔(結果)(具體說明如下),其中包含了查詢優化器的決策、索引使用情況、掃描文檔數量等信息。通過分析 explain() 返回的執行計劃,可以確定是否使用了適當的索引,是否存在潛在的性能問題,並根據需要進行索引優化、查詢重寫等操作,以提高查詢性能。

  4. 執行計劃的返回結果:

    • queryPlanner:查詢優化器的決策和統計信息。

    • winningPlan:優化器選擇的最佳執行計劃。

    • executionStats:執行計劃的統計信息,如掃描的文檔數量、查詢時間等。

    • serverInfo:MongoDB 伺服器的信息。

註意:explain()` 的輸出非常詳細,包含了大量的信息。因此,它在調試和優化查詢時非常有用,但在生產環境中不應該頻繁地使用,以避免對性能產生負面影響。

官方文檔:

2. Explain的語法和使用

語法
// 普通的語句,直接在語句後面加上explain,也可以挪到find的前面。  
db.collection.find({}).explain(<optional verbosity mode>);

// 對聚合pipeline的執行計劃分析,explain放前面 
db.collection.explain(<optional verbosity mode>).aggregate([]);

<optional verbosity mode> 是可選的輸出模式。
如果什麼都不寫,比如  db.collection.find({"name"”":"onepiece"}).explain();  那麼mongo會走預設模式 "queryPlanner"。
如果要指定模式,則直接加上,比如  db.collection.find({"name"”":"onepiece"}).explain("executionStats")

explain有三種模式:

  1. queryPlanner (預設) : 只列出所有可能執行的方案,不會執行實際的語句,顯示已經勝出的方案winningPlan(最佳查詢計劃)。

  2. executionStats : 只執行winningPlan方案,並輸出結果。

  3. allPlansExecution :執行所有的方案,並輸出結果。

關於explain的參數可以參考官網: https://www.mongodb.com/docs/v5.0/reference/method/cursor.explain/#std-label-explain-cursor-method-verbosity

 

3. 三種模式下的說明和結果解釋

3.1 queryPlanner 模式

運行查詢優化器對當前的查詢進行評估並選擇一個最佳的查詢計劃,不執行實際語句。

 

下麵三種模式均以這個查詢語句為例:

db.getCollection("dailyTrip").explain().find({"parent_type":"Ride-hailing apps"}, { _id:0});

下麵是一個執行的返回結果,我把詳細內容先收起,可以看到返回的欄位有哪些。

 

具體解釋

{
    "explainVersion" : "1",  // 執行計劃的版本,可以忽略 
    "queryPlanner" : {     //  queryPlanner,就是執行計劃。 這部分重點看
        "namespace" : "test.dailyTrip",  // 就是collection和所在db
        "indexFilterSet" : false,   // 是否設置了索引過濾器集合,Filter決定了查詢優化器對於某個查詢將如何使用索引
        "parsedQuery" : {    // 經過解析後的query內容。
            "parent_type" : {
                "$eq" : "Ride-hailing apps"
            }
        },
        "queryHash" : "8B8C334A",  // 查詢的哈希值,
        "planCacheKey" : "F910A33F", // 查詢執行計劃的緩存鍵
        "maxIndexedOrSolutionsReached" : false, // 是否已達到最大索引【或】解決方案的限制。  跳過
        "maxIndexedAndSolutionsReached" : false, // 表示是否已達到最大索引【和】解決方案的限制。 跳過
        "maxScansToExplodeReached" : false, // 是否已達到最大掃描數的限制。 跳過
        "winningPlan" : {   // 這個很關鍵,這裡是最後底層選擇的執行計劃。   
            "stage" : "PROJECTION_DEFAULT",  // stage 是指步驟 可以由stage名稱看到具體做了什麼。 projection是應用投影操作,選擇所需的欄位
            "transformBy" : { // 具體投影內容  跳過
                "_id" : 0.0
            },
            "inputStage" : { // 執行階段的子階段,這裡是一個FETCH的子過程
                "stage" : "FETCH",   // FETCH是 從索引中獲取文檔數據。
                "inputStage" : { // 其中一個階段,具體操作。 這裡面的東西是重點要看的。 
                    "stage" : "IXSCAN", // 這裡是說使用索引進行掃描,通常表示優化的查詢。
                    "keyPattern" : { // 表示下麵的索引欄位的排列方式,1.0正序,-1.0倒序。
                        "parent_type" : 1.0,
                        "car_type" : 1.0
                    },
                    "indexName" : "parent_type_1_car_type_1", // 用到的索引名稱
                    "isMultiKey" : false, // 是否為多鍵索引。 如果為 true,表示索引包含數組值
                    "multiKeyPaths" : { // 如果索引是多鍵索引,這個屬性將會包含索引中包含數組值的欄位路徑。本例中的索引不是多鍵索引,因此下麵的欄位為空數組。
                        "parent_type" : [
                        ],
                        "car_type" : [
                        ]
                    },
                    "isUnique" : false, // 是否為唯一索引
                    "isSparse" : false, // 是否為稀疏索引
                    "isPartial" : false, // 是否為部分索引
                    "indexVersion" : 2.0, // 索引版本 
                    "direction" : "forward", // 索引的遍歷方向
                    "indexBounds" : {  // 當前查詢具體使用的索引
                        "parent_type" : [
                            "[\"Ride-hailing apps\", \"Ride-hailing apps\"]"
                        ],
                        "car_type" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                }
            }
        },
        "rejectedPlans" : [  // 底層優化器拒絕的計劃,沒有執行的計劃。  
        ]
    },
    "command" : {   // 語句具體涉及到的命令、collection、DB
        "find" : "dailyTrip",  // 表名 從哪個collection中查找
        "filter" : {   // 哪個階段,這裡就是過濾
            "parent_type" : "Ride-hailing apps"
        },
        "projection" : {   // 投影 控制要返回的欄位 
            "_id" : 0.0
        },
        "$db" : "test"   //
    },
    "serverInfo" : {     // 伺服器信息   
        "host" : "onepiece-pc",  // mongo的示例主機名稱
        "port" : 27017.0, //
        "version" : "5.0.9", // mongodb的版本
        "gitVersion" : "6f7dae919422dcd7*****************1ad00e6"  // 這裡是git版本,這裡無關緊要,我脫敏了。 
    },
    "serverParameters" : {   // 伺服器的參數,各種緩存大小、最大閾值設置,暫時和我們這裡說的內容無關,跳過。
        "internalQueryFacetBufferSizeBytes" : 104857600.0,
        "internalQueryFacetMaxOutputDocSizeBytes" : 104857600.0,
        "internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600.0,
        "internalDocumentSourceGroupMaxMemoryBytes" : 104857600.0,
        "internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600.0,
        "internalQueryProhibitBlockingMergeOnMongoS" : 0.0,
        "internalQueryMaxAddToSetBytes" : 104857600.0,
        "internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600.0
    },
    "ok" : 1.0
}

3.2 executionStats模式

db.getCollection("dailyTrip").find({"parent_type":"Ride-hailing apps"}, { _id:0}).explain("executionStats");

運行查詢優化器對當前的查詢進行評估並選擇一個最佳的查詢計划進行執行,在執行完畢後返回這個最佳執行計劃執行完成時的相關統計信息。

 

{
    "explainVersion" : "1",
    "queryPlanner" : {
        // ... 同上,省略。
        "parsedQuery" : {
           // ... 同上,省略。
        },
        // ... 同上,省略。
        "winningPlan" : {
           // ... 同上,省略。
        }
        "rejectedPlans" : [
           // ... 同上,省略。
        ]
    },
    "executionStats" : { // 重點留意這裡面的內容
        "executionSuccess" : true,  // 執行結果
        "nReturned" : 23010.0,  // 返回的文檔數
        "executionTimeMillis" : 70.0, // 執行耗時 ms
        "totalKeysExamined" : 23010.0,  // 掃描了的索引總數
        "totalDocsExamined" : 23010.0,  // 總的掃描文檔數
        "executionStages" : {  // 執行stage 裡面會有具體每個stage的詳細
            "stage" : "PROJECTION_DEFAULT",  // 階段類型
            "nReturned" : 23010.0, 
            "executionTimeMillisEstimate" : 10.0, // 預估執行時間  ms
            "works" : 23011.0,  // 階段中掃描任務數
            "advanced" : 23010.0, // 階段中向上提交數量
            "needTime" : 0.0, // 階段中定位索引位置所需次數
            "needYield" : 0.0, // 階段中獲取鎖等待時間
            "saveState" : 23.0, // :表示在查詢執行過程中,保存中間狀態所花費的時間 ms
            "restoreState" : 23.0, // 表示在查詢執行過程中,恢復之前保存的中間狀態所花費的時間 ms
            "isEOF" : 1.0, // 階段中是否到達流的結束位,對於limit限制符的查詢可能為0
            "transformBy" : { // 表示查詢計劃是否使用了投影操作來轉換結果 
                "_id" : 0.0
            },
            "inputStage" : { // 執行階段的子階段,這裡是一個fetch的子過程
                "stage" : "FETCH", // 內容差不多 
                "nReturned" : 23010.0,
                "executionTimeMillisEstimate" : 4.0,
                "works" : 23011.0,
                "advanced" : 23010.0,
                "needTime" : 0.0,
                "needYield" : 0.0,
                "saveState" : 23.0,
                "restoreState" : 23.0,
                "isEOF" : 1.0,
                "docsExamined" : 23010.0,
                "alreadyHasObj" : 0.0,
                "inputStage" : { //  子階段,ixscan子過程
                    "stage" : "IXSCAN",
                    "nReturned" : 23010.0,
                    "executionTimeMillisEstimate" : 4.0,
                    "works" : 23011.0,
                    "advanced" : 23010.0,
                    "needTime" : 0.0,
                    "needYield" : 0.0,
                    "saveState" : 23.0,
                    "restoreState" : 23.0,
                    "isEOF" : 1.0,
                    "keyPattern" : {
                        "parent_type" : 1.0,
                        "car_type" : 1.0
                    },
                    "indexName" : "parent_type_1_car_type_1",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "parent_type" : [

                        ],
                        "car_type" : [

                        ]
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2.0,
                    "direction" : "forward",
                    "indexBounds" : {
                        "parent_type" : [
                            "[\"Ride-hailing apps\", \"Ride-hailing apps\"]"
                        ],
                        "car_type" : [
                            "[MinKey, MaxKey]"
                        ]
                    },
                    "keysExamined" : 23010.0,
                    "seeks" : 1.0,
                    "dupsTested" : 0.0,
                    "dupsDropped" : 0.0
                }
            }
        }
    },
    "command" : { // ... 同上,省略。
    },
    "serverInfo" : { // ... 同上,省略。
    },
    "serverParameters" : {// ... 同上,省略。
    },
    "ok" : 1.0
}

3.3 allPlansExecution模式

// 調用explain("allPlansExecution"), allPlansExecution 模式,執行所有的方案,並輸出結果。
db.getCollection("dailyTrip").find({"parent_type":"Ride-hailing apps"}, { _id:0}).explain("allPlansExecution");

allPlansExecution相比executionStats,其他的備選執行計劃也會去執行,並統計結果出來。 會存放在 executionStats中。

即按照最佳的執行計劃執行以及列出統計信息, 如果有多個查詢計劃,還會列出這些非最佳執行計劃部分的統計信息。

 

{
    "explainVersion" : "1",
    "queryPlanner" : {
        "namespace" : "test.dailyTrip",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "parent_type" : {
                "$eq" : "Ride-hailing apps"
            }
        },
        "maxIndexedOrSolutionsReached" : false,
        "maxIndexedAndSolutionsReached" : false,
        "maxScansToExplodeReached" : false,
        "winningPlan" : {
           // ... 同上,省略。
        },
        "rejectedPlans" : [
           // ... 同上,省略。
        ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 23010.0,
        "executionTimeMillis" : 68.0,
        "totalKeysExamined" : 23010.0,
        "totalDocsExamined" : 23010.0,
        "executionStages" : {
         // 這裡的executionStage和2.2的executionStats中的executionstages一樣,參考那裡。 
        },
        "allPlansExecution" : [  // 重點:allPlansExecution 如果有其他執行計劃,那麼會在這裡把執行統計結果放出來。   
        ]
    },
    "command" : {// ... 同上,省略。
    },
    "serverInfo" : {// ... 同上,省略。
    },
    "serverParameters" : {// ... 同上,省略。
    },
    "ok" : 1.0
}

Stage參數值

  • COLLSCAN:全表掃描

  • IXSCAN:索引掃描

  • FETCH:根據索引去檢索指定document

  • SHARD_MERGE:將各個分片返回數據進行merge

  • SORT:表明在記憶體中進行了排序

  • LIMIT:使用limit限制返回數

  • SKIP:使用skip進行跳過

  • IDHACK:針對_id進行查詢

  • SHARDING_FILTER:通過mongos對分片數據進行查詢 ---伺服器是分片的才有。

  • COUNT:利用db.coll.explain().count()之類進行count運算

  • COUNTSCAN: count不使用Index進行count時的stage返回

  • COUNT_SCAN: count使用了Index進行count時的stage返回

  • SUBPLA:未使用到索引的$or查詢的stage返回

  • TEXT:使用全文索引進行查詢時候的stage返回

  • PROJECTION:限定返回欄位時候stage的返回

參考文檔

https://www.cnblogs.com/littleatp/p/8419678.html

 

4. 查詢優化思路

  • 儘量使用索引。 舉例,比如通過上面的執行計劃發現某個作為查詢條件的欄位,沒有用上索引,且通過索引可以極大提高性能,那麼可以考慮對該欄位增加索引。

  • 掃描文檔數越小越好。舉例,比如上面的執行計劃中,某個階段返回的掃描文檔數量極大,那麼可以考慮優化語句,比如調整語句順序,先過濾再處理;或者對查詢條件欄位增加索引。

 

完。

感謝查閱,希望對你有幫助,點個贊再走唄~


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

-Advertisement-
Play Games
更多相關文章
  • 本篇文章將帶你認識C#的新語法、創建項目、發佈運行、讀取的相關操作、MVC開發、擴展、各種容易的使用,許可權等.NET的相關知識。帶你從零到精通,全面掌握.NET5的開發技能。 ...
  • DrawingBrush 背景圖 Background上畫,線條(LineGeometry)、幾何圖形(RectangleGeometry)、橢圓(EllipseGeometry)、弧線(ArcSegment )等。 屬性 說明 描述 <GeometryDrawing Brush="Orange"> ...
  • 前面講了Centos如何安裝telnet遠程,這次分享Ubuntu系統如何安裝遠程telnet,作為咱們運維備用遠程途徑 一、下載和安裝 查看系統版本:Ubuntu 22.04.1 LTS 線上安裝: apt install telnet telnetd openbsd-inetd 離線安裝: 離線 ...
  • # Docker概述 ## Docker為何要出現 由於傳統的方式,不能打包環境,而環境的配置也及其繁瑣,及其耗費時間,而docker就可以非常有效的解決這個問題 **docker的思想來源於集裝箱,docker的核心思想!打包裝箱,也就是說每個箱子都是隔開的,docker通過這種機制可以吧伺服器利 ...
  • 提要:本系列文章主要參考`MIT 6.828課程`以及兩本書籍`《深入理解Linux內核》` `《深入Linux內核架構》`對Linux內核內容進行總結。 記憶體管理的實現覆蓋了多個領域: 1. 記憶體中的物理記憶體頁的管理 2. 分配大塊記憶體的伙伴系統 3. 分配較小記憶體的slab、slub、slob分 ...
  • 屏蔽驅動列印信息 root許可權:echo "1 4 1 7" > /proc/sys/kernel/printk sudo sh -c "sudo echo "1 4 1 7" > /proc/sys/kernel/printk 實現觸摸屏校驗 第一次進入Linux系統時,會出現觸摸校驗提示,按提示 ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230811232544082-164229613.png) # 1. 基本信息 SQL經典實例 SQL Cookbook [[美]安東尼·莫利納羅(Anthony Molin ...
  • ![file](https://img2023.cnblogs.com/other/3195851/202308/3195851-20230811181235894-1707158282.png) # 個人簡介 * 王海林 白鯨開源研發工程師 * GitHub ID:hailin0 * 做過性能監控 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...