如何处理数据库并发问题

如何处理数据库并发问题,第1张

想要知道如何处理数据并发,自然需要先了解数据并发。

什么是数据并发 *** 作呢?

就是同一时间内,不同的线程同时对一条数据进行读写 *** 作。

在互联网时代,一个系统常常有很多人在使用,因此就可能出现高并发的现象,也就是不同的用户同时对一条数据进行 *** 作,如果没有有效的处理,自然就会出现数据的异常。而最常见的一种数据并发的场景就是电商中的秒杀,成千上万个用户对在极端的时间内,抢购一个商品。针对这种场景,商品的库存就是一个需要控制的数据,而多个用户对在同一时间对库存进行重写,一个不小心就可能出现超卖的情况。

针对这种情况,我们如何有效的处理数据并发呢?

第一种方案、数据库锁

从锁的基本属性来说,可以分为两种:一种是共享锁(S),一种是排它锁(X)。在MySQL的数据库中,是有四种隔离级别的,会在读写的时候,自动的使用这两种锁,防止数据出现混乱。

这四种隔离级别分别是:

读未提交(Read Uncommitted)

读提交(Read Committed)

可重复读(Repeated Read)

串行化(Serializable)

当然,不同的隔离级别,效率也是不同的,对于数据的一致性保证也就有不同的结果。而这些可能出现的又有哪些呢?

脏读(dirty read)

事务与事务之间没有任何隔离的时候,就可能会出现脏读。例如:商家想看看所有的订单有哪些,这时,用户A提交了一个订单,但事务还没提交,商家却看到了这个订单。而这时就会出现一种问题,当商家去 *** 作这个订单时,可能用户A的订单由于部分问题,导致数据回滚,事务没有提交,这时商家的 *** 作就会失去目标。

不可重复读(unrepeatable read)

一个事务中,两次读 *** 作出来的同一条数据值不同,就是不可重复读。

例如:我们有一个事务A,需要去查询一下商品库存,然后做扣减,这时,事务B *** 作了这个商品,扣减了一部分库存,当事务A再次去查询商品库存的时候,发现这一次的结果和上次不同了,这就是不可重复读。

幻读(phantom problem)

一个事务中,两次读 *** 作出来的结果集不同,就是幻读。

例如:一个事务A,去查询现在已经支付的订单有哪些,得到了一个结果集。这时,事务B新提交了一个订单,当事务A再次去查询时,就会出现,两次得到的结果集不同的情况,也就是幻读了。

那针对这些结果,不同的隔离级别可以干什么呢?

“读未提(Read Uncommitted)”能预防啥?啥都预防不了。

“读提交(Read Committed)”能预防啥?使用“快照读(Snapshot Read)”方式,避免“脏读”,但是可能出现“不可重复读”和“幻读”。

“可重复读(Repeated Red)”能预防啥?使用“快照读(Snapshot Read)”方式,锁住被读取记录,避免出现“脏读”、“不可重复读”,但是可能出现“幻读”。

“串行化(Serializable)”能预防啥?有效避免“脏读”、“不可重复读”、“幻读”,不过运行效率奇差。

好了,锁说完了,但是,我们的数据库锁,并不能有效的解决并发的问题,只是尽可能保证数据的一致性,当并发量特别大时,数据库还是容易扛不住。那解决数据并发的另一个手段就是,尽可能的提高处理的速度。

因为数据的IO要提升难度比较大,那么通过其他的方式,对数据进行处理,减少数据库的IO,就是提高并发能力的有效手段了。

最有效的一种方式就是:缓存

想要减少并发出现的概率,那么读写的效率越高,读写的执行时间越短,自然数据并发的可能性就变小了,并发性能也有提高了。

还是用刚才的秒杀举例,我们为的就是保证库存的数据不出错,卖出一个商品,减一个库存,那么,我们就可以将库存放在内存中进行处理。这样,就能够保证库存有序的及时扣减,并且不出现问题。这样,我们的数据库的写 *** 作也变少了,执行效率也就大大提高了。

当然,常用的分布式缓存方式有:Redis和Memcache,Redis可以持久化到硬盘,而Memcache不行,应该怎么选择,就看具体的使用场景了。

当然,缓存毕竟使用的范围有限,很多的数据我们还是必须持久化到硬盘中,那我们就需要提高数据库的IO能力,这样避免一个线程执行时间太长,造成线程的阻塞。

那么,读写分离就是另一种有效的方式了

当我们的写成为了瓶颈的时候,读写分离就是一种可以选择的方式了。

我们的读库就只需要执行读,写库就只需要执行写,把读的压力从主库中分离出去,让主库的资源只是用来保证写的效率,从而提高写 *** 作的性能。

在程序员的职业生涯中,总会遇到数据库表被锁的情况,前些天就又撞见一次。由于业务突发需求,各个部门都在批量 *** 作、导出数据,而数据库又未做读写分离,结果就是:数据库的某张表被锁了!

