当前位置: 首页 > article >正文

什么是数据库的锁?怎么实现?

数据库的锁是数据库管理系统(DBMS)用来控制多个并发事务访问共享数据的一种机制。它通过限制对数据的访问,确保在并发环境下数据的完整性和一致性。锁的主要目的是防止多个事务在同一时间对相同的数据进行修改,避免出现数据冲突和不一致的问题。

锁的类型
  1. 行级锁(Row-level lock)

    • 只对某一行数据进行锁定,其他事务可以访问该表中的其他行数据。
    • 优点:并发性高,多个事务可以同时对不同的数据行进行操作。
    • 缺点:锁管理开销较大,可能导致性能下降。
  2. 表级锁(Table-level lock)

    • 锁定整个表,其他事务无法对表中的任何行进行操作。
    • 优点:锁管理简单,性能开销小。
    • 缺点:并发性差,多个事务不能同时访问表中的不同数据行。
  3. 页级锁(Page-level lock)

    • 锁定数据库中存储的一个数据页,通常是磁盘上的一页(如4KB或8KB)。
    • 优点:在行级锁和表级锁之间提供平衡,锁的粒度适中。
    • 缺点:可能比行级锁更容易导致阻塞,但又比表级锁更细粒度。
锁的粒度
  1. 共享锁(Shared lock)

    • 允许多个事务同时读取某数据,但不允许修改。其他事务可以读取该数据,但不能写入。
  2. 排他锁(Exclusive lock)

    • 当事务获得排他锁时,其他事务既不能读取也不能修改该数据。适用于修改数据的场景。
  3. 意向锁(Intent lock)

    • 用于在行级锁和表级锁之间提供一种锁的指示。例如,事务想要对某一行加锁时,可以先在表上加意向锁,表示它将对某一行加锁,从而避免其他事务同时在同一表上加排他锁。
  4. 乐观锁(Optimistic Locking)

    • 基于数据版本控制,不真正加锁,而是在数据修改时检查数据是否被其他事务修改过。乐观锁适用于并发冲突较少的场景。
  5. 悲观锁(Pessimistic Locking)

    • 假设并发事务会发生冲突,因此在事务操作数据时,会主动对数据进行加锁,防止其他事务修改。
锁的模式
  1. 死锁(Deadlock)

    • 当两个或多个事务互相等待对方持有的锁,导致系统无法继续执行时,称为死锁。数据库管理系统需要有机制检测和解决死锁问题,通常会选择回滚某个事务来解除死锁。
  2. 锁升级和锁降级

    • 锁升级:指当事务需要更大粒度的锁时,从行锁升级为表锁等。
    • 锁降级:指当事务操作减少时,锁从较高粒度(如表锁)降级为较低粒度(如行锁)。

举个sqlite3和c语言实现数据库锁的例子:

基本流程:

  1. SQLite的锁机制:SQLite会自动管理锁(行锁、表锁),但你可以通过事务和不同的隔离级别来影响锁的行为。
  2. 事务控制:通过显式的BEGINCOMMITROLLBACK等SQL语句来手动控制锁的行为。

示例代码:使用SQLite的事务控制来模拟锁行为

#include <stdio.h>
#include <sqlite3.h>

#define DB_NAME "test.db"

int create_db(sqlite3 *db) {
    const char *create_table_sql = 
        "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT);";
    
    char *err_msg = 0;
    int rc = sqlite3_exec(db, create_table_sql, 0, 0, &err_msg);
    
    if (rc != SQLITE_OK) {
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
        return rc;
    }
    return SQLITE_OK;
}

int insert_user(sqlite3 *db, const char *name) {
    const char *insert_sql = "INSERT INTO users (name) VALUES (?);";
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(db, insert_sql, -1, &stmt, 0);
    
    if (rc != SQLITE_OK) {
        fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
        return rc;
    }

    sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    
    if (rc != SQLITE_DONE) {
        fprintf(stderr, "Execution failed: %s\n", sqlite3_errmsg(db));
        sqlite3_finalize(stmt);
        return rc;
    }

    sqlite3_finalize(stmt);
    return SQLITE_OK;
}

