zoukankan      html  css  js  c++  java
  • SQL Server – Concurrency 并发控制

    前言

    以前写过相关的, 但这篇主要讲一下概念. 帮助理解

    Entity Framework with MySQL 学习笔记一(乐观并发)

    Asp.net core 学习笔记 ( ef core transaction scope & change level )

    sql server 学习笔记 (nested transaction 嵌套事务)

    SQL Server中锁与事务隔离级别 (深入浅出的文章, 以后写事务隔离级别可以参考, 借方这里先)

    并发与事务

    并发是指在同一个时间线上, 多人一起做事情. (比如我在修改一个文档, 你也在修改同一个文档) 

    在做信息处理的时候, 我们有一个"事务"的概念. 我们可以把它理解成一个信息处理的 "过程".

    比如: 

    在信息处理的时候, 最常见的情况是这样的.

    step1: 需要从数据库拿一些信息出来

    step2: 依据这些信息做逻辑判断, 逻辑计算, 结合新的 input, 得出最终要 insert/update 的数据

    step3: 把新的数据 insert/update to 数据库

    这 3 个 step 就是一个过程, 也就是一个事务. 

    并发的意思就是说, 在同一个时间上, 有很多人在做着许许多多的事务.

    数据库对事务有一个原子性规则, 在一个事务中, 可以读读写写多次, 如果间中发生任何问题, 事务会回滚, 测回所有写入的资料 (当然我们也可以在事务任何时候手到回滚.)

    并发诞生的问题

    在进行一个事务的时候, 我们希望不被任何其它事务"影响", 不然就乱了套了.

    比如,

    step 1: 从数据库拿出了数据.

    step 2: 通过数据做逻辑计算

    step 2.5: 这时另一个事务修改了我们 step 1 拿出来的数据. 

    这个我们 step 2 的逻辑计算就不准确了. 

    从而也导致了我们 step 3 update 进去的资料是错误的. 

    这就是并发导致的问题. 

    解决并发的问题

    为什么会乱? 因为没有规则, 所以需要制定规则

    一个最简单的规则, 单线程. 让每一个事务都排队. 一个一个处理. 

    这样就没有并发了咯. 但这当然不行, 太慢了丫. 所以 SQL Server 做了很多不同的 Lock 和 不同的 IsolationLevel 来适量的控制并发的情况.

    规则

    读了就不能写

    如果我们细看刚才的 step 1,2,3 

    它有 2 个点很重要, 第一就是读取出来的数据, 在事务结束以前都不可以被其它事务修改. 

    比如我 SELECT Name FROM Person WHERE Id = 1

    SQL Server 就会把这条 row 锁上, 不让其它事务 update/delete 它 (其它事务就排队, 等我完成了才能修改)

    如果我 SELECT Name FROM Person WHERE Age > 10 

    那么 SQL Server 会锁更大的范围, 同时也不能 insert Age > 10 的 row

    锁的范围越大, 受影响的事务就越多, 排队的事务多就以为着慢. 就回到了最初单线方案的问题. 

    所以这里就是一个我们需要自己控制的 trade-off.

    写了就不能读

    另外一个角度就是, 在一个事务过程中, 也许我们会多次读读写写. 

    而在我写入新数据后, 但事务还没有结束前, 这些新数据是不允许被其它事务读取的. 

    因为可能最终因为某些原因, 我想取消整个事务. 所以就有了一个反向, 写了就不能读的锁机制. 

    一样的道理, 锁的越多排队就越多就越慢. 

    死锁

    由于我们允许事务同时处理 (只是中间加了一些规则), 事务进行到一般的时候有可能会突然去排队.

    而这个排队会依赖另一个事务的结束, 好死不死遇到循环依赖...那么就死锁了. 

    我等你, 你等我. SQL Server 会发现这个死锁的情况, 然后自动干掉其中一个事务, 去解锁. 

    但是我们应该要尽可能在业务层级上去避免这种事情的诞生. 不然又影响数据库性能了. 

    总结 & 乐观并发

    并发的本质就是为了快, 缺乏管理, 导致了混乱. 

    快是我们追求的, 不要乱也是我们追求的, 鱼与熊掌不能兼得, 所以要懂得 trade-off.

    乐观并发

    上面我们说的各种锁的机制都是属于悲观并发.

    有一种叫乐观并发的规则. 它可以在不锁 (不用排队) 的情况下做到一定层度的管理规则 (解决乱的问题)

    举例, 我们想做一个事务处理

    step 1: 读取资料

    step 2: 做逻辑计算

    step 3: update 数据

    一般上我们需要用事务去完成上面 3 个步骤. 但是乐观并发不用.

    它就跑语句, 读, 然后计算, 然后 update.

    回到并发的问题, 如果间中有其它人对数据做了修改怎么办?

    它有一个前提, 读 1 row, update 同一个 row. 这种情况下才可以用乐观并发机制, 超出这个范围还是得用事务和锁.

    在 row 里面加入一个 row version column 做 version control.  每当 row update 的时候 row version 就修改

    然后在 udpate 的时候判断 row version 是否和上一次获取的一样. 

    如果一样就表示期间没有人修改过资料. 如果不一样就更新失败. 

    这是一个很聪明的机制, 因为这种场景是最多的. 用一个巧思来解决总比锁的成本小很多. 但是它也只能解决简单的情况, 遇到复杂一些的最终还是得锁的.

  • 相关阅读:
    Sanic二十七:Sanic + tortoise-orm 之Q对象
    Sanic二十六:Sanic + tortoise-orm 之Model、QuerySet提供的查询方法
    Sanic二十五:Sanic + tortoise-orm 之表关联
    Sanic二十四:Sanic + tortoise-orm 之常用字段类型和参数
    Sanic二十三:Sanic + tortoise-orm 之父类Field的参数、属性、方法
    Sanic二十二:Sanic + tortoise-orm 之使用aerich执行数据库迁移
    Sanic二十一:Sanic + tortoise-orm 之模型定义
    [JavaScript]Promise:异步编程
    手把手教你搭建一个SpringBoot工程
    Android 11(R) Power HAL AIDL简析 -- 基本接口
  • 原文地址:https://www.cnblogs.com/keatkeat/p/15555764.html
Copyright © 2011-2022 走看看