MongoDB分析insert源代碼

mongo插入單條文檔insert()

> db.user.insert({
...     "name": "alice",
...     "age": 28
... });
WriteResult({ "nInserted" : 1 })
>

MongoDB插入文檔代碼調用鏈如下:

  1. mongo/db/commands/write_commands/write_commands.cpp的CmdInsert類
  2. mongo/db/commands/write_commands/write_commands.cpp的runImpl
  3. mongo/db/ops/write_ops_exec.cpp中的performInserts
  4. mongo/db/ops/write_ops_exec.cpp中的insertBatchAndHandleErrors
  5. mongo/db/ops/write_ops_exec.cpp中的insertDocuments
  6. mongo/db/catalog/collection_impl.cpp的insertDocuments
  7. mongo/db/catalog/collection_impl.cpp的_insertDocuments
  8. mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp的insertRecords
  9. mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp的_insertRecords

mongo/db/commands/write_commands/write_commands.cpp的CmdInsert代碼如下:

class CmdInsert final : public WriteCommand {
public:CmdInsert() : WriteCommand("insert") {}private:class Invocation final : public InvocationBase {public:Invocation(const WriteCommand* cmd, const OpMsgRequest& request): InvocationBase(cmd, request), _batch(InsertOp::parse(request)) {}private:NamespaceString ns() const override {return _batch.getNamespace();}...void runImpl(OperationContext* opCtx, BSONObjBuilder& result) const override {auto reply = performInserts(opCtx, _batch);serializeReply(opCtx,ReplyStyle::kNotUpdate,!_batch.getWriteCommandBase().getOrdered(),_batch.getDocuments().size(),std::move(reply),&result);}write_ops::Insert _batch;};std::unique_ptr<CommandInvocation> parse(OperationContext*,const OpMsgRequest& request) override {return std::make_unique<Invocation>(this, request);}...std::string help() const final {return "insert documents";}
} cmdInsert;

mongo/db/ops/write_ops_exec.cpp中的方法performInserts代碼:

1、獲取系統閾值,?maxBatchSize = internalInsertMaxBatchSize.load()默認64,batch一次最多插入64個文檔;? ? ? ? ? ? ? ?maxBatchBytes = write_ops::insertVectorMaxBytes默認256,batch一次最多插入N個文檔大小小于256KB。

2、循環wholeOp.getDocuments()文檔集合,將遍歷文檔放入batch中,fixDocumentForInsert驗證每個文檔參數合法性,重要是系統字段_id生成。

3、循環wholeOp.getDocuments()文檔集合,將遍歷文檔放入batch中,batch滿足兩個閥值其中一個就insertBatchAndHandleErrors插入到數據庫中。

