@@ -36,12 +36,6 @@ const (
3636// All data access is performed through transactions which can be obtained through the DB.
3737// All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called.
3838type DB struct {
39- // Put `stats` at the first field to ensure it's 64-bit aligned. Note that
40- // the first word in an allocated struct can be relied upon to be 64-bit
41- // aligned. Refer to https://pkg.go.dev/sync/atomic#pkg-note-BUG. Also
42- // refer to discussion in https://github.com/etcd-io/bbolt/issues/577.
43- stats Stats
44-
4539 // When enabled, the database will perform a Check() after every commit.
4640 // A panic is issued if the database is in an inconsistent state. This
4741 // flag has a large performance impact so it should only be used for
@@ -138,6 +132,7 @@ type DB struct {
138132 pageSize int
139133 opened bool
140134 rwtx * Tx
135+ stats * Stats
141136
142137 freelist fl.Interface
143138 freelistLoad sync.Once
@@ -203,6 +198,10 @@ func Open(path string, mode os.FileMode, options *Options) (db *DB, err error) {
203198 db .MaxBatchDelay = common .DefaultMaxBatchDelay
204199 db .AllocSize = common .DefaultAllocSize
205200
201+ if ! options .NoStatistics {
202+ db .stats = new (Stats )
203+ }
204+
206205 if options .Logger == nil {
207206 db .logger = getDiscardLogger ()
208207 } else {
@@ -430,7 +429,9 @@ func (db *DB) loadFreelist() {
430429 // Read free list from freelist page.
431430 db .freelist .Read (db .page (db .meta ().Freelist ()))
432431 }
433- db .stats .FreePageN = db .freelist .FreeCount ()
432+ if db .stats != nil {
433+ db .stats .FreePageN = db .freelist .FreeCount ()
434+ }
434435 })
435436}
436437
@@ -808,10 +809,12 @@ func (db *DB) beginTx() (*Tx, error) {
808809 db .metalock .Unlock ()
809810
810811 // Update the transaction stats.
811- db .statlock .Lock ()
812- db .stats .TxN ++
813- db .stats .OpenTxN ++
814- db .statlock .Unlock ()
812+ if db .stats != nil {
813+ db .statlock .Lock ()
814+ db .stats .TxN ++
815+ db .stats .OpenTxN ++
816+ db .statlock .Unlock ()
817+ }
815818
816819 return t , nil
817820}
@@ -867,10 +870,12 @@ func (db *DB) removeTx(tx *Tx) {
867870 db .metalock .Unlock ()
868871
869872 // Merge statistics.
870- db .statlock .Lock ()
871- db .stats .OpenTxN --
872- db .stats .TxStats .add (& tx .stats )
873- db .statlock .Unlock ()
873+ if db .stats != nil {
874+ db .statlock .Lock ()
875+ db .stats .OpenTxN --
876+ db .stats .TxStats .add (& tx .stats )
877+ db .statlock .Unlock ()
878+ }
874879}
875880
876881// Update executes a function within the context of a read-write managed transaction.
@@ -1088,9 +1093,13 @@ func (db *DB) Sync() (err error) {
10881093// Stats retrieves ongoing performance stats for the database.
10891094// This is only updated when a transaction closes.
10901095func (db * DB ) Stats () Stats {
1091- db .statlock .RLock ()
1092- defer db .statlock .RUnlock ()
1093- return db .stats
1096+ var s Stats
1097+ if db .stats != nil {
1098+ db .statlock .RLock ()
1099+ s = * db .stats
1100+ db .statlock .RUnlock ()
1101+ }
1102+ return s
10941103}
10951104
10961105// This is for internal access to the raw data bytes from the C cursor, use
@@ -1340,15 +1349,20 @@ type Options struct {
13401349
13411350 // Logger is the logger used for bbolt.
13421351 Logger Logger
1352+
1353+ // NoStatistics turns off statistics collection, Stats method will
1354+ // return empty structure in this case. This can be beneficial for
1355+ // performance under high-concurrency read-only transactions.
1356+ NoStatistics bool
13431357}
13441358
13451359func (o * Options ) String () string {
13461360 if o == nil {
13471361 return "{}"
13481362 }
13491363
1350- return fmt .Sprintf ("{Timeout: %s, NoGrowSync: %t, NoFreelistSync: %t, PreLoadFreelist: %t, FreelistType: %s, ReadOnly: %t, MmapFlags: %x, InitialMmapSize: %d, PageSize: %d, MaxSize: %d, NoSync: %t, OpenFile: %p, Mlock: %t, Logger: %p}" ,
1351- o .Timeout , o .NoGrowSync , o .NoFreelistSync , o .PreLoadFreelist , o .FreelistType , o .ReadOnly , o .MmapFlags , o .InitialMmapSize , o .PageSize , o .MaxSize , o .NoSync , o .OpenFile , o .Mlock , o .Logger )
1364+ return fmt .Sprintf ("{Timeout: %s, NoGrowSync: %t, NoFreelistSync: %t, PreLoadFreelist: %t, FreelistType: %s, ReadOnly: %t, MmapFlags: %x, InitialMmapSize: %d, PageSize: %d, MaxSize: %d, NoSync: %t, OpenFile: %p, Mlock: %t, Logger: %p, NoStatistics: %t }" ,
1365+ o .Timeout , o .NoGrowSync , o .NoFreelistSync , o .PreLoadFreelist , o .FreelistType , o .ReadOnly , o .MmapFlags , o .InitialMmapSize , o .PageSize , o .MaxSize , o .NoSync , o .OpenFile , o .Mlock , o .Logger , o . NoStatistics )
13521366
13531367}
13541368
0 commit comments