看了一下,MYSQL在读写时会自动给表或者行加锁,那为啥还会出现所谓的并发问题?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了看了一下,MYSQL在读写时会自动给表或者行加锁,那为啥还会出现所谓的并发问题?相关的知识,希望对你有一定的参考价值。

假如说1000块钱,我2人同时借钱各200,不去控制,mysql不是在写入时会加锁吗,我不去控制,两个写入执行的应该是一个一个来吧,结果应该就剩600块啊,为什么网上有的帖子说这样会出现不等于600的情况啊?求解!

我理解的是:读表的锁表是指在读的过程中上锁,不允许中途还insert其他记录,当读表完毕,获得select结果后,表就解锁了,可以继续新的select或insert等操作。
例子里:2人同时借钱,没有业务锁的话,两个请求发到后端后可能同时去select,此时2次借款操作select余额都是1000(在另一人借200后回写余额800之前),于是2个请求操作各自开始借钱,算出借钱后都剩下800,再分别update进表中,表里余额就是800
如果加业务锁:2个请求到后端后,select余额和借钱后Update按一个事务进行,那第1人select 1000元并借款后剩下800更新进表中,完成第1人借钱事务后,再进行第2人借钱select 剩下的800元并借款后剩下600更新进表中,就可以避免
参考技术A 这个问题很简单,仔细想想如果这两个动作一个成功一个失败了怎么办,那我的钱岂不是没了,这涉及到了事务,要保证成功同时提交,失败同时回滚追问

您好,我想问的本质是,mysql既然已经可以自动加锁了,有些情况却还要人为添加乐观锁等等的锁啊?

锁详解区分 互斥锁⾃旋锁读写锁乐观锁悲观锁

今天看了下常见的几种锁:

互斥锁、⾃旋锁、读写锁、乐观锁、悲观锁,总结一下


互斥锁和自旋锁

最底层的就是互斥锁自旋锁,有很多⾼级的锁都是基于它们实现的

加锁的⽬的就是保证共享资源在任意时间⾥,只有⼀个线程访问,这样就可以避免多线程导致共享数据错乱的问题

互斥锁和⾃旋锁的区别就是对于加锁失败后的处理⽅式是不⼀样的:

  • 互斥锁加锁失败后,线程会释放CPU,给其他线程。加锁的代码就会被阻塞。
  • 自旋锁加锁失败后,线程会忙等待,也就是一直请求加锁,直到它拿到锁

也就是当加锁失败时,互斥锁⽤「线程切换」来应对,⾃旋锁则⽤「忙等待」来应对。


如图

所以,互斥锁加锁失败,会从用户态陷入到内核态,让内核帮我们切换线程,这会有两次线程上下文切换的成本,具有一定的性能开销:

  • 当线程加锁失败时,内核会把线程的状态从运行状态设置为睡眠状态,然后把CPU切换给其他线程运行
  • 当锁被释放时,之前睡眠状态的线程会变为就绪状态,然后内核会在合适时间,把CPU切换给该线程运行

因为虚拟内存是共享的,这里上下文切换的是线程私有数据、寄存器等不共享的数据

所以如果代码运行时间很短,可以考虑不用互斥锁,而是选用自旋锁


自旋锁是通过CPU提供的CAS函数数(Compare And Swap),在用户态完成加锁和解锁操作,不会主动产生线程上下文切换,所以相比较互斥锁来说,会快一点,开销也小一点

⼀般加锁的过程,包含两个步骤:

第⼀步,查看锁的状态,如果锁是空闲的,则执⾏第⼆步;

第⼆步,将锁设置为当前线程持有;

CAS函数就把这两步骤合并成一条硬件级指令,形成原子指令


读写锁

读写锁读锁写锁两部分构成,如果只读共享资源用读锁加锁,如果需要修改共享资源则用写锁加锁

工作原理

写锁没有被线程持有时,多个线程可以并发地持有读锁,但是当写锁被线程持有后,其他线程获取读锁写锁的操作都会阻塞

读写锁在读多写少的场景,能发挥出优势


根据实现的不同分为读优先锁写优先锁

读优先锁

写优先锁

但是这两种都会造成线程“饥饿”的问题,比如

读优先锁:一直有读线程获取读锁,那么写线程将永远获取不到,造成写线程“饥饿”。

写优先锁:如果⼀直有写线程获取写锁,读线程也会被「饿死」。


所以我们可以搞一个公平读写锁

公平读写锁⽐较简单的⼀种⽅式是:⽤队列把获取锁的线程排队,不管是写线程还是读线程都按照先进先出的原则加锁即可,这样读线程仍然可以并发,也不会出现「饥饿」的现象。


乐观锁 悲观锁

悲观锁:认为多线程同时修改共享资源的概率⽐较⾼,于是很容易出现冲突,所以访问共享资源前,先要上锁。

前⾯提到的互斥锁、⾃旋锁、读写锁,都是属于悲观锁。

乐观锁:假定冲突的概率很低,它的⼯作⽅式是:先修改完共享资源,再验证这段时间内有没有发⽣冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作。另外虽然叫锁,但是乐观锁全程并没有加锁,所以它也叫⽆锁编程。

一般会使用版本号机制CAS算法实现





以上是关于看了一下,MYSQL在读写时会自动给表或者行加锁,那为啥还会出现所谓的并发问题?的主要内容,如果未能解决你的问题,请参考以下文章

mysql数据库锁MDL锁的解释

mysql中锁

锁详解区分 互斥锁⾃旋锁读写锁乐观锁悲观锁

035 控制并发 select * from test1 where id =1 for update 就会对这行加锁了?

MySQL- 锁

MySQL的RR和RC事务隔离级别加锁类型验证