WriteResult performInserts(OperationContext* opCtx,const write_ops::Insert& wholeOp,bool fromMigrate) {// Insert performs its own retries, so we should only be within a WriteUnitOfWork when run in a// transaction.auto txnParticipant = TransactionParticipant::get(opCtx);invariant(!opCtx->lockState()->inAWriteUnitOfWork() ||(txnParticipant && opCtx->inMultiDocumentTransaction()));auto& curOp = *CurOp::get(opCtx);ON_BLOCK_EXIT([&] {// This is the only part of finishCurOp we need to do for inserts because they reuse the// top-level curOp. The rest is handled by the top-level entrypoint.curOp.done();Top::get(opCtx->getServiceContext()).record(opCtx,wholeOp.getNamespace().ns(),LogicalOp::opInsert,Top::LockType::WriteLocked,durationCount<Microseconds>(curOp.elapsedTimeExcludingPauses()),curOp.isCommand(),curOp.getReadWriteType());});{stdx::lock_guard<Client> lk(*opCtx->getClient());curOp.setNS_inlock(wholeOp.getNamespace().ns());curOp.setLogicalOp_inlock(LogicalOp::opInsert);curOp.ensureStarted();curOp.debug().additiveMetrics.ninserted = 0;}uassertStatusOK(userAllowedWriteNS(wholeOp.getNamespace()));DisableDocumentValidationIfTrue docValidationDisabler(opCtx, wholeOp.getWriteCommandBase().getBypassDocumentValidation());LastOpFixer lastOpFixer(opCtx, wholeOp.getNamespace());WriteResult out;out.results.reserve(wholeOp.getDocuments().size());bool containsRetry = false;ON_BLOCK_EXIT([&] { updateRetryStats(opCtx, containsRetry); });size_t stmtIdIndex = 0;size_t bytesInBatch = 0;std::vector<InsertStatement> batch;const size_t maxBatchSize = internalInsertMaxBatchSize.load();const size_t maxBatchBytes = write_ops::insertVectorMaxBytes;batch.reserve(std::min(wholeOp.getDocuments().size(), maxBatchSize));std::cout << "conca " << "maxBatchSize:"<<maxBatchSize<<std::endl;std::cout << "conca " << "maxBatchBytes:"<<maxBatchBytes<< std::endl;for (auto&& doc : wholeOp.getDocuments()) {const bool isLastDoc = (&doc == &wholeOp.getDocuments().back());auto fixedDoc = fixDocumentForInsert(opCtx->getServiceContext(), doc);if (!fixedDoc.isOK()) {// Handled after we insert anything in the batch to be sure we report errors in the// correct order. In an ordered insert, if one of the docs ahead of us fails, we should// behave as-if we never got to this document.} else {const auto stmtId = getStmtIdForWriteOp(opCtx, wholeOp, stmtIdIndex++);if (opCtx->getTxnNumber()) {if (!opCtx->inMultiDocumentTransaction() &&txnParticipant.checkStatementExecutedNoOplogEntryFetch(stmtId)) {containsRetry = true;RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount();out.results.emplace_back(makeWriteResultForInsertOrDeleteRetry());continue;}}BSONObj toInsert = fixedDoc.getValue().isEmpty() ? doc : std::move(fixedDoc.getValue());batch.emplace_back(stmtId, toInsert);bytesInBatch += batch.back().doc.objsize();if (!isLastDoc && batch.size() < maxBatchSize && bytesInBatch < maxBatchBytes)continue;  // Add more to batch before inserting.}bool canContinue =insertBatchAndHandleErrors(opCtx, wholeOp, batch, &lastOpFixer, &out, fromMigrate);batch.clear();  // We won't need the current batch any more.bytesInBatch = 0;if (canContinue && !fixedDoc.isOK()) {globalOpCounters.gotInsert();ServerWriteConcernMetrics::get(opCtx)->recordWriteConcernForInsert(opCtx->getWriteConcern());try {uassertStatusOK(fixedDoc.getStatus());MONGO_UNREACHABLE;} catch (const DBException& ex) {canContinue = handleError(opCtx, ex, wholeOp.getNamespace(), wholeOp.getWriteCommandBase(), &out);}}if (!canContinue)break;}return out;
}

其中fixDocumentForInsert方法驗證參數,如果_id沒有值,則MongoDB生成系統字符串對象ObjectId,具體代碼是?b.appendOID("_id", nullptr, true)。

MongoDB的ObjectId介紹:

  • 插入一條數據系統都會自動插入一個_id鍵,鍵值不可以重復,它可以是任何類型的,也可以手動的插入,默認情況下它的數據類型是ObjectId,由于MongoDB在設計之初就是用作分布式數據庫,所以使用ObjectId可以避免不同數據庫中_id的重復(如果使用自增的方式在分布式系統中就會出現重復的_id的值)。
  • objectId使用12字節的存儲空間,每個字節可以存儲兩個十六進制數字,所以一共可以存儲24個十六進制數字組成的字符串,在這24個字符串中,前8位表示時間戳,接下來6位是一個機器碼,接下來4位表示進程id,最后6位表示計數器。示意圖如下:
其中包括4-byte Unix 時間戳,3-byte 機器 ID,2-byte 進程 ID,3-byte 計數器(初始化隨機)。

601e2b6b aa203c c89f 2d31aa
↑ ? ? ?↑ ? ? ↑ ? ? ↑
時間戳 ?機器碼 進程id 計數器

