- 工程实践 - 《高并发系统正确性保障 - 锁的范式》

news2024/12/22 13:25:31

        本文属于专栏《构建工业级QPS百万级服务》系列简介-CSDN博客


        “锁”,在新华字段的解释是“加在门、箱子、抽屉等物体上的封缄器,要用专用的钥匙才能打开”。在计算机领域,可以抽象为:主体A,在物品W上,附加物品S(锁),让其他主体不能完整地使用

        所以我们在理解一个锁时,不管它是语言层面的如:C++、Java、Python,还是软件层面的如:数据库、内核,还是硬件层面的如:多核CPU。这些都可以抽象为上面这句话,只要理解的谁是主体A,谁是物品W,以及“不完整使用”到达什么程度,就能理解锁的作用。

        在计算机领域,锁的实现都是底层硬件支持,是通过缓存一致率协议,而该协议底层逻辑依然是“单写”。这很重要,不仅是计算机领域,在整个信息领域,要保证并发时数据不冲突,唯一的办法是,明确的界限内,只有单写。这个再深层的根本原因是,信息传递是有速度的。同理,如果信息传递是不需要时间的,且我们认为时间是离散的,那就不再需要锁,比如量子计算机,就是一个不需要锁的计算机,不过这不在我们讨论范围中。

        当理解了,锁的作用,以及锁的实现本质。计算机领域的一切锁,理解起来就十分轻松了。我会在软件层面、硬件层面举一些锁的例子,以及为了性能而添加的锁的附加工具。这里还有一个可以断言的,所有锁的附加工具,都是为了提高系统并发性能,注意这里的性能问题不是加锁和释放锁本身耗费资源太多,而是加锁之后业务代码持有时间太长,对其他线程阻塞导致的,以c++互斥锁为例,这个用户态的锁,在现代计算机,平均一次获取仅需要5-15ns。

       语言层的锁都是内核层的锁的封装,没有本质的区别,所以我们不再单独拿一个语言的锁来描述。软件层,我以内核和Mysql为例。

  • 内核层面:
    • 互斥锁:
      • 范式说明
        • 主体:线程
        • 物品:一个权限(用一个标识表示,如std::atomic_flag)
        • 不完整使用:一个主体获得该权限后,其他主体不再能获得
        • 附加解释:这里的权限,是业务逻辑的约定,比如约定获得这个权限的主体,可以修改变量a,那权限相应的命名为mutex_a。而这里的约定是编码者要遵守的,而不是编译器或者内核遵守的,也就是没有获取mutex_a的主体,修改a,编译器和内核都不会报错,但是这样的程序,运行起来,就像一个定时炸弹,随时出现意想不到的结果。
      • 作用:一个线程获取权限时,其他线程一定获取不了,且获取是同步阻塞的。注意这里阻塞时,会调用系统接口,把线程挂起
      • 常见附加工具
        • 条件变量
          • 场景:线程A获取锁,发现不满足处理条件,如果一直等待,那么线程B会阻塞。所以当不满足条件时,线程A释放锁,并将线程A挂起,当条件满足时,再将线程A放到 ready队列。
      • 实现:内核也是由硬件提供的原子操作集的指令支持
    • 读写锁:
      • 范式说明
        • 主体:线程
        • 物品:两个权限,共享权限,和独有权限(用两个标识表示,如两个原子变量)
        • 不完整使用:
          • 有主体“获取独有权限,或者已经发起独有权限申请”之后,其他主体不能获取共享权限
          • 没有主体“获取独有权限,或者已经发起独有权限申请”时,其他主体可以获取共享权限
          • 如果有主体A“获得了共享权限”,而主体B申请“获得独有权限”,需要等待A释放权限之后
        • 附加解释:读-写锁命名并不准确,这里要得不是读/写的权限,而是共享和独享的权限。
      • 作用:将权限分离,核心是在共享权限场景下,增加使用主体,从而增加了系统并行性
      • 实现:基于互斥锁,再加上内核提供接口支持,而内核也是由硬件提供的原子操作集的指令支持
    • 自旋锁:
      • 范式说明:与互斥锁完全一致。唯一的差别是互斥锁,获取不到时,线程被挂起等待通知。而自旋锁,是持续占有cpu,并尝试获取锁
  • Mysql数据库
    • 排他锁(排的是行/表/页/意向)
      • 范式说明
        • 主体:事务
        • 物品:行/表/页/意向的读写权限
        • 不完整使用:
          • 在读已提交的隔离级别下(Mysql默认隔离级别)。一个事务在获取“行/表/页/意向”锁之后,其他事务将不能读或写这段数据
        • 附加解释:这里和内核的互斥锁没有本质的区别。只是这里的权限,是行/表/页/意向的读写权,而内核中的权限是对一个标识的权限,而这个标识可以绑定可操作的任意的资源
      • 作用:一个事务执行时,阻止其他事务对指定数据的读写
    • 共享锁(享的是行/表/页/表中某个范围)
      • 范式说明
        • 主体:事务
        • 物品:行/表/页/意向的读写权限
        • 不完整使用:
          • 在读已提交的隔离级别下(Mysql默认隔离级别)。一个事务在获取“行/表/页/意向”锁之后,其他事务将不能写这段数据
        • 附加解释:这里可以理解为当前事务占有了,这部分数据的写权限,但没有占有读权限
  • 其他:
    • 乐观锁
      • 说明:乐观锁本质不是一把锁,而是“锁的方式”。核心思想是,主体先在数据A的副本上操作,操作完成判断有没有其他主体在过去一段时间也对数据A有操作,如果没有,就用副本替换原数据。这里要关注的是判断这个动作,是需要加锁的,无论是使用互斥锁,还是直接使用互斥锁的底层接口CAS,没有本质的区别,都是硬件层面提供的支持。
    • 无锁队列
      • 说明:部分语言或者三方库提供无锁队列,这里只是没有互斥锁,但是CAS是必须有的。所以所谓的无锁队列,多少有些噱头。还是那句话,多主体写,必须加锁,而保证锁的一致性,必须单写,所以性能也必受影响。

        前面描述了锁的作用,虽然锁的形态有所差异,但是基于这个范式,对作用的理解就不会有偏差。

        最后我们说说锁的实现。锁的支持来源于硬件层面,那硬件层面是如果支持锁的。硬件支持锁,本质就是,在CPU系统中,如何保证一个表示权限的变量,一定时间界限内,只被一个CPU核写入,且下一个CPU在写时,保证已经获取到最新的数值。那么这里有两个难点,怎么保证只有一个写,以及怎么保证每次写都是基于最新的值,这些都是缓存一致性协议完成的。

        缓存一致性本身是硬件层面的协议,它也经过几个版本的迭代,为了性能也迭代了规则。对于平时的软件开发,我们不需要去理解它所有的细节。只需要理解到,其核心思想是,当一个CPU核A写数据时,会广播信息告诉其他CPU,不要再写了,同时收集所有CPU中最新的值。等其他CPU反馈已经收到核A的信息后,核A才会基于最新的数据去写。而复杂,也是复杂在如何基于这个方式修改,提高性能,如果不是做这个层面的研究,倒是没有必要细枝末节都去熟悉。同样是分布式节点的一致性,对于软件开发者,花费精力去学习raft,倒是一个更划算的事。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1558107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

