第三节 Begin()实现

此处需要说明一下:在boltdb中,事务的开启方法是绑定在DB对象上的,为了保证内容的完整性,我们还是把事务开启的Begin()方法补充到这个地方。

前面提到boltdb中事务分为两类,它的区分就是在开启事务时,根据传递的参数来内部执行不同的逻辑。

在读写事务中,开始事务时加锁,也就是db.rwlock.Lock()。在事务提交或者回滚时才释放锁:db.rwlock.UnLock()。同时也印证了我们前面说的,同一时刻只能有一个读写事务在执行。

  1. // Begin starts a new transaction.
  2. // Multiple read-only transactions can be used concurrently but only one
  3. // write transaction can be used at a time. Starting multiple write transactions
  4. // will cause the calls to block and be serialized until the current write
  5. // transaction finishes.
  6. //
  7. // Transactions should not be dependent on one another. Opening a read
  8. // transaction and a write transaction in the same goroutine can cause the
  9. // writer to deadlock because the database periodically needs to re-mmap itself
  10. // as it grows and it cannot do that while a read transaction is open.
  11. //
  12. // If a long running read transaction (for example, a snapshot transaction) is
  13. // needed, you might want to set DB.InitialMmapSize to a large enough value
  14. // to avoid potential blocking of write transaction.
  15. //
  16. // IMPORTANT: You must close read-only transactions after you are finished or
  17. // else the database will not reclaim old pages.
  18. func (db *DB) Begin(writable bool) (*Tx, error) {
  19. if writable {
  20. return db.beginRWTx()
  21. }
  22. return db.beginTx()
  23. }
  24. func (db *DB) beginTx() (*Tx, error) {
  25. // Lock the meta pages while we initialize the transaction. We obtain
  26. // the meta lock before the mmap lock because that's the order that the
  27. // write transaction will obtain them.
  28. db.metalock.Lock()
  29. // Obtain a read-only lock on the mmap. When the mmap is remapped it will
  30. // obtain a write lock so all transactions must finish before it can be
  31. // remapped.
  32. db.mmaplock.RLock()
  33. // Exit if the database is not open yet.
  34. if !db.opened {
  35. db.mmaplock.RUnlock()
  36. db.metalock.Unlock()
  37. return nil, ErrDatabaseNotOpen
  38. }
  39. // Create a transaction associated with the database.
  40. t := &Tx{}
  41. t.init(db)
  42. // Keep track of transaction until it closes.
  43. db.txs = append(db.txs, t)
  44. n := len(db.txs)
  45. // Unlock the meta pages.
  46. db.metalock.Unlock()
  47. // Update the transaction stats.
  48. db.statlock.Lock()
  49. db.stats.TxN++
  50. db.stats.OpenTxN = n
  51. db.statlock.Unlock()
  52. return t, nil
  53. }
  54. func (db *DB) beginRWTx() (*Tx, error) {
  55. // If the database was opened with Options.ReadOnly, return an error.
  56. if db.readOnly {
  57. return nil, ErrDatabaseReadOnly
  58. }
  59. // Obtain writer lock. This is released by the transaction when it closes.
  60. // This enforces only one writer transaction at a time.
  61. db.rwlock.Lock()
  62. // Once we have the writer lock then we can lock the meta pages so that
  63. // we can set up the transaction.
  64. db.metalock.Lock()
  65. defer db.metalock.Unlock()
  66. // Exit if the database is not open yet.
  67. if !db.opened {
  68. db.rwlock.Unlock()
  69. return nil, ErrDatabaseNotOpen
  70. }
  71. // Create a transaction associated with the database.
  72. t := &Tx{writable: true}
  73. t.init(db)
  74. db.rwtx = t
  75. // Free any pages associated with closed read-only transactions.
  76. var minid txid = 0xFFFFFFFFFFFFFFFF
  77. // 找到最小的事务id
  78. for _, t := range db.txs {
  79. if t.meta.txid < minid {
  80. minid = t.meta.txid
  81. }
  82. }
  83. if minid > 0 {
  84. // 将之前事务关联的page全部释放了,因为在只读事务中,没法释放,只读事务的页,因为可能当前的事务已经完成 ,但实际上其他的读事务还在用
  85. db.freelist.release(minid - 1)
  86. }
  87. return t, nil
  88. }