我们要学习锁首先要了解下我们想了解的锁到底是什么🤔
而在MySQL中给某个数据加锁的本质其实就是在内存中创建一个锁结构与之关联,而这个锁结构就是我们常提到的MySQL的锁🔒
那么接下来的问题就是,这个锁结构长啥样呢?
一张图详解锁结构( ̄∇ ̄)/

为了节约资源,并非每个锁都有一个单独的锁结构与之对应,符合如下条件的记录就会放在同一个锁结构中
在同一个事务/页面中进行的加锁操作
加锁的类型一样
等待状态一样
按操作方式
读锁/共享锁/S(Share Lock)
写锁/排他锁/X(Exclusive Lock)
按锁粒度(Lock Granularity)
全局锁
表级锁(Table Lock)
表级别的共享锁和排他锁
意向锁(Intention Lock)
自增锁(AUTO-INC)
元数据锁(MDL)
页级锁
行级锁
记录锁(Record Locks)
间隙锁(Gap Locks)
临键锁(Next-Key Locks)
插入意向锁(Insert Intention Locks)
按思维方式/设计思想
乐观锁(Optimistic Concurrency Control)
悲观锁(Pessimistic Concurrency Control)
按加锁方式
隐式锁
显式锁
并发事务中"读-读"的情况一般不会引起什么问题,一般需要解决的就是对于"写-写"和"读-写"/"写-读"引起一些问题,主要有两种解决方式:加锁,或者需MVCC
既要允许"读-读"情况不受影响,又要使"写-写"、"读-写"或"写-读"情况中的操作相互阻塞,所以MySQL实现一个由两种类型的锁组成的锁系统来解决
这两种类型的锁通常被称为共享锁(Shared Lock,S Lock)和排他锁(Exclusive Lock,X Lock),也叫读锁(readlock)和写锁(write lock )
共享锁/S(读锁)
针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,相互不阻阻塞
排他锁/X(写锁)
当前写操作没有完成前,它会阻断其他写锁和读锁
这样就能确保在给定的时间里,只有一个事务能执行写入,并防止其他用户读取正在写入的同一资源
一句话概括就是,不同事务中,只有都是共享锁才互不影响不会阻塞,但凡有一个事务有排他锁,都会阻塞,对于InnoDB引擎来说,共享锁和排他锁可以加在表上,也可以加在行上
加共享锁
SQL语句 + "LOCK IN SHARE MODE" / "FOR SHARE";
加排他锁
SQL语句 + "FOR UPDATE";
需要注意的是:
读操作(SELECT)可以加共享锁,也可以加排他锁
写操作(DELETE、UPDATE、INSERT)只能加排他锁
INSERT
MySQL通过"隐式锁"来维护
DELETE
B+树上定位 -> 获取X锁 -> 执行delete mark操作
UPDATE
修改键值:DELETE操作 + INSERT操作
不修改键值:
不修改存储空间:B+树上定位 -> 获取X锁 -> 修改
修改存储空间:B+树上定位 -> 获取X锁 -> 删除原纪录(移入垃圾链表) -> 插入新纪录(INSERT操作)
补充:MySQL 8.0 新特性
nowait
查询的数据已经加锁,立刻返回报错
skip locked
立刻返回未被锁定的行
数据库的并发度和锁的粒度息息相关,锁的粒度越小,并发度越高,但也越耗资源(锁的获取、检查、释放等操作),影响系统性能,因而我们需要根据实际的业务场景,在高并发响应与系统性能之间进行平衡
对整个数据库实例进行加锁,加锁后整个数据库处于只读状态,命令如下:
Flush tables with read lock
开销小,但并发性较差
可以避免死锁
主要用于在已经加了行级锁后,会在上一层记录中自动加一个意向锁,用于判断当前数据页或者数据表是否被加过锁,意向共享锁和意向排他锁之间是任意组合都是相互兼容的,但是与普通的共享锁/排他锁之间,只有意向共享锁与普通共享锁兼容,其余都互斥
具体使用意向共享锁,还是意向排他锁,要根据行级锁(与行级锁一致)
意向锁之间不互斥
意向锁与行级锁不互斥,与表级锁互斥
意向锁在保证高并发的前提下,实现了行锁与表锁共存,并且满足事务隔离性的要求
建表字段中如果包含"AUTO_INCREMENT"关键字,每当插入操作时,就要对自增锁进行竞争,以保证主键唯一且单调递增
首先,插入数据的模式有3种:
简单插入(Simple inserts)
事先明确插入的数量批量插入(Bulk inserts)
基于现有的表进行插入(事先并明确插入的数量)混合模式插入(Mixed-mode inserts)
插入的数据有的有主键,有的没主键主要有3种模式
"传统"锁定模式(并发性较差)
- innodb_autoinc_lock_mode=0"连续"锁定模式(8.0前默认)
innodb_autoinc_lock_mode=1"交错"锁定模式(8.0后默认)
innodb_autoinc_lock_mode=2可以通过下面👇代码查看当前执行的process信息
show processlist;
如果想查看在等待哪把锁,可以重点关注下State字段
开销、粒度、并发度 介于表锁和行锁之间
也会出现死锁
每个层级的锁数量是有限制的(锁会占用内存空间),锁空间的大小是有限制的,某个层达到阈值后就会进行锁升级(用更大力度的锁代替多个较小粒度的锁)
比如一张表中出现大量DELETE操作时,会锁表
行锁又称记录锁,锁的是某一行数据
MySQL的服务层并没有实现行锁机制,行锁是在存储引擎层实现的
优点
锁粒度小,发生锁冲突概率低,可实现的并发度高
缺点
锁的开销较大,加锁较慢,会出现死锁
可参照读锁和写锁理解,只不过粒度是行,只有操作相同的行才有影响
在MySQL的可重复读的隔离级别下,可以解决幻读问题
因为在一开始进行读取操作的时候,我们并不知道会产生幻影数据的主键是多少(还不存在),因而无法使用记录锁事先锁定那一行,间隙锁会阻塞当前记录和其前一条记录之间的插入操作,举个例子:
当前加锁的记录id为8,前一条记录id为5,加了间隙锁会不允许别的事务插入任何id在(5,8)之间的数据,直到拥有这个间隙锁的事务提交后
一句话理解~就是记录锁+间隙锁(不愧是我(*≧ω≦)/)
获取锁失败的事务,开始等待的事务的其锁的类型就是插入意向锁,插入意向锁并不会阻止别的事务继续获取该记录上任何类型的锁
悲观锁是通过数据库自身的锁机制来实现,从而保证致据操作的排它性。
悲观锁总是假设最坏的情况,认为每次去查数据的时候都会被别人修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)比收如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,当其他线程想要访问数据时,都需要阻塞挂起
如果在SELECT语句上加悲观锁,在这条语句执行过程中所有扫描的行都会被锁上,因此在MySQL中使用悲观锁需要确定必须使用了索引,不然会全表扫描,把整张表都锁上
乐观锁以为对同一数据的并发操作不会总发生,不用每次都对数据上锁,但是在更新的时候会判断一下在此期间别人有设有去更新这个数据,也就是不采用数据库自身的锁机制,而是通过程序来实现的。在程序上,我们可以采用版本号机制或者CAS机制实现,乐观锁适用于多读的应用类型,这样可以提高吞吐量
ps:在Java中java.util.concurrent.atomic包下的原子变量类就是使用了乐观锁的CAS实现的
在表中设计一个饭木字段version,第一次读的时候,会获取version字段的取值。然后对数据进行更新或删除操作时,会执行UPDATE..·SET version=version+1 NHERE version=version。此时如果已经有事务对这条数据进行了更改,修改就不会成功。
这种方式类以我们展悉的SN、CVS版本管理系统,当我们修改了代玛进行提交时,首先会检查当前版木号与服务器上的版本号昏一致,如果一致就可以直接提交,如果不一致就需要更新服务器上的最新代码,然后再进行提交。
时间戰和版本号机制一样,也是在更新提交的时候,将当前数据的时间戳和更新之前取得的时间戳进行较,如果两者一致则更新功,否则是版本冲突,基本是通过给数据行增加一个戳(版本号或者时间戳),从而证明当前拿到的数据是否最新
乐观锁适用于读多的场景;悲观锁适用于写多的场景
乐观锁基于程序实现,不会产生死锁;悲观锁基于MySQL的锁机制实现
一个事务在执行INSERT操作时,如果即将插入的侧除已经被其他争务加了间隙锁,那么本次INSERT操作会阻塞,并且当前事务会在该间隙上加一个插入意向锁,否则一股情况下INSERT操作是不会显式加锁的,执行逻辑大致如下:
InnoDB的每条记录中都有一个隐藏的trx_id字段,存于聚簇索引的B+树中
在操作一条记录前,首先根据记录中的trx_id检查该事务是否是活动的事务(未提交、回滚),如果是活动的事务,就会现将隐式锁转换为显式锁
检查是否有锁冲突,如果有,创建锁,并设置为waiting的状态,没有就执行步骤5
等待加锁成功,被唤醒,或者超时
写数据,并将自己的trx_id写入trx_id字段
通过特定语句进行添加的锁,一般称为显式锁("FOR UPDATE"等)
搞定撒个花(。・ω・。)ノ🎉
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
当我使用has_one时,它工作得很好,但在has_many上却不行。在这里您可以看到object_id不同,因为它运行了另一个SQL来再次获取它。ruby-1.9.2-p290:001>e=Employee.create(name:'rafael',active:false)ruby-1.9.2-p290:002>b=Badge.create(number:1,employee:e)ruby-1.9.2-p290:003>a=Address.create(street:"123MarketSt",city:"SanDiego",employee:e)ruby-1.9.2-p290
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决
您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应
我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin
我想编写一个ruby脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"
我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi
对于我正在编写的Rails3应用程序,我正在考虑从本地文件系统上的XML、YAML或JSON文件中读取一些配置数据。重点是:我应该把这些文件放在哪里?Rails应用程序中是否有用于存储此类内容的默认位置?附带说明一下,我的应用程序部署在Heroku上。 最佳答案 我经常做的是:如果文件是通用配置文件:我在目录/config中创建一个YAML文件,每个环境有一个上层key如果我为每个环境(大项目)创建一个文件:我为每个环境创建一个YAML并将它们存储在/config/environments/然后我在加载YAML的地方创建了一个初始化