成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

認(rèn)識(shí)MongoDB 4.0的新特性——事務(wù)(Transactions)

數(shù)據(jù)庫(kù) 其他數(shù)據(jù)庫(kù) MongoDB
以前 MongoDB 是不支持事務(wù)的,因此開發(fā)者在需要用到事務(wù)的時(shí)候,不得不借用其他工具,在業(yè)務(wù)代碼層面去彌補(bǔ)數(shù)據(jù)庫(kù)的不足。隨著 4.0 版本的發(fā)布,MongoDB 也為我們帶來(lái)了原生的事務(wù)操作,下面就讓我們一起來(lái)認(rèn)識(shí)它,并通過(guò)簡(jiǎn)單的例子了解如何去使用。

前言

相信使用過(guò)主流的關(guān)系型數(shù)據(jù)庫(kù)的朋友對(duì)“事務(wù)(Transactions)”不會(huì)太陌生,它可以讓我們把對(duì)多張表的多次數(shù)據(jù)庫(kù)操作整合為一次原子操作,這在高并發(fā)場(chǎng)景下可以保證多個(gè)數(shù)據(jù)操作之間的互不干擾;并且一旦在這些操作過(guò)程任一環(huán)節(jié)中出現(xiàn)了錯(cuò)誤,事務(wù)會(huì)中止并且讓數(shù)據(jù)回滾,這使得同時(shí)在多張表中修改數(shù)據(jù)的時(shí)候保證了數(shù)據(jù)的一致性。

以前 MongoDB 是不支持事務(wù)的,因此開發(fā)者在需要用到事務(wù)的時(shí)候,不得不借用其他工具,在業(yè)務(wù)代碼層面去彌補(bǔ)數(shù)據(jù)庫(kù)的不足。隨著 4.0 版本的發(fā)布,MongoDB 也為我們帶來(lái)了原生的事務(wù)操作,下面就讓我們一起來(lái)認(rèn)識(shí)它,并通過(guò)簡(jiǎn)單的例子了解如何去使用。

 

 

 

[[249466]]

 

介紹

事務(wù)和副本集(Replica Sets)

副本集是 MongoDB 的一種主副節(jié)點(diǎn)架構(gòu),它使數(shù)據(jù)得到***的可用性,避免單點(diǎn)故障引起的整個(gè)服務(wù)不能訪問(wèn)的情況的發(fā)生。目前 MongoDB 的多表事務(wù)操作僅支持在副本集上運(yùn)行,想要在本地環(huán)境安裝運(yùn)行副本集可以借助一個(gè)工具包——run-rs,以下的文章中有詳細(xì)的使用說(shuō)明:

https://thecodebarbarian.com/...

事務(wù)和會(huì)話(Sessions)

事務(wù)和會(huì)話(Sessions)關(guān)聯(lián),一個(gè)會(huì)話同一時(shí)刻只能開啟一個(gè)事務(wù)操作,當(dāng)一個(gè)會(huì)話斷開,這個(gè)會(huì)話中的事務(wù)也會(huì)結(jié)束。

事務(wù)中的函數(shù)

  • Session.startTransaction()

在當(dāng)前會(huì)話中開始一次事務(wù),事務(wù)開啟后就可以開始進(jìn)行數(shù)據(jù)操作。在事務(wù)中執(zhí)行的數(shù)據(jù)操作是對(duì)外隔離的,也就是說(shuō)事務(wù)中的操作是原子性的。

  • Session.commitTransaction()

提交事務(wù),將事務(wù)中對(duì)數(shù)據(jù)的修改進(jìn)行保存,然后結(jié)束當(dāng)前事務(wù),一次事務(wù)在提交之前的數(shù)據(jù)操作對(duì)外都是不可見的。

  • Session.abortTransaction()

中止當(dāng)前的事務(wù),并將事務(wù)中執(zhí)行過(guò)的數(shù)據(jù)修改回滾。

重試

當(dāng)事務(wù)運(yùn)行中報(bào)錯(cuò),catch 到的錯(cuò)誤對(duì)象中會(huì)包含一個(gè)屬性名為 errorLabels 的數(shù)組,當(dāng)這個(gè)數(shù)組中包含以下2個(gè)元素的時(shí)候,代表我們可以重新發(fā)起相應(yīng)的事務(wù)操作。

  • TransientTransactionError:出現(xiàn)在事務(wù)開啟以及隨后的數(shù)據(jù)操作階段
  • UnknownTransactionCommitResult:出現(xiàn)在提交事務(wù)階段

示例

經(jīng)過(guò)上面的鋪墊,你是不是已經(jīng)迫不及待想知道究竟應(yīng)該怎么寫代碼去完成一次完整的事務(wù)操作?下面我們就簡(jiǎn)單寫一個(gè)例子:

