首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >sqlite3 db在只读触发SQLITE_BUSY中打开。

sqlite3 db在只读触发SQLITE_BUSY中打开。
EN

Stack Overflow用户
提问于 2020-04-08 15:26:45
回答 1查看 463关注 0票数 0

在只读环境中访问sqlite3 db时,我遇到了一些问题.在系统(Linux )上,有一个C++应用程序打开(并保持打开)到db的r/w连接。这个应用程序几乎连续地读写。

然后是一个PHP应用程序,每1秒打开一个只读连接来获取数据。它似乎一点问题也没有。

最后,有一个C应用程序应该打开到同一个db的另一个只读连接,并每隔几秒钟获取一次数据。这个应用程序经常运行在忙误差中。

据我从这个主题的帮助和其他问题中了解到的,这通常是在您尝试编写并且有另一个正在运行的事务时触发的。帮助也说“或在某些情况下阅读”,但我不知道这些案例是什么。

在C++应用程序仍在写入时尝试读取可能会发生,但据我所知,这不应导致此错误。读取查询将返回“旧”值,但应该没有问题地完成。

是对的吗?

在这里,返回此错误的C代码:

代码语言:javascript
复制
sqlite3 *db;
sqlite3_stmt *stmt;
char *qry=0;
int rc,value=0;

sqlite3_open_v2( "<mydb>", &db,SQLITE_OPEN_READONLY,NULL);
if (db == NULL)
{
    syslog(LOG_INFO,"Failed to open db\n");
    return -1;
}

qry=malloc(80);

if (qry) {
    sprintf (qry,"select value from dataplc WHERE address=%d",addr);

    rc = sqlite3_prepare_v2(db, qry,-1,&stmt,NULL);
    if (rc != SQLITE_OK) {
        syslog(LOG_INFO, "Failed to read: %d - rc: %d", addr,rc); // -5 -> db busy
        value = -1;
    } else {
        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
           value = sqlite3_column_int(stmt, 0);
           // do something
        }
    }
    free (qry);
}
sqlite3_finalize(stmt);   
sqlite3_close(db);
return value;

在代码或整个方法中有什么明显的错误吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-04-08 22:18:20

想必准备需要独占访问来读取数据库的当前架构数据。

一个可能的解决方案是使用sqlite3_busy_handler。

代码语言:javascript
复制
#define ABORT 0
#define CONTINUE 1

int busy_handler(void *data, int attempt) {
    printf("attempt: %d\n", attempt);
    if(attempt < 10) {
        sqlite3_sleep(250);
        return CONTINUE;
    }
    return ABORT;
}

sqlite3_prepare_v2调用之前设置它,如下所示:

代码语言:javascript
复制
sqlite3_busy_handler(db, busy_handler, NULL);

重试和睡眠时间的最大数量(以毫秒为单位)应根据您的需求来确定。

正如注释中已经提到的那样,检查sqlite3_open_v2调用的返回代码也是有意义的。

这样使用准备好的语句大概也是有意义的:

代码语言:javascript
复制
rc = sqlite3_prepare_v2(db, "SELECT value FROM dataplc WHERE address = ?1", -1, &stmt, NULL);

并将参数绑定到:

代码语言:javascript
复制
sqlite3_bind_int(stmt, 1, addr);

如何测试

为了进行测试,可以使用一个技巧,可以在这个很好的答案中找到:https://stackoverflow.com/a/57786662/2331445

例如,可以简单地将睡眠时间临时设置为1秒(在busy_handler sqlite3_sleep(1000);中)。

然后在准备之前添加一个getchar();

您的程序只是稍微修改了一下上面提到的点,如下所示:

代码语言:javascript
复制
#include <stdio.h>
#include <sys/syslog.h>
#include "sqlite3.h"

#define ABORT 0
#define CONTINUE 1

int busy_handler(void *data, int attempt) {
    printf("attempt: %d\n", attempt);
    if (attempt < 10) {
        sqlite3_sleep(1000);
        return CONTINUE;
    }
    return ABORT;
}

int read_from_db(int addr) {
    sqlite3 *db;
    sqlite3_stmt *stmt;
    int rc, value = 0;

    rc = sqlite3_open_v2("mydb", &db, SQLITE_OPEN_READONLY, NULL);
    if (rc != SQLITE_OK) {
        sqlite3_close(db);
        syslog(LOG_INFO, "Failed to open db\n");
        return -1;
    }

    sqlite3_busy_handler(db, busy_handler, NULL);

    printf("press enter to continue:\n");
    getchar(); //only for testing

    rc = sqlite3_prepare_v2(db, "SELECT value FROM dataplc WHERE address = ?1", -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        syslog(LOG_INFO, "Failed to read: %d - rc: %d", addr, rc); // -5 -> db busy
        value = -1;
    } else {
        sqlite3_bind_int(stmt, 1, addr);

        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
            value = sqlite3_column_int(stmt, 0);
            // do something
        }
    }

    sqlite3_finalize(stmt);
    sqlite3_close(db);
    return value;
}


int main(void) {
    int val = read_from_db(42);
    printf("result: %d\n", val);
    return 0;
}

在sqlite3命令行接口上,可以输入:

代码语言:javascript
复制
begin exclusive;

然后,在运行程序的控制台上,您可以按ENTER键。现在它将显示来自busy_handler的printfs。一旦我们用一个

代码语言:javascript
复制
commit;

返回结果。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61104320

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档