void simulate_locking_behavior() {
    sqlite3 *db1, *db2;
    int rc1, rc2;

    // Open two separate database connections
    rc1 = sqlite3_open(DB_NAME, &db1);
    if (rc1) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db1));
        return;
    }

    rc2 = sqlite3_open(DB_NAME, &db2);
    if (rc2) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db2));
        sqlite3_close(db1);
        return;
    }

    // Create a table if it does not exist
    create_db(db1);

    // Start a transaction in the first connection (db1)
    sqlite3_exec(db1, "BEGIN IMMEDIATE;", 0, 0, 0);  // IMMEDIATE lock

    printf("Transaction started on db1...\n");

    // Simulate some work by inserting a row into db1
    insert_user(db1, "Alice");

    // Try to start another transaction in db2 (this will block until db1 commits or rolls back)
    sqlite3_exec(db2, "BEGIN IMMEDIATE;", 0, 0, 0);  // Will be blocked by db1

    printf("Transaction blocked on db2...\n");

    // Commit the transaction on db1
    sqlite3_exec(db1, "COMMIT;", 0, 0, 0);
    printf("Transaction committed on db1...\n");

    // db2 can now proceed
    sqlite3_exec(db2, "COMMIT;", 0, 0, 0);
    printf("Transaction committed on db2...\n");

    sqlite3_close(db1);
    sqlite3_close(db2);
}

int main() {
    simulate_locking_behavior();
    return 0;
}

代码说明:

  1. 数据库连接和创建表

    • sqlite3_open 用于打开数据库。如果数据库不存在,SQLite会自动创建一个新的数据库文件。
    • create_db 用于创建一个简单的表,表中包含id(主键)和name字段。
  2. 事务操作

    • 通过 BEGIN IMMEDIATE; 开始一个事务,IMMEDIATE模式会在事务开始时锁定数据库。
    • sqlite3_exec 执行 SQL 语句,比如 BEGIN IMMEDIATECOMMIT等。
  3. 模拟锁定行为

    • 我们在两个独立的数据库连接(db1 和 db2)上分别开始事务。
    • db1上进行插入操作并提交,db2的事务将被阻塞,直到db1提交或回滚。
    • 最终,两个事务成功提交。

锁的行为:

  1. 在 db1 上启动事务时,BEGIN IMMEDIATE 会锁定整个数据库,防止其他事务对数据库进行修改,直到提交或回滚。
  2. db2 上的事务会被阻塞,直到 db1 的事务结束。
  3. SQLite内部使用的锁会根据事务类型(共享锁、排他锁等)来控制对数据库的访问。

 运行结果:

Transaction started on db1...
Transaction blocked on db2...
Transaction committed on db1...
Transaction committed on db2...

锁的种类:

  • IMMEDIATE:此模式会在事务开始时获得写锁,这意味着其他事务不能同时对数据库进行写操作,但仍然允许读取。
  • EXCLUSIVE:此模式会在事务开始时获得排他锁,其他事务不能访问数据库。

通过这些方式,你可以控制SQLite数据库在并发环境下的访问权限,并避免数据冲突。


http://www.kler.cn/a/456398.html

相关文章:

  • odoo中@api.model, @api.depends和@api.onchange 装饰器的区别
  • lua和C API库一些记录
  • LeetCode每日三题(六)数组
  • 算法基础一:冒泡排序
  • 07 基于OpenAMP的核间通信方案
  • 影刀进阶应用 | 知乎发布想法
  • 【教程】如何编译指定版本Spark
  • [Hive]七 Hive 内核
  • Kotlin 协程基础知识总结三 —— 协程上下文与异常处理
  • AutoFOX:一种冠状动脉X线造影与OCT的自动化跨模态3D融合框架|文献速递-视觉大模型医疗图像应用
  • IDEA 搭建 SpringBoot 项目之配置 Maven
  • 打造RAG系统:四大向量数据库Milvus、Faiss、Elasticsearch、Chroma 全面对比与选型指南
  • 【新方法】通过清华镜像源加速 PyTorch GPU 2.5安装及 CUDA 版本选择指南
  • 从CreateDialogIndirectParam起---我与大模型对话
  • 使用机器学习在单细胞水平识别肿瘤细胞
  • 【Halcon】数据结构汇总
  • 从0入门自主空中机器人-3-【环境与常用软件安装】
  • 关于最新MySQL9.0.1版本zip自配(通用)版下载、安装、环境配置
  • 1.business english--build rapport
  • 分類タスクの評価指標をわかりやすく解説!
  • 【畅购电商】项目总结
  • 计算机毕业设计Python+Spark知识图谱酒店推荐系统 酒店价格预测系统 酒店可视化 酒店爬虫 酒店大数据 neo4j知识图谱 深度学习 机器学习
  • 机器学习和深度学习中的种子设置
  • 【泰克生物】酵母展示技术在纳米抗体筛选中的应用:优化抗体筛库策略
  • C++入门基础(下)
  • C语言明示常量