場(chǎng)景描述: 假設(shè)一個(gè)交易系統(tǒng)中有2張表——記錄商品的名稱、庫(kù)存數(shù)量等信息的表 commodities,和記錄訂單的表 orders。當(dāng)用戶下單的時(shí)候,首先要找到 commodities 表中對(duì)應(yīng)的商品,判斷庫(kù)存數(shù)量是否滿足該筆訂單的需求,是的話則減去相應(yīng)的值,然后在 orders 表中插入一條訂單數(shù)據(jù)。在高并發(fā)場(chǎng)景下,可能在查詢庫(kù)存數(shù)量和減少庫(kù)存的過(guò)程中,又收到了一次新的創(chuàng)建訂單請(qǐng)求,這個(gè)時(shí)候可能就會(huì)出問(wèn)題,因?yàn)樾碌恼?qǐng)求在查詢庫(kù)存的時(shí)候,上一次操作還未完成減少庫(kù)存的操作,這個(gè)時(shí)候查詢到的庫(kù)存數(shù)量可能是充足的,于是開始執(zhí)行后續(xù)的操作,實(shí)際上可能上一次操作減少了庫(kù)存后,庫(kù)存的數(shù)量就已經(jīng)不足了,于是新的下單請(qǐng)求可能就會(huì)導(dǎo)致實(shí)際創(chuàng)建的訂單數(shù)量超過(guò)庫(kù)存數(shù)量。

以往要解決這個(gè)問(wèn)題,我們可以用給商品數(shù)據(jù)“加鎖”的方式,比如基于 Redis 的各種鎖,同一時(shí)刻只允許一個(gè)訂單操作一個(gè)商品數(shù)據(jù),這種方案能解決問(wèn)題,缺點(diǎn)就是代碼更復(fù)雜了,并且性能會(huì)比較低。如果用數(shù)據(jù)庫(kù)事務(wù)的方式就可以簡(jiǎn)潔很多:

commodities 表數(shù)據(jù)(stock 為庫(kù)存):

 

  1. "_id" : ObjectId("5af0776263426f87dd69319a"), "name" : "滅霸原味手套""stock" : 5 } 
  2. "_id" : ObjectId("5af0776263426f87dd693198"), "name" : "雷神專用鐵錘""stock" : 2 } 

 

orders 表數(shù)據(jù):

 

  1. "_id" : ObjectId("5af07daa051d92f02462644c"), "commodity": ObjectId("5af0776263426f87dd69319a"), "amount": 2 } 
  2. "_id" : ObjectId("5af07daa051d92f02462644b"), "commodity": ObjectId("5af0776263426f87dd693198"), "amount": 3 } 

 