labelme的安装与使用以及如何将labelme标注的json格式关键点标签转为yolo格式的标签

有任何问题我们一起交流,让我们共同学习 标注的json格式以及转换后的yolo格式示例希望得到您的指导背景及代码可用范围一、yolo关键点检测数据集格式二、labelme的安装和使用(一)labelme的安装(二)labelme的使用 三、j…

Unity-C#进阶——3.27更新中

文章目录 数据结构类ArrayListStackQueueHashtable 泛型泛型类、泛型方法、泛型接口ListDictionaryLinkedList泛型栈,泛型队列 委托和事件委托事件匿名函数Lambad 表达式**闭包** List 排序逆变协变多线程进程线程多线程方法:线程之间共享数据&#xff1…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《强沙尘暴下新能源基地的弹性评估及其提升方法 》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

mysql 条件/系统/加密/其它函数

学习了日期时间函数,接着学习条件、系统、加密和其它函数。 3,条件判断函数 条件判断函数也称为控制流程函数,根据满足的条件的不同,执行相应的流程。MySQL中进行条件判断的函数有IF、IFNULL和 CASE。 函数 说明 IF(expr,v1,v2…

单例设计模式(3)

单例模式(3) 实现集群环境下的分布式单例类 如何理解单例模式中的唯一性? 单例模式创建的对象是进程唯一的。以springboot应用程序为例,他是一个进程,可能包含多个线程,单例代表在这个进程的某个类是唯一…

跨境电商IP防关联是什么?有什么作用?

做跨境电商的朋友应该都知道IP防关联这个词,那么为何IP需要防关联呢?今天为大家来解答这个问题。 跨境电商IP防关联是指在跨境电商运营中,通过采取一系列技术手段,确保每个跨境电商账号使用独立的IP地址,以避免账号之间因为IP地址…

【Linux实践室】Linux用户管理实战指南:用户权限切换操作详解

🌈个人主页:聆风吟_ 🔥系列专栏:Linux实践室、网络奇遇记 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 🔔图形化界面登录2.2 🔔使用login…

【java苍穹外卖项目实战四】新增员工功能

文章目录 1、需求设计分析2、接口设计3、表设计4、设计DTO类5、Controller层功能实现6、Service层功能实现7、Mapper层功能实现 1、需求设计分析 一般在做需求分析时,往往都是对照着产品原型进行分析,因为产品原型比较直观,便于我们理解业务…

Unreal的Quixel Bridge下载速度过慢、下载失败

从Quixel Bridge下载MetaHuman模型,速度非常慢,而且经常下载失败,从头下载。 可以从Quixel Bridge的右上角我的图标->Support->Show Logs打开日志目录 downloaded-assets目录下为下载的资源 bridge-plugin.log文件记录了下载URL和下载…

矩阵间关系的建立

参考文献 2-D Compressive Sensing-Based Visually Secure Multilevel Image Encryption Scheme 加密整体流程如下: 我们关注左上角这一部分: 如何在两个图像之间构建关系,当然是借助第3个矩阵。 A. Establish Relationships Between Different Images 简单说明如下: …

leetcode 331. 验证二叉树的前序序列化【计数器】

原题链接:331. 验证二叉树的前序序列化 题目描述: 序列化二叉树的一种方法是使用 前序遍历 。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。 例如…

鸿蒙OS开发实例:【瀑布流式图片浏览】

介绍 瀑布流式展示图片文字,在当前产品设计中已非常常见,本篇将介绍关于WaterFlow的图片浏览场景,顺便集成Video控件,以提高实践的趣味性 准备 请参照[官方指导],创建一个Demo工程,选择Stage模型熟读Har…

思维题,LeetCode331. 验证二叉树的前序序列化

一、题目 1、题目描述 序列化二叉树的一种方法是使用 前序遍历 。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。 例如,上面的二叉树可以被序列化为字符串 &quo…

数字孪生|山海鲸可视化软件Windows版安装步骤

哈喽,大家好啊,我是雷工! 今天尝试下该数字孪生软件,以下为安装步骤,我这里安装的是Windows版本。 1、系统配置要求 由于该软件主要功能是为了编辑可视化大屏,因此该软件必须安装在有桌面的系统内。 2、…

动态规划详细讲解c++|经典例题讲解认识动态规划|0-1背包问题详解

引言 uu们,你们好!这次的分享是动态规划,其中介绍了动态规划的相关概念和做题模板(三要素),同时为了uu们对动态规划方法有更加形象的认识,特地找了两个经典问题,和大家一起分析。并…

关于未来自我的发展和一些学习方法(嵌入式方向)

我是一名大二的学生,考研还是就业,到底是重视专业课还是重视数学英语,这些问题一直困扰了我很久,但如今已经有了一些浅显的认识,所以才会想写这样一篇文章来记录一下自己的状态和未来的规划 下面的看法都是个人的看法&…

WMware虚拟机配置静态IP

注意:如果是克隆的虚拟机,需要先重新生成mac地址,如下图所示 修改配置文件 :/etc/sysconfig/network-scripts/ifcfg-ens33 注意:1. BOOTPROTO设置为static 2.将下面的IPADDR地址替换为你实际要设置的ip地址 3.NAT模式…

Unity urp渲染管线下,动态修改材质球surfaceType

在项目中遇到了需要代码动态修改材质球的surfaceType,使其动态切换是否透明的需求。 urp渲染管线下,动态修改材质球的surfaceType,查了大部分帖子,都有一些瑕疵,可能会造成透明后阴影投射有问题。 其次在webgl平台上…

postcss安装和使用(详细)

1,安装postcss: 在此之前需要安装有node.js 第一步 命令:cnpm install postcss-cli -g 第二步 命令:cnpm install postcss –g 推荐内容 2,下载autoprefixer插件,并创建postcss.config.js文件并写入配置代码 autoprefixer插件…

Node.js中Router的使用

文章目录 介绍router的优点1.导入Express和创建Router:2. 定义路由:3.将router暴露到模块外:4. 将Router挂载到Express应用中:4.1.引入router4.2.使用中间件让router在Express应用中生效(三种写法) 5. 完整示例:5.1.编…