用户反馈系统部分功能无法使用,紧急排查,定位是数据库表被锁,然后进行紧急处理。这篇文章给大家讲讲遇到类似紧急状况的排查及解决过程,建议点赞收藏,以备不时之需。

用户反馈某功能页面报502错误,于是第一时间看服务是否正常,数据库是否正常。在控制台看到数据库CPU飙升,堆积大量未提交事务,部分事务已经阻塞了很长时间,基本定位是数据库层出现问题了。

查看阻塞事务列表,发现其中有锁表现象,本想利用控制台直接结束掉阻塞的事务,但控制台账号权限有限,于是通过客户端登录对应账号将锁表事务kill掉,才避免了情况恶化。

下面就聊聊,如果当突然面对类似的情况,我们该如何紧急响应?

想象一个场景,当然也是软件工程师职业生涯中会遇到的一种场景:原本运行正常的程序,某一天突然数据库的表被锁了,业务无法正常运转,那么我们该如何快速定位是哪个事务锁了表,如何结束对应的事物?

首先最简单粗暴的方式就是:重启MySQL。对的,网管解决问题的神器——“重启”。至于后果如何,你能不能跑了,要你自己三思而后行了!

重启是可以解决表被锁的问题的,但针对线上业务很显然不太具有可行性。

下面来看看不用跑路的解决方案:

遇到数据库阻塞问题,首先要查询一下表是否在使用。

如果查询结果为空,那么说明表没在使用,说明不是锁表的问题。

如果查询结果不为空,比如出现如下结果:

则说明表(test)正在被使用,此时需要进一步排查。

查看数据库当前的进程,看看是否有慢SQL或被阻塞的线程。

执行命令:

该命令只显示当前用户正在运行的线程,当然,如果是root用户是能看到所有的。

在上述实践中,阿里云控制台之所以能够查看到所有的线程,猜测应该使用的就是root用户,而笔者去kill的时候,无法kill掉,是因为登录的用户非root的数据库账号,无法 *** 作另外一个用户的线程。

如果情况紧急,此步骤可以跳过,主要用来查看核对:

如果情况紧急,此步骤可以跳过,主要用来查看核对:

看事务表INNODB_TRX中是否有正在锁定的事务线程,看看ID是否在show processlist的sleep线程中。如果在,说明这个sleep的线程事务一直没有commit或者rollback,而是卡住了,需要手动kill掉。

搜索的结果中,如果在事务表发现了很多任务,最好都kill掉。

执行kill命令:

对应的线程都执行完kill命令之后,后续事务便可正常处理。

针对紧急情况,通常也会直接 *** 作第一、第二、第六步。

这里再补充一些MySQL锁相关的知识点:数据库锁设计的初衷是处理并发问题,作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则,而锁就是用来实现这些访问规则的重要数据结构。

根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类。MySQL中表级别的锁有两种:一种是表锁,一种是元数据锁(metadata lock,MDL)。

表锁是在Server层实现的,ALTER TABLE之类的语句会使用表锁,忽略存储引擎的锁机制。表锁通过lock tables… read/write来实现,而对于InnoDB来说,一般会采用行级锁。毕竟锁住整张表影响范围太大了。

另外一个表级锁是MDL(metadata lock),用于并发情况下维护数据的一致性,保证读写的正确性,不需要显式的使用,在访问一张表时会被自动加上。

常见的一种锁表场景就是有事务 *** 作处于:Waiting for table metadata lock状态。

MySQL在进行alter table等DDL *** 作时,有时会出现Waiting for table metadata lock的等待场景。

一旦alter table TableA的 *** 作停滞在Waiting for table metadata lock状态,后续对该表的任何 *** 作(包括读)都无法进行,因为它们也会在Opening tables的阶段进入到Waiting for table metadata lock的锁等待队列。如果核心表出现了锁等待队列,就会造成灾难性的后果。

通过show processlist可以看到表上有正在进行的 *** 作(包括读),此时alter table语句无法获取到metadata 独占锁,会进行等待。

通过show processlist看不到表上有任何 *** 作,但实际上存在有未提交的事务,可以在information_schema.innodb_trx中查看到。在事务没有完成之前,表上的锁不会释放,alter table同样获取不到metadata的独占锁。

处理方法:通过 select * from information_schema.innodb_trxG, 找到未提交事物的sid,然后kill掉,让其回滚。

通过show processlist看不到表上有任何 *** 作,在information_schema.innodb_trx中也没有任何进行中的事务。很可能是因为在一个显式的事务中,对表进行了一个失败的 *** 作(比如查询了一个不存在的字段),这时事务没有开始,但是失败语句获取到的锁依然有效,没有释放。从performance_schema.events_statements_current表中可以查到失败的语句。

