Linux下sqlite3多线程和串行模式

sqlite3支持三种模式:单线程模式,多线程模式和串行模式。

  • 模式可在编译,启动时或运行时设置,一般来讲,启动时的设置会覆盖编译时的设置,运行时则会覆盖启动时或编译时的设置。但是一旦单线程模式被设置之后就不能再被覆盖了。

  • 编译时可通过SQLITE_THREADSAFE设置模式。sqlite3标准 发行版本默认设置为SQLITE_THREADSAFE=1, 即串行模式。SQLITE_THREADSAFE=2为多线程模式,SQLITE_THREADSAFE=0为单线程模式,当设置为0时,即使初始化或运行时设置为其他模式,也会保持单线程模式。

  • 启动时是指在sqlite3在初始化之前调用sqlite3_config()函数设置,参数为SQLITE_CONFIG_SINGLETHREAD,SQLITE_CONFIG_MULTITHREAD或SQLITE_CONFIG_SERIALIZED。

  • sqlite3初始化指的是调用sqlite3_initalize(),该函数会在调用sqlite3_open_v2()时调用。运行时也就是程序第一次(仅第一次调用有效)调用该函数创建数据库连接时,通过设置第三个参数为设置不同模式,SQLITE_OPEN_NOMUTEX为多线程模式,SQLITE_OPEN_FULLMUTEX为串行模式。

串行模式

串行模式支持多线程操作,但是必须统一使用一个全局的数据库连接,这一点非常重要。串行模式会打开sqlite3所有的锁,在同一时刻保证只有一个线程能访问。这里可以理解为只有一条指向数据库的连接,多个线程的请求将会在该连接上串行传输。

多线程模式

  1. 多线程模式支持线程并发操作,但也有例外,因为在该模式下,sqlite3打开了bCoreMutex锁,关闭了bFullMutex锁,也就禁止了多个线程并发使用同一个数据库连接和perpared statement, perpared statement可以简单的使用信号量进行互斥。

  2. 使用多线程模式,除了需要设置模式外,还必须在执行数据库操作前调用sqlite3_busy_handler()或sqlite3_busy_timeout() 。这两个函数会判断sqlite是否处于SQLITE_BUSY状态,是的话将进行sleep等待。区别在于sqlite3_busy_handler()需要自己实现等待的处理,而sqlite3_busy_timeout()实际上是调用了sqlite3_busy_handler()并使用一个默认的等待处理函数。

  3. sqlite3_busy_timeout()通过指定最大超时时间进行等待。根据sqlite3_busy_timeout()源码,需要注意,如果系统未定义HAVE_USLEEP或定定义为false,则超时时间必须指定为大于1000且是它的整数倍。
    无论有无定义,使用sqlite3_busy_timeout()的等待时间都是该函数的算法实现,所以如果需要完全按照自己的需求决定等待时间,可以使用sqlite3_busy_handler(),该函数需要自己实现等待处理函数,处理函数中即可指定等待时间。

  4. sqlite3_busy_timeout() 源码:

static int sqliteDefaultBusyCallback(  
    void *ptr,               /* Database connection */  
    int count                /* Number of times table has been busy */  
)  
{  
#if SQLITE_OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP)  
    static const u8 delays[] =  
        { 1, 2, 5, 10, 15, 20, 25, 25,  25,  50,  50, 100 };  
    static const u8 totals[] =  
        { 0, 1, 3,  8, 18, 33, 53, 78, 103, 128, 178, 228 };  
# define NDELAY (sizeof(delays)/sizeof(delays[0]))  
    sqlite3 *db = (sqlite3 *)ptr;  
    int timeout = db->busyTimeout;  
    int delay, prior;  
    assert( count>=0 );  
    if( count < NDELAY ){  
        delay = delays[count];  
        prior = totals[count];  
    }else{  
        delay = delays[NDELAY-1];  
        prior = totals[NDELAY-1] + delay*(count-(NDELAY-1));  
    }  
    if( prior + delay > timeout ){  
        delay = timeout - prior;  
        if( delay<=0 ) return 0;  
    }  
    sqlite3OsSleep(db->pVfs, delay*1000);  
    return 1;  
#else  
    sqlite3 *db = (sqlite3 *)ptr;  
    int timeout = ((sqlite3 *)ptr)->busyTimeout;  
    if( (count+1)*1000 > timeout ){  
        return 0;//1000>timeout,so timeout must bigger than 1000  
    }  
    sqlite3OsSleep(db->pVfs, 1000000);//1000ms  
    return 1;  
#endif  
}  

int sqlite3_busy_timeout(sqlite3 *db, int ms){  
    if( ms>0 ){  
        db->busyTimeout = ms;  
        sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db);  
    }else{  
        sqlite3_busy_handler(db, 0, 0);  
    }  
    return SQLITE_OK;  
}

Exmaple

    // 判断编译时模式,设置启动时模式为多线程模式
    int mode = sqlite3_threadsafe();
    if (mode == 0) {
        AIO_LOG_W("SQLite database is not compiled to be threadsafe");
        return AIO_ERR;
    }

    int ret = sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
    if (ret != SQLITE_OK) {
        AIO_LOG_E("setting sqlite thread safe mode to multithread failed: %d", ret);
        return AIO_ERR;
    }

    ... ...

    // 设置SQLITE_BUSY 处理,使用sqlite3_busy_timeout()

    #define SQLITE_EXE(db, sql, callback, ptr, msg) \
    {       \
        rc = sqlite3_busy_timeout(db, 10 * MSEC_PER_SEC); \
        if (rc != SQLITE_OK) {  \
            AIO_LOG_E("SQL error: %d, file: %s. func: %s, line: %d.\n", \
                      rc, __FILE__, __func__, __LINE__);    \
        }   \
        rc = sqlite3_exec(db, sql, callback, ptr, &msg);  \
        if (rc != SQLITE_OK) {  \
            AIO_LOG_E("SQL error: %s, file: %s. func: %s, line: %d.\n", \
                      msg, __FILE__, __func__, __LINE__); \
            sqlite3_free(msg);      \
        }       \
    }

    // 设置SQLITE_BUSY 处理,使用sqlite3_busy_handler()
    static int cb_sql_busy(void *ptr, int count)
    {
        int timeout = *((int *)ptr);

        (void)usleep(timeout);
        return 1;
    }

    static int sql_busy_check(sqlite3 *db, int ms)
    {
        if( ms > 0 )
            sqlite3_busy_handler(db, cb_sql_busy, (void*)&ms);
        else
            sqlite3_busy_handler(db, 0, 0);  

        return SQLITE_OK;
    }

    int my_sqlite_exe(...) {
        rc = sql_busy_check(db, 100);
        ... ...
        rc = sqlite3_exec(db, sql, NULL, NULL, &msg);
        ... ...
    }

你可能感兴趣的:(OpenWrt,Linux)