mongo/db/ops/write_ops_exec.cpp中的方法insertBatchAndHandleErrors。一批數據通過分批拆分存入多個batch后,調用insertBatchAndHandleErrors()接口來完成單個batch的數據寫入。整個batch數據寫入可以在一個transaction事務完成,也可以一條數據一個事務來完成寫入,具體核心代碼實現如下:

bool insertBatchAndHandleErrors(OperationContext* opCtx,const write_ops::Insert& wholeOp,std::vector<InsertStatement>& batch,LastOpFixer* lastOpFixer,WriteResult* out,bool fromMigrate) {if (batch.empty())return true;auto& curOp = *CurOp::get(opCtx);CurOpFailpointHelpers::waitWhileFailPointEnabled(&hangDuringBatchInsert,opCtx,"hangDuringBatchInsert",[&wholeOp]() {log() << "batch insert - hangDuringBatchInsert fail point enabled for namespace "<< wholeOp.getNamespace()<< ". Blocking ""until fail point is disabled.";},true,  // Check for interrupt periodically.wholeOp.getNamespace());if (MONGO_unlikely(failAllInserts.shouldFail())) {uasserted(ErrorCodes::InternalError, "failAllInserts failpoint active!");}boost::optional<AutoGetCollection> collection;auto acquireCollection = [&] {while (true) {collection.emplace(opCtx,wholeOp.getNamespace(),fixLockModeForSystemDotViewsChanges(wholeOp.getNamespace(), MODE_IX));if (collection->getCollection())break;collection.reset();  // unlock.makeCollection(opCtx, wholeOp.getNamespace());}curOp.raiseDbProfileLevel(collection->getDb()->getProfilingLevel());assertCanWrite_inlock(opCtx, wholeOp.getNamespace(), collection->getCollection());CurOpFailpointHelpers::waitWhileFailPointEnabled(&hangWithLockDuringBatchInsert, opCtx, "hangWithLockDuringBatchInsert");};try {acquireCollection();auto txnParticipant = TransactionParticipant::get(opCtx);auto inTxn = txnParticipant && opCtx->inMultiDocumentTransaction();LOG(1) << "conca collection->getCollection()->isCapped();"<<collection->getCollection()->isCapped();LOG(1) << "conca batch.size();"<<batch.size();if (!collection->getCollection()->isCapped() && !inTxn && batch.size() > 1) {// First try doing it all together. If all goes well, this is all we need to do.// See Collection::_insertDocuments for why we do all capped inserts one-at-a-time.lastOpFixer->startingOp();insertDocuments(opCtx, collection->getCollection(), batch.begin(), batch.end(), fromMigrate);lastOpFixer->finishedOpSuccessfully();globalOpCounters.gotInserts(batch.size());ServerWriteConcernMetrics::get(opCtx)->recordWriteConcernForInserts(opCtx->getWriteConcern(), batch.size());SingleWriteResult result;result.setN(1);std::fill_n(std::back_inserter(out->results), batch.size(), std::move(result));curOp.debug().additiveMetrics.incrementNinserted(batch.size());return true;}} catch (const DBException&) {// Ignore this failure and behave as if we never tried to do the combined batch// insert. The loop below will handle reporting any non-transient errors.collection.reset();}LOG(1) << "conca Try to insert the batch one-at-a-time;";// Try to insert the batch one-at-a-time. This path is executed for singular batches,// multi-statement transactions, capped collections, and if we failed all-at-once inserting.for (auto it = batch.begin(); it != batch.end(); ++it) {globalOpCounters.gotInsert();ServerWriteConcernMetrics::get(opCtx)->recordWriteConcernForInsert(opCtx->getWriteConcern());try {LOG(1) << "conca writeConflictRetry;";writeConflictRetry(opCtx, "insert", wholeOp.getNamespace().ns(), [&] {try {if (!collection)acquireCollection();// Transactions are not allowed to operate on capped collections.uassertStatusOK(checkIfTransactionOnCappedColl(opCtx, collection->getCollection()));lastOpFixer->startingOp();insertDocuments(opCtx, collection->getCollection(), it, it + 1, fromMigrate);lastOpFixer->finishedOpSuccessfully();SingleWriteResult result;result.setN(1);out->results.emplace_back(std::move(result));curOp.debug().additiveMetrics.incrementNinserted(1);} catch (...) {// Release the lock following any error if we are not in multi-statement// transaction. Among other things, this ensures that we don't sleep in the WCE// retry loop with the lock held.// If we are in multi-statement transaction and under a WUOW, we will// not actually release the lock.collection.reset();throw;}});} catch (const DBException& ex) {bool canContinue =handleError(opCtx, ex, wholeOp.getNamespace(), wholeOp.getWriteCommandBase(), out);if (!canContinue) {// Failed in ordered batch, or in a transaction, or from some unrecoverable error.return false;}}}return true;
}

一批batch數據(假設64條)寫入過程,如果不是capped固定集合,則這64條數據首先放入一個transaction事務中完成寫入,writeConflictRetry里面會執行{}中的函數體。如果寫入異常,判斷canContinue =handleError()是否繼續一個事務一條數據寫入。

mongo/db/ops/write_ops_exec.cpp中的方法insertDocuments把單條文檔插入,核心代碼如下:WriteUnitOfWork wuow(opCtx)事務開始;把數組begin到end之間的所有doc文檔數據放入該事務中insertDocuments;wuow.commit()事務提交。

void insertDocuments(OperationContext* opCtx,Collection* collection,std::vector<InsertStatement>::iterator begin,std::vector<InsertStatement>::iterator end,bool fromMigrate) {WriteUnitOfWork wuow(opCtx);
...LOG(1) << "conca collection->insertDocuments" ;uassertStatusOK(collection->insertDocuments(opCtx, begin, end, &CurOp::get(opCtx)->debug(), fromMigrate));wuow.commit();
}

上面從mongo/db/ops/write_ops_exec.cpp數據庫層轉移到集合層,調用的是集合插入文檔方法collection->insertDocuments。

mongo/db/catalog/collection_impl.cpp的方法insertDocuments

Status CollectionImpl::insertDocuments(OperationContext* opCtx,const std::vector<InsertStatement>::const_iterator begin,const std::vector<InsertStatement>::const_iterator end,OpDebug* opDebug,bool fromMigrate) {LOG(1) << "conca CollectionImpl::insertDocuments"  ;auto status = checkFailCollectionInsertsFailPoint(_ns, (begin != end ? begin->doc : BSONObj()));if (!status.isOK()) {return status;}// Should really be done in the collection object at creation and updated on index create.const bool hasIdIndex = _indexCatalog->findIdIndex(opCtx);for (auto it = begin; it != end; it++) {if (hasIdIndex && it->doc["_id"].eoo()) {return Status(ErrorCodes::InternalError,str::stream()<< "Collection::insertDocument got document without _id for ns:"<< _ns);}auto status = checkValidation(opCtx, it->doc);if (!status.isOK())return status;}const SnapshotId sid = opCtx->recoveryUnit()->getSnapshotId();status = _insertDocuments(opCtx, begin, end, opDebug);if (!status.isOK()) {return status;}invariant(sid == opCtx->recoveryUnit()->getSnapshotId());getGlobalServiceContext()->getOpObserver()->onInserts(opCtx, ns(), uuid(), begin, end, fromMigrate);opCtx->recoveryUnit()->onCommit([this](boost::optional<Timestamp>) { notifyCappedWaitersIfNeeded(); });hangAfterCollectionInserts.executeIf([&](const BSONObj& data) {const auto& firstIdElem = data["first_id"];std::string whenFirst;if (firstIdElem) {whenFirst += " when first _id is ";whenFirst += firstIdElem.str();}log() << "hangAfterCollectionInserts fail point enabled for " << _ns << whenFirst<< ". Blocking until fail point is disabled.";hangAfterCollectionInserts.pauseWhileSet(opCtx);},[&](const BSONObj& data) {const auto& collElem = data["collectionNS"];const auto& firstIdElem = data["first_id"];// If the failpoint specifies no collection or matches the existing one, hang.return (!collElem || _ns.ns() == collElem.str()) &&(!firstIdElem ||(begin != end && firstIdElem.type() == mongo::String &&begin->doc["_id"].str() == firstIdElem.str()));});return Status::OK();
}

mongo/db/catalog/collection_impl.cpp的方法_insertDocuments

Status CollectionImpl::_insertDocuments(OperationContext* opCtx,const std::vector<InsertStatement>::const_iterator begin,const std::vector<InsertStatement>::const_iterator end,OpDebug* opDebug) {dassert(opCtx->lockState()->isCollectionLockedForMode(ns(), MODE_IX));const size_t count = std::distance(begin, end);if (isCapped() && _indexCatalog->haveAnyIndexes() && count > 1) {// We require that inserts to indexed capped collections be done one-at-a-time to avoid the// possibility that a later document causes an earlier document to be deleted before it can// be indexed.// TODO SERVER-21512 It would be better to handle this here by just doing single inserts.return {ErrorCodes::OperationCannotBeBatched,"Can't batch inserts into indexed capped collections"};}if (_needCappedLock) {// X-lock the metadata resource for this capped collection until the end of the WUOW. This// prevents the primary from executing with more concurrency than secondaries.// See SERVER-21646.Lock::ResourceLock heldUntilEndOfWUOW{opCtx->lockState(), ResourceId(RESOURCE_METADATA, _ns.ns()), MODE_X};}std::vector<Record> records;records.reserve(count);std::vector<Timestamp> timestamps;timestamps.reserve(count);for (auto it = begin; it != end; it++) {records.emplace_back(Record{RecordId(), RecordData(it->doc.objdata(), it->doc.objsize())});timestamps.emplace_back(it->oplogSlot.getTimestamp());}LOG(1) << "conca CollectionImpl::insertDocuments _recordStore->insertRecords"  ;Status status = _recordStore->insertRecords(opCtx, &records, timestamps);if (!status.isOK())return status;std::vector<BsonRecord> bsonRecords;bsonRecords.reserve(count);int recordIndex = 0;for (auto it = begin; it != end; it++) {RecordId loc = records[recordIndex++].id;invariant(RecordId::min() < loc);invariant(loc < RecordId::max());BsonRecord bsonRecord = {loc, Timestamp(it->oplogSlot.getTimestamp()), &(it->doc)};bsonRecords.push_back(bsonRecord);}LOG(1) << "conca CollectionImpl::insertDocuments __indexCatalog->indexRecords"  ;int64_t keysInserted;status = _indexCatalog->indexRecords(opCtx, bsonRecords, &keysInserted);if (opDebug) {opDebug->additiveMetrics.incrementKeysInserted(keysInserted);}return status;
}

RecordStore> _recordStore是抽象類,?_recordStore->insertRecords()不同的存儲引擎有各自的實現類,MongoDB默認的存儲引擎是wiredtiger,后面會重點分析wiredtiger_record_store.cpp插入文檔記錄邏輯。

_recordStore->insertRecords()插入文檔之后調用_indexCatalog->indexRecords繼續插入索引key,_recordStore->insertRecords()后面單獨分析索引插入過程。

mongo/db/storage/mobile/mobile_record_store.cpp移動存儲引擎是 MongoDB 為嵌入式設備設計的輕量級存儲引擎,特點是資源占用少、依賴簡單。

mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp,WiredTiger是MongoDB 的默認存儲引擎,提供高性能、高并發和完整的事務支持,insertRecords插入代碼:

Status WiredTigerRecordStore::insertRecords(OperationContext* opCtx,std::vector<Record>* records,const std::vector<Timestamp>& timestamps) {return _insertRecords(opCtx, records->data(), timestamps.data(), records->size());
}Status WiredTigerRecordStore::_insertRecords(OperationContext* opCtx,Record* records,const Timestamp* timestamps,size_t nRecords) {dassert(opCtx->lockState()->isWriteLocked());// We are kind of cheating on capped collections since we write all of them at once ....// Simplest way out would be to just block vector writes for everything except oplog ?int64_t totalLength = 0;for (size_t i = 0; i < nRecords; i++)totalLength += records[i].data.size();// caller will retry one element at a timeif (_isCapped && totalLength > _cappedMaxSize)return Status(ErrorCodes::BadValue, "object to insert exceeds cappedMaxSize");LOG(1) << "conca WiredTigerRecordStore::insertRecords _uri:" << _uri;LOG(1) << "conca WiredTigerRecordStore::insertRecords _tableId:" << _tableId;WiredTigerCursor curwrap(_uri, _tableId, true, opCtx);curwrap.assertInActiveTxn();WT_CURSOR* c = curwrap.get();invariant(c);RecordId highestId = RecordId();dassert(nRecords != 0);for (size_t i = 0; i < nRecords; i++) {auto& record = records[i];if (_isOplog) {StatusWith<RecordId> status =oploghack::extractKey(record.data.data(), record.data.size());if (!status.isOK())return status.getStatus();record.id = status.getValue();} else {record.id = _nextId(opCtx);}dassert(record.id > highestId);highestId = record.id;}for (size_t i = 0; i < nRecords; i++) {auto& record = records[i];Timestamp ts;if (timestamps[i].isNull() && _isOplog) {// If the timestamp is 0, that probably means someone inserted a document directly// into the oplog.  In this case, use the RecordId as the timestamp, since they are// one and the same. Setting this transaction to be unordered will trigger a journal// flush. Because these are direct writes into the oplog, the machinery to trigger a// journal flush is bypassed. A followup oplog read will require a fresh visibility// value to make progress.ts = Timestamp(record.id.repr());opCtx->recoveryUnit()->setOrderedCommit(false);} else {ts = timestamps[i];}if (!ts.isNull()) {LOG(4) << "inserting record with timestamp " << ts;fassert(39001, opCtx->recoveryUnit()->setTimestamp(ts));}setKey(c, record.id);WiredTigerItem value(record.data.data(), record.data.size());c->set_value(c, value.Get());int ret = WT_OP_CHECK(c->insert(c));if (ret)return wtRCToStatus(ret, "WiredTigerRecordStore::insertRecord");}_changeNumRecords(opCtx, nRecords);_increaseDataSize(opCtx, totalLength);if (_oplogStones) {_oplogStones->updateCurrentStoneAfterInsertOnCommit(opCtx, totalLength, highestId, nRecords);} else {_cappedDeleteAsNeeded(opCtx, highestId);}return Status::OK();
}

WiredTigerCursor curwrap(_uri, _tableId, true, opCtx);WT_CURSOR* c = curwrap.get();獲取WiredTiger引擎游標;

record.id = _nextId(opCtx);設置記錄系統隱藏字段recordId,數值類型,自增,唯一不變;

setKey(c, record.id);WiredTiger引擎游標設置索引record.id值;

WiredTigerItem value(record.data.data(), record.data.size());生成文檔WiredTigerItem

c->set_value(c, value.Get());WiredTiger引擎游標設置文檔WiredTigerItem

int ret = WT_OP_CHECK(c->insert(c));;WiredTiger引擎游標插入。

WiredTiger引擎游標WiredTigerCursor是第三方WiredTiger引擎核心mongo-r4.0.7\src\third_party\wiredtiger,后續文檔會繼續分析的,B+樹怎么存儲索引,對應文檔的。

mongo插入單條文檔insert()主要分4個階段:

(1)分割文檔,分批次插入

(2)將文檔傳遞給集合類插入

(3)將文檔傳遞給記錄record插入

(4)傳遞給引擎游標WiredTigerCursor插入

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918673.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918673.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918673.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

react路由跳轉與路由懶加載等(對照vue來說一說不同之處)

前言&#xff1a;react路由跳轉與路由懶加載等路由懶加載&#xff1a;使用 loadable/component 插件來實現安裝&#xff1a;npm i loadable/component具體使用&#xff1a;1、引入loadable/component2、正常封裝的地方const HomeLoadable(()>import(./views/Home.jsx));也可…

Nginx 架構和安裝

二、.Nginx 架構和安裝 2.1 Nginx 概述 2.1.1 Nginx 介紹 Nginx&#xff1a;engine X &#xff0c;2002年開發&#xff0c;分為社區版和商業版(nginx plus ) 2019年3月11日 F5 Networks 6.7億美元的價格收購 Nginx是免費的、開源的、高性能的HTTP和反向代理服務器、郵件代理服務…

HarmonyOS NDK的JavaScript/TypeScript與C++交互機制

HarmonyOS NDK的JavaScript/TypeScript與C交互機制 細解釋這個調用流程&#xff1a; 整體架構流程 ArkTS/JavaScript ←→ .d.ts (類型定義) ←→ NAPI ←→ .cpp (C實現)文件結構和作用 項目結構示例&#xff1a; MyHarmonyApp/ ├── entry/src/main/ets/ # ArkTS應…

[激光原理與應用-226]:機械 - 如何學習3D圖設計

學習機械領域的3D圖設計需要系統掌握軟件操作、設計思維、工程規范和實戰經驗。以下是分階段的學習路徑和實用建議&#xff0c;幫助你高效入門并提升技能&#xff1a;一、基礎準備階段1. 明確學習目標方向選擇&#xff1a;根據興趣確定細分領域&#xff08;如機械零件設計、鈑金…

uniapp -- 小程序處理與設備通訊 GBK/GB2312 編碼問題。

?? 小程序/UniApp 中處理 GBK 編碼:iconv-lite + Buffer 實用指南 適用場景:設備通信、藍牙傳輸、舊系統對接、十六進制轉中文等涉及 GB2312/GBK 編碼 的中文亂碼問題。 ?? 一、為什么需要這個工具? 在小程序或 UniApp 開發中,常遇到以下問題: 藍牙設備返回的中文是 …

8.13 JavaWeb(MySQL P89-P103)

DML&#xff08;數據操作語言&#xff09;Data Manipulation Language&#xff0c;用來對數據庫表中的數據記錄進行增、刪、改操作添加數據-- DML &#xff1a; 數據操作語言 -- DML &#xff1a; 插入數據 - insert -- 1.為tb_emp表的username&#xff0c;name&#xff0c;gen…

Python 類元編程(元類基礎知識)

元類基礎知識 元類是制造類的工廠&#xff0c;不過不是函數&#xff08;如示例 21-2 中的 record_factory&#xff09;&#xff0c;而是類。圖 21-1 使用機器和小怪獸圖示法描述元 類&#xff0c;可以看出&#xff0c;元類是生產機器的機器。根據 Python 對象模型&#xff0c;類…

【Vue 3 響應式系統深度解析:reactive vs ref 全面對比】

Vue 3 響應式系統深度解析&#xff1a;reactive vs ref 全面對比 目錄 概述響應式系統基礎reactive 深度分析ref 深度分析底層實現原理依賴收集機制演進解構和轉換工具常見誤區和陷阱技術選型指南最佳實踐和建議 概述 Vue 3 引入了基于 Proxy 的全新響應式系統&#xff0c;…

JavaSE高級-01

文章目錄1. 異常異常的分類自定義異常異常的處理資源關閉&#xff1a;try-with-resource2. 泛型泛型類泛型接口泛型方法、通配符、上下限通配符泛型的上下限泛型支持的類型3. 包裝類4. Collection集合和Map集合4.1 Collection集合Collection集合特點Collection的遍歷方式一&…

MyBatis執行器與ORM特性深度解析

一、MyBatis的Executor執行器詳解1. MyBatis執行器類型MyBatis有三種核心執行器實現&#xff0c;在org.apache.ibatis.executor包中定義&#xff1a;執行器類型特點描述SimpleExecutor默認執行器&#xff0c;每次執行都會創建新的Statement對象ReuseExecutor重用預處理語句(Pre…

紅黑樹的特性與實現

在數據結構領域&#xff0c;二叉搜索樹&#xff08;BST&#xff09;憑借 O (log n) 的平均時間復雜度成為查找、插入和刪除操作的優選結構。但它有個致命缺陷&#xff1a;當輸入數據有序時&#xff0c;會退化為鏈表&#xff0c;時間復雜度驟降至 O (n)。為解決這一問題&#xf…

ClickHouse從入門到企業級實戰全解析課程簡介

【課程簡介】你是否正在面臨這些挑戰&#xff1f;海量數據的分析查詢慢如蝸牛&#xff0c;報表一等就是幾小時&#xff1f;想構建實時數倉&#xff0c;卻不知如何高效處理 Kafka 等流式數據&#xff1f;對 ClickHouse 的眾多 MergeTree 引擎感到困惑&#xff0c;不知如何選型&a…

【新啟航】從人工偏差到機械精度:旋轉治具讓三維掃描重構數據重復精度提升至 ±0.01mm

在三維掃描重構領域&#xff0c;傳統人工操作方式受限于人為因素干擾&#xff0c;數據重復精度難以保證&#xff0c;無法滿足高精度工業檢測與逆向工程需求。旋轉治具憑借先進的機械設計與自動化控制技術&#xff0c;將三維掃描重構數據重復精度提升至 0.01mm&#xff0c;實現從…

《匯編語言:基于X86處理器》第13章 復習題和編程練習

本篇記錄了《匯編語言&#xff1a;基于X86處理器》第13章 復習題和編程練習的學習筆記。13.6 復習題1.當匯編過程被高級語言程序調用時&#xff0c;主調程序與被調過程是否應使用相同的內存模式?答&#xff1a;主調程序與被調過程使用的內存模式必須相同。2.C 和 C程序調用匯編…

SpringAI智能航空助手實戰<Demo>

我們將如何將我們得傳統業務進行智能化的改造>>>1.將我們傳統的航空票務系統 我們之前通過按鈕的方式來完成 現在我們通過智能對話的方式完成 >現在我們通過對話的方式來完成 整個智能化的改造 傳統應用如何進行智能化改造 我們把我們的項目通過Spring-ai 來接入A…

windows git安裝步驟

1&#xff0c;從官網下載安裝包&#xff1a;gitg官網 進行安裝 2&#xff0c;配置git環境&#xff1a; git config --global user.name "Your Name" git config --global user.email "Your Email"3&#xff0c;生成 SSH Key&#xff1a; ssh-keygen -t r…

使用chroma和LlamaIndex做RAG增強

RAG 原理&#xff1a;通過 “檢索&#xff08;從知識庫獲取相關信息&#xff09;→ 增強&#xff08;將信息作為上下文輸入模型&#xff09;→ 生成&#xff08;模型基于上下文回答&#xff09;” 三步&#xff0c;解決大模型知識時效性、領域局限性問題。 接下來將完成這么一個…

2025 最應避免的攝影陷阱以及解決方案

你有沒有想過&#xff0c;當你拍完了一個完美的場景后&#xff0c;卻發現畫面模糊、光線不足&#xff0c;或者更糟的是&#xff0c;存儲卡中的文件丟失了&#xff1f;這些問題可能會發生在任何人身上&#xff0c;無論是業余愛好者、專業人士還是最好的攝影師。當珍貴的記憶變成…

python類--python011

面向對象編程中的類的概念、屬性使用、繼承和類的改造問題等。7.1 初識類在軟件編程中&#xff0c;面向過程和面向對象是兩種主要的編程方法。面向過程的編程強調通過函數來實現特定的功能&#xff0c;具有靈活性&#xff0c;但在復雜系統中往往導致代碼重復&#xff0c;維護困…

Python函數篇:從零到精通

一、函數1.1 為什么有函數我們對于一個項目時&#xff0c;會有上千甚至上萬條代碼&#xff0c;當我們要使用到某個函數時&#xff0c;例如我需要計算一個求和代碼&#xff0c;獲得求和的值來服務我們的項目&#xff0c;那我們可能會這樣#計算1&#xff5e;100的和 theSun 0 fo…