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

打印 上一主题 下一主题

主题 1047|帖子 1047|积分 3141

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

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


  • 行级锁(Row-level lock)

    • 只对某一行数据进行锁定,其他事务可以访问该表中的其他行数据。
    • 优点:并发性高,多个事务可以同时对差别的数据行进行操作。
    • 缺点:锁管理开销较大,大概导致性能降落。

  • 表级锁(Table-level lock)

    • 锁定整个表,其他事务无法对表中的任何行进行操作。
    • 优点:锁管理简朴,性能开销小。
    • 缺点:并发性差,多个事务不能同时访问表中的差别数据行。

  • 页级锁(Page-level lock)

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

锁的粒度


  • 共享锁(Shared lock)

    • 允许多个事务同时读取某数据,但不允许修改。其他事务可以读取该数据,但不能写入。

  • 排他锁(Exclusive lock)

    • 当事务获得排他锁时,其他事务既不能读取也不能修改该数据。适用于修改数据的场景。

  • 意向锁(Intent lock)

    • 用于在行级锁和表级锁之间提供一种锁的指示。比方,事务想要对某一行加锁时,可以先在表上加意向锁,表示它将对某一行加锁,从而避免其他事务同时在同一表上加排他锁。

  • 乐观锁(Optimistic Locking)

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

  • 灰心锁(Pessimistic Locking)

    • 假设并发事务会发生冲突,因此在事务操作数据时,会主动对数据进行加锁,防止其他事务修改。

锁的模式


  • 死锁(Deadlock)

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

  • 锁升级和锁降级

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


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


根本流程:


  • SQLite的锁机制:SQLite会主动管理锁(行锁、表锁),但你可以通过事务和差别的隔离级别来影响锁的举动。
  • 事务控制:通过显式的BEGIN, COMMIT, ROLLBACK等SQL语句来手动控制锁的举动。
示例代码:利用SQLite的事务控制来模拟锁举动

  1. #include <stdio.h>
  2. #include <sqlite3.h>
  3. #define DB_NAME "test.db"
  4. int create_db(sqlite3 *db) {
  5.     const char *create_table_sql =
  6.         "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT);";
  7.    
  8.     char *err_msg = 0;
  9.     int rc = sqlite3_exec(db, create_table_sql, 0, 0, &err_msg);
  10.    
  11.     if (rc != SQLITE_OK) {
  12.         fprintf(stderr, "SQL error: %s\n", err_msg);
  13.         sqlite3_free(err_msg);
  14.         return rc;
  15.     }
  16.     return SQLITE_OK;
  17. }
  18. int insert_user(sqlite3 *db, const char *name) {
  19.     const char *insert_sql = "INSERT INTO users (name) VALUES (?);";
  20.     sqlite3_stmt *stmt;
  21.     int rc = sqlite3_prepare_v2(db, insert_sql, -1, &stmt, 0);
  22.    
  23.     if (rc != SQLITE_OK) {
  24.         fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
  25.         return rc;
  26.     }
  27.     sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);
  28.    
  29.     rc = sqlite3_step(stmt);
  30.    
  31.     if (rc != SQLITE_DONE) {
  32.         fprintf(stderr, "Execution failed: %s\n", sqlite3_errmsg(db));
  33.         sqlite3_finalize(stmt);
  34.         return rc;
  35.     }
  36.     sqlite3_finalize(stmt);
  37.     return SQLITE_OK;
  38. }
  39. void simulate_locking_behavior() {
  40.     sqlite3 *db1, *db2;
  41.     int rc1, rc2;
  42.     // Open two separate database connections
  43.     rc1 = sqlite3_open(DB_NAME, &db1);
  44.     if (rc1) {
  45.         fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db1));
  46.         return;
  47.     }
  48.     rc2 = sqlite3_open(DB_NAME, &db2);
  49.     if (rc2) {
  50.         fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db2));
  51.         sqlite3_close(db1);
  52.         return;
  53.     }
  54.     // Create a table if it does not exist
  55.     create_db(db1);
  56.     // Start a transaction in the first connection (db1)
  57.     sqlite3_exec(db1, "BEGIN IMMEDIATE;", 0, 0, 0);  // IMMEDIATE lock
  58.     printf("Transaction started on db1...\n");
  59.     // Simulate some work by inserting a row into db1
  60.     insert_user(db1, "Alice");
  61.     // Try to start another transaction in db2 (this will block until db1 commits or rolls back)
  62.     sqlite3_exec(db2, "BEGIN IMMEDIATE;", 0, 0, 0);  // Will be blocked by db1
  63.     printf("Transaction blocked on db2...\n");
  64.     // Commit the transaction on db1
  65.     sqlite3_exec(db1, "COMMIT;", 0, 0, 0);
  66.     printf("Transaction committed on db1...\n");
  67.     // db2 can now proceed
  68.     sqlite3_exec(db2, "COMMIT;", 0, 0, 0);
  69.     printf("Transaction committed on db2...\n");
  70.     sqlite3_close(db1);
  71.     sqlite3_close(db2);
  72. }
  73. int main() {
  74.     simulate_locking_behavior();
  75.     return 0;
  76. }
复制代码
代码说明:


  • 数据库连接和创建表

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

  • 事务操作

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

  • 模拟锁定举动

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

锁的举动:


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

 运行结果:
  1. Transaction started on db1...
  2. Transaction blocked on db2...
  3. Transaction committed on db1...
  4. Transaction committed on db2...
复制代码

锁的种类:



  • IMMEDIATE:此模式会在事务开始时获得写锁,这意味着其他事务不能同时对数据库进行写操作,但仍然允许读取。
  • EXCLUSIVE:此模式会在事务开始时获得排他锁,其他事务不能访问数据库。
通过这些方式,你可以控制SQLite数据库在并发情况下的访问权限,并避免数据冲突。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表