通過(guò)一次事務(wù)完成創(chuàng)建訂單操作(mongo Shell):

 

  1. // 執(zhí)行 txnFunc 并且在遇到 TransientTransactionError 的時(shí)候重試 
  2. function runTransactionWithRetry(txnFunc, session) { 
  3.   while (true) { 
  4.     try { 
  5.       txnFunc(session); // 執(zhí)行事務(wù) 
  6.       break; 
  7.     } catch (error) { 
  8.       if ( 
  9.         error.hasOwnProperty('errorLabels') && 
  10.         error.errorLabels.includes('TransientTransactionError'
  11.       ) { 
  12.         print('TransientTransactionError, retrying transaction ...'); 
  13.         continue
  14.       } else { 
  15.         throw error; 
  16.       } 
  17.     } 
  18.   } 
  19.  
  20. // 提交事務(wù)并且在遇到 UnknownTransactionCommitResult 的時(shí)候重試 
  21. function commitWithRetry(session) { 
  22.   while (true) { 
  23.     try { 
  24.       session.commitTransaction(); 
  25.       print('Transaction committed.'); 
  26.       break; 
  27.     } catch (error) { 
  28.       if ( 
  29.         error.hasOwnProperty('errorLabels') && 
  30.         error.errorLabels.includes('UnknownTransactionCommitResult'
  31.       ) { 
  32.         print('UnknownTransactionCommitResult, retrying commit operation ...'); 
  33.         continue
  34.       } else { 
  35.         print('Error during commit ...'); 
  36.         throw error; 
  37.       } 
  38.     } 
  39.   } 
  40.  
  41. // 在一次事務(wù)中完成創(chuàng)建訂單操作 
  42. function createOrder(session) { 
  43.   var commoditiesCollection = session.getDatabase('mall').commodities; 
  44.   var ordersCollection = session.getDatabase('mall').orders; 
  45.   // 假設(shè)該筆訂單中商品的數(shù)量 
  46.   var orderAmount = 3; 
  47.   // 假設(shè)商品的ID 
  48.   var commodityID = ObjectId('5af0776263426f87dd69319a'); 
  49.  
  50.   session.startTransaction({ 
  51.     readConcern: { level'snapshot' }, 
  52.     writeConcern: { w: 'majority' }, 
  53.   }); 
  54.  
  55.   try { 
  56.     var { stock } = commoditiesCollection.findOne({ _id: commodityID }); 
  57.     if (stock < orderAmount) { 
  58.       print('Stock is not enough'); 
  59.       session.abortTransaction(); 
  60.       throw new Error('Stock is not enough'); 
  61.     } 
  62.     commoditiesCollection.updateOne( 
  63.       { _id: commodityID }, 
  64.       { $inc: { stock: -orderAmount } } 
  65.     ); 
  66.     ordersCollection.insertOne({ 
  67.       commodity: commodityID, 
  68.       amount: orderAmount, 
  69.     }); 
  70.   } catch (error) { 
  71.     print('Caught exception during transaction, aborting.'); 
  72.     session.abortTransaction(); 
  73.     throw error; 
  74.   } 
  75.  
  76.   commitWithRetry(session); 
  77.  
  78. // 發(fā)起一次會(huì)話 
  79. var session = db.getMongo().startSession({ readPreference: { mode: 'primary' } }); 
  80.  
  81. try { 
  82.   runTransactionWithRetry(createOrder, session); 
  83. } catch (error) { 
  84.   // 錯(cuò)誤處理 
  85. } finally { 
  86.   session.endSession(); 

 

上面的代碼看著感覺很多,其實(shí) runTransactionWithRetry 和 commitWithRetry 這兩個(gè)函數(shù)都是可以抽離出來(lái)成為公共函數(shù)的,不需要每次操作都重復(fù)書寫。用上了事務(wù)之后,因?yàn)槭聞?wù)中的數(shù)據(jù)操作都是一次原子操作,所以我們就不需要考慮分布并發(fā)導(dǎo)致的數(shù)據(jù)一致性的問(wèn)題,是不是感覺簡(jiǎn)單了許多?

你可能注意到了,代碼中在執(zhí)行 startTransaction 的時(shí)候設(shè)置了兩個(gè)參數(shù)——readConcern 和 writeConcern,這是 MongoDB 讀寫操作的確認(rèn)級(jí)別,在這里用于在副本集中平衡數(shù)據(jù)讀寫操作的可靠性和性能,如果在這里展開就太多了,所以感興趣的朋友建議去閱讀官方文檔了解一下:

readConcern:

https://docs.mongodb.com/mast...

writeConcern:

https://docs.mongodb.com/mast... 

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2009-08-10 18:16:33

ICustomQuer.NET 4.0

2009-08-19 16:51:14

C# 4.0 dyna

2009-09-04 16:28:05

ASP.NET 4.0

2009-05-26 09:28:22

C# 4.0dynamic動(dòng)態(tài)類型

2009-12-30 10:21:36

.NET 4.0

2018-07-05 10:55:25

數(shù)據(jù)庫(kù)MongoDB 4.0多文檔事務(wù)

2009-05-26 11:15:31

C# 4.0dynamicVisual Stud

2010-01-05 09:26:13

.NET 4.0

2010-08-17 09:57:39

C#

2010-02-24 14:24:35

.NET 4.0

2009-08-18 09:37:42

ASP.NET 4.0

2011-01-14 10:27:18

C#.netasp.net

2019-08-26 18:45:59

RedisRedis4.0數(shù)據(jù)庫(kù)

2009-07-06 11:00:56

.NET 4.0新特性.NET

2010-05-25 08:34:10

C# 4.0

2009-08-13 09:46:49

C#歷史C# 4.0新特性

2024-08-15 08:00:00

MongoDB數(shù)據(jù)庫(kù)NoSQL

2012-05-18 14:36:50

Fedora 17桌面環(huán)境

2009-01-16 10:01:57

MySQL復(fù)制特性測(cè)試

2012-06-13 01:05:53

JavaRubyJVM
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 中文字幕在线视频免费观看 | 久久精品国产一区 | 国产一区二区电影网 | 91精品国产91久久久久久不卞 | 99亚洲精品视频 | 免费国产视频 | 欧美视频免费在线观看 | 亚洲一区二区视频在线播放 | 久久国产精品-久久精品 | 国产精品伦一区二区三级视频 | 欧美视频免费 | 日本一区二区高清不卡 | 成人免费网站在线 | 亚洲国产一区二区三区在线观看 | 亚洲激情在线观看 | 欧美国产精品一区二区三区 | 亚洲久久一区 | 精品日韩在线观看 | 在线欧美一区 | 亚洲成a| 香蕉久久av | 国产成人精品亚洲日本在线观看 | 中文字幕一区在线观看视频 | 精品免费国产视频 | 国产精品视频一二三 | 亚洲美女一区 | 国产精品亚洲一区二区三区在线 | 国产在线中文字幕 | 久久综合久 | 超碰91在线 | 亚洲精品9999 | 97av视频| 91九色视频 | 综合久久亚洲 | 欧美性受xxxx | 国产精品久久久久久吹潮 | 国产a区 | 国产视频久久久久 | 久久99久久98精品免观看软件 | 天天精品在线 | 成人久久一区 |