美文网首页LevelDbLevelDB
Leveldb之并发写入

Leveldb之并发写入

作者: ieasy_tm | 来源:发表于2017-08-24 15:53 被阅读61次

这篇文章想分析下Leveldb的写入流程以及Leveldb是如何处理并发写入问题的。阅读https://github.com/syndtr/goleveldb代码,从代码的逻辑看,每一次put操作都会new一个Batch对象,Batch的数据结构如下

func (db *DB) Put(key, value []byte, wo *opt.WriteOptions) error {
    b := new(Batch)
    b.Put(key, value)
    return db.Write(b, wo)
}
type Batch struct {
    data       []byte
    rLen, bLen int
    seq        uint64
    sync       bool
}

Batch中的data存放的是key和value, 然后数据长度: bLen, 这个k-v对的序列号: seq。

put.png

Put进来的key-value对写入Batch之后。key-value对将写到.log文件后立马写入到内存中memdb中。这个过程中涉及到将memdb中的写入sstable中。sstablememdb落盘后的磁盘存储结构。每次的write操作都有可能触发将immemdb落地操作:

func (db *DB) Write(b *Batch, wo *opt.WriteOptions) (err error) {
    ...
    // Set batch first seq number relative from last seq.
    b.seq = db.seq + 1

    // Write journal concurrently if it is large enough.
    if b.size() >= (128 << 10) {
        // Push the write batch to the journal writer
        select {
        case db.journalC <- b:
            // Write into memdb
            if berr := b.memReplay(mdb.DB); berr != nil {
                panic(berr)
            }
        case err = <-db.compPerErrC:
            return
        case _, _ = <-db.closeC:
            err = ErrClosed
            return
        }
        // Wait for journal writer
        select {
        case err = <-db.journalAckC:
            if err != nil {
                // Revert memdb if error detected
                if berr := b.revertMemReplay(mdb.DB); berr != nil {
                    panic(berr)
                }
                return
            }
        case _, _ = <-db.closeC:
            err = ErrClosed
            return
        }
    } else {
        err = db.writeJournal(b)
        if err != nil {
            return
        }
        if berr := b.memReplay(mdb.DB); berr != nil {
            panic(berr)
        }
    }

    // Set last seq number.
    db.addSeq(uint64(b.Len()))

    if b.size() >= mdbFree {
        db.rotateMem(0)
    }
    return
}
func (db *DB) flush(n int) (mdb *memDB, mdbFree int, err error) {
    delayed := false
    flush := func() (retry bool) {
        v := db.s.version()
        defer v.release()
        mdb = db.getEffectiveMem()
        defer func() {
            if retry {
                mdb.decref()
                mdb = nil
            }
        }()
        mdbFree = mdb.Free()
        switch {
        case v.tLen(0) >= db.s.o.GetWriteL0SlowdownTrigger() && !delayed:
            delayed = true
            time.Sleep(time.Millisecond)
        case mdbFree >= n:
            return false
        case v.tLen(0) >= db.s.o.GetWriteL0PauseTrigger():
            delayed = true
            err = db.compSendIdle(db.tcompCmdC)
            if err != nil {
                return false
            }
        default:
            // Allow memdb to grow if it has no entry.
            if mdb.Len() == 0 {
                mdbFree = n
            } else {
                mdb.decref()
                mdb, err = db.rotateMem(n)
                if err == nil {
                    mdbFree = mdb.Free()
                } else {
                    mdbFree = 0
                }
            }
            return false
        }
        return true
    }
    start := time.Now()
    for flush() {
    }
    if delayed {
        db.writeDelay += time.Since(start)
        db.writeDelayN++
    } else if db.writeDelayN > 0 {
        db.logf("db@write was delayed N·%d T·%v", db.writeDelayN, db.writeDelay)
        db.writeDelay = 0
        db.writeDelayN = 0
    }
    return
}

相关文章

  • Leveldb之并发写入

    这篇文章想分析下Leveldb的写入流程以及Leveldb是如何处理并发写入问题的。阅读https://githu...

  • leveldb(2) 并发写入

    写入流程 先加锁 往队列里加入数据(有可能有别的线程也加入数据) wait等待队首数据的线程被唤醒(此时其它数据可...

  • leveldb源码学习--memtable之Key

    leveldb中数据存储过程 当向leveldb写入数据时,首先将数据写入log文件,然后在写入memtable内...

  • 从leveldb中学编码技巧(2)

    leveldb提供的接口大部分都是线程安全的,包括write。当多个线程并发的调用write时,leveldb会保...

  • Redis之并发写入

    首先需要澄清一个事实:redis服务端是单线程处理客户端请求,也就是说客户端请求在服务端是串行化执行的,因此对服务...

  • 无标题文章

    大并发写入案例: 高并发,大数据量写入:先把数据写入到内存,积累到一定的量后,在定时或者定量的写入到磁盘(减少磁盘...

  • 利用GCD实现高效的文件读取

    需求思路 读取时候可以并发进行,读取后要返回读取状态。 写入文件时候不可以读取,并且文件写入时不可以并发的进行写入...

  • Fabric和CouchDB

    因为良好的追加写入性能,从比特币到以太坊再到Hyperledge的Fabric,都选用了LevelDB作为存储方案...

  • 使用leveldb模块

    LevelDB是谷歌开源的一个键值数据库,速度非常快,同时自动压缩数据。 起因是直接写入文本不方便第二次查找;写入...

  • [LevelDB/源码]Batch结构分析

    1.Batch的日常使用 batch的日常使用比较简单,使用Batch是提高leveldb写入性能的一个关键。 本...

网友评论

    本文标题:Leveldb之并发写入

    本文链接:https://www.haomeiwen.com/subject/qiredxtx.html