处理方法:通过performance_schema.events_statements_current找到其sid,kill 掉该session,也可以kill掉DDL所在的session。

总之,alter table的语句是很危险的(核心是未提交事务或者长事务导致的),在 *** 作之前要确认对要 *** 作的表没有任何进行中的 *** 作、没有未提交事务、也没有显式事务中的报错语句。

如果有alter table的维护任务,在无人监管的时候运行,最好通过lock_wait_timeout设置好超时时间,避免长时间的metedata锁等待。

关于MySQL的锁表其实还有很多其他场景,我们在实践的过程中尽量避免锁表情况的发生,当然这需要一定经验的支撑。但更重要的是,如果发现锁表我们要能够快速的响应,快速的解决问题,避免影响正常业务,避免情况进一步恶化。所以,本文中的解决思路大家一定要收藏或记忆一下,做到有备无患,避免突然状况下抓瞎。

补充楼主:

其实我没什么经验,只不过是了解一些基础的东西罢了。

一楼的 一朵瘩红花 实际经验很丰富,你可以向她咨询一下。

你问的问题挺好得。三个概念紧密联系在一起。

这样说吧:并发的几个事务同时发生,不加锁控制的话数据就会乱套了,而加了锁后,又是并发访问会出现死锁,所以就会出现避免死锁的一些措施。

首先谈并发:理论指的是在一段时间同时对某件事进行 *** 作。 注意精度问题,修改数据库是在一段时间内 *** 作,不是在某个时刻,而日志则会从 时刻 开始记录你的 *** 作。

造成死锁的原因是为了防止 不同的用户同时间(不是时刻)都对某个数据修改,造成访问不一致的问题。

比如你读了数据库的一个数据然后把它修改了并存回去,是需要时间的(假如是student表中的有个grade属性,你改了一条记录的一个值)在这个过程当中,有人又访问了数据库并且恰恰访问的是存回去之前的数据,然后他要进行 *** 作,过了一段时间,此时你已经存回去了数据。会发现原来的数据被改动了。这时数据就乱套了。(专业术语叫读脏数据,其实还有很多其他类似这种导致前后数据不一致的问题)所以为了限定这种 *** 作,数据库设计了-----锁---来锁定这种 *** 作。就是你正在 *** 作某个数据的时候----通常之前会先锁定这个数据,这样别人就不能对此数据 *** 作了(严格来说就是只能读,不能改),必须等你 *** 作完才能对此数据修改等 *** 作,这就在一定程度上避免了前后 *** 作数据不一致的问题。

但是有了锁后,新问题出现了,就是死锁:

简单解释死锁:进程A等待进程B释放他的资源,B又等待A释放他的资源,这样就互相等待就形成死锁

官方解释死锁

死锁,根本原因在于对共享存储区的访问。在数据库中也一样,如果需要“修改”一条数据,首先数据库管理系统会在上面加锁,以保证在同一时间只有一个事务能进行修改 *** 作。锁有多种实现方式,比如意向锁,共享-排他锁,锁表,树形协议,时间戳协议等等。锁还有多种粒度,比如可以在表上加锁,也可以在记录上加锁。

在并发控制中,锁是非常重要的。

至于在Oracle还是别的数据库管理系统中,死锁产生的原因没有不同,不同的顶多是锁的实现或者死锁的恢复等罢了

再来说说事务:

事务简单来说就是 一系列的对数据库的 *** 作揉在一起,要么同时完成,要么就都不完成。

比如---你要取钱的过程就可以当成是一个小的事务: 插卡,输入取钱金额,取走钱,拿出来卡。此过程缺一不可。把所有这些过程细节封装起来就成为一个事务。

以oracle数据库为例:

一个事务(你可以认为是一系列业务的 *** 作)起始于dml语句(insert、update、delete)

即一条dml语句就做为一个事务的起始,然后根据业务需要,进行其他的dml *** 作都算是事务的一部分。

最后碰到commit。或者rollback,或者其他意外什么的都算作一个事务的结束。

整个过程就是一个事务。

事务的理论解释就是那四个什么特性:什么原子性、一致性、隔离性和持久性

简称ACID

剩下的:数据库是建立在 *** 作系统之上的一个层次。

你问的是数据库的存储机制??工作机制??还是什么的??

数据库就是存数据的。数据库管理系统是 对存的数据进行高效率的管理

大的结构分物理数据跟逻辑数据。

物理数据就是数据在存储设备上的存储方式,什么物理联系,物理结构,物理记录等 术语。

逻辑数据就是程序员和用户看到的数据形式。什么逻辑联系,逻辑结构==同上

数据库管理类系统就是把这些逻辑跟物理相互转换。 好比你输入的叫逻辑数据存储在磁盘上叫物理数据。等等。

废话了一堆,也不知道回答对你的问题没~~


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/9931316.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-03
下一篇 2023-05-03

发表评论

登录后才能评论

评论列表(0条)

保存