第三节 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. }

25. func (db *DB) beginTx() (*Tx, error) {
26. // Lock the meta pages while we initialize the transaction. We obtain
27. // the meta lock before the mmap lock because that's the order that the
28. // write transaction will obtain them.
29. db.metalock.Lock()

31. // Obtain a read-only lock on the mmap. When the mmap is remapped it will
32. // obtain a write lock so all transactions must finish before it can be
33. // remapped.
34. db.mmaplock.RLock()

36. // Exit if the database is not open yet.
37. if !db.opened {
38. db.mmaplock.RUnlock()
39. db.metalock.Unlock()
40. return nil, ErrDatabaseNotOpen
41. }

43. // Create a transaction associated with the database.
44. t := &Tx{}
45. t.init(db)

47. // Keep track of transaction until it closes.
48. db.txs = append(db.txs, t)
49. n := len(db.txs)

51. // Unlock the meta pages.
52. db.metalock.Unlock()

54. // Update the transaction stats.
55. db.statlock.Lock()
56. db.stats.TxN++
57. db.stats.OpenTxN = n
58. db.statlock.Unlock()

60. return t, nil
61. }

63. func (db *DB) beginRWTx() (*Tx, error) {
64. // If the database was opened with Options.ReadOnly, return an error.
65. if db.readOnly {
66. return nil, ErrDatabaseReadOnly
67. }

69. // Obtain writer lock. This is released by the transaction when it closes.
70. // This enforces only one writer transaction at a time.
71. db.rwlock.Lock()

73. // Once we have the writer lock then we can lock the meta pages so that
74. // we can set up the transaction.
75. db.metalock.Lock()
76. defer db.metalock.Unlock()

78. // Exit if the database is not open yet.
79. if !db.opened {
80. db.rwlock.Unlock()
81. return nil, ErrDatabaseNotOpen
82. }

84. // Create a transaction associated with the database.
85. t := &Tx{writable: true}
86. t.init(db)
87. db.rwtx = t

89. // Free any pages associated with closed read-only transactions.
90. var minid txid = 0xFFFFFFFFFFFFFFFF
91. // 找到最小的事务id
92. for _, t := range db.txs {
93. if t.meta.txid < minid {
94. minid = t.meta.txid
95. }
96. }
97. if minid > 0 {
98. // 将之前事务关联的page全部释放了,因为在只读事务中,没法释放,只读事务的页,因为可能当前的事务已经完成 ,但实际上其他的读事务还在用
99. db.freelist.release(minid - 1)
100. }

102. return t, nil
103. }