mysql并发操作引发的一些思考

最近公司项目中用到了关系型数据库mysql,其中会涉及到并发操作,之前也用过mysql,但是只是停在了简单的用上面(老大搭好架子、写好demo,我模仿),趁放假好好看看相关的资料,解决一下我一直以来的疑惑。

在网上查阅文章的过程中,我对mysql有了新的认识,对锁、事务等有了了解。感觉要深入理解mysql锁还是需要花费很多时间的,在这里我先浅尝辄止,只记录一下自己的理解。

我的疑惑:

一、应用程序层如何在高并发下保证数据一致性?
二、数据库层(mysql)是否已经保证了高并发下的数据一致性?如果是,如何实现的?
三、分布式时如何在高并发下保证数据一致性?
四、sqlalchemy如何保证高并发下的数据一致性?

一、应用程序层如何在高并发下保证数据一致性?

这个还是比较好解决的。不管是多线程还是多进程只要用锁就能保证数据一致性,当然这样会损失效率。

二、数据库层(mysql)是否已经保证了高并发下的数据一致性?如果是,如何实现的?

使用InnoDB在特定隔离级别下时可以保证高并发下的数据一致性的,具体看下面:

1、mysql是支持多种存储类型的,现在用的最多的是InnoDB(后续分析都是围绕InnoDB)

通过查资料,我对mysql有了新的认识:

mysql是开源的关系型数据库,它提供了很多种类型的存储引擎,我们可以根据对数据处理的需求,选择不同的存储引擎,从而最大限度的利用MySQL强大的功能。

这是介绍mysql engines的文章,现在主要用的是InnoDB,之前公司用过MyISAM。

InnoDB
InnoDB是一个健壮的事务型存储引擎,为用户操作非常大的数据存储提供了一个强大的解决方案。
InnoDB还引入了行级锁定和外键约束,在以下场合下,使用InnoDB是最理想的选择:
1.更新密集的表。InnoDB存储引擎特别适合处理多重并发的更新请求。
2.事务。InnoDB存储引擎是支持事务的标准MySQL存储引擎。
3.自动灾难恢复。与其它存储引擎不同,InnoDB表能够自动从灾难中恢复。
4.外键约束。MySQL支持外键的存储引擎只有InnoDB。
5.支持自动增加列AUTO_INCREMENT属性。
一般来说,如果需要事务支持,并且有较高的并发读取频率,InnoDB是不错的选择。

MyISAM
据说已经被淘汰了,现在用的很少。支持选择密集或者插入密集的场景。
MyISAM表无法处理事务,这就意味着有事务处理需求的表,不能使用MyISAM存储引擎。MyISAM存储引擎特别适合在以下几种情况下使用:
1.选择密集型的表。MyISAM存储引擎在筛选大量数据时非常迅速,这是它最突出的优点。
2.插入密集型的表。MyISAM的并发插入特性允许同时选择和插入数据。例如:MyISAM存储引擎很适合管理邮件或Web服务器日志数据。

2、InnoDB自己对sql进行加锁,我们要做的选择合适的隔离级别和避免死锁

参考文章:
Innodb中的事务隔离级别和锁的关系-美团技术分享
MySQL 加锁处理分析-阿里数据库专家写的
我看了十几篇文章,最后看完这两篇文章,才真正对数据库锁有了了解,建议阅读这两篇。
我的总结:

(1) 数据库遵循的是两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁)

(2)在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。我们的数据库锁,也是为了构建这些隔离级别存在的。

  • 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
  • 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
  • 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
  • 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

(3)InnoDB通过乐观锁(实现方式是MVCC)在RR级别中,解决了快照读时出现幻读的问题。

快照读:就是select

  • select * from table ….;

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。

  • select * from table where ? lock in share mode;
  • select * from table where ? for update;
  • insert;
  • update ;
  • delete;

(4)行锁防止别的事务修改或删除,GAP锁防止别的事务新增,行锁和GAP锁结合形成的的Next-Key锁共同解决了RR级别在写数据(即当前读)时的幻读问题。

(5)综上:在InnoDB的RR隔离级别下,基本不需要自己加锁,因为InnoDB本身都帮着干了,除非你在数据时效特别敏感的业务中需要当前读,比如select * from table where ? for update。

三、分布式时如何在高并发下保证数据一致性?

我觉得有两种方式:
1、使用自身支持分布式的数据库
2、使用数据库中间件,比如360开源的Atlas,阿里云的DRDS服务(现在的云服务特别的方便,大大减小了小公司的运维成本,但是还是需要懂相关的基础知识才行)

四、sqlalchemy如何保证高并发下的数据一致性?

sqlalchemy使用session的时候,大多数情况都是不需要自己加锁的。如果需要当前读则可以调用函数with_for_update。

2017.01.04更新
补充其他一些概念:

1、脏读、不重复读、幻读:

(1)脏读,最容易理解。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据。 (2)不重复读。解决了脏读后,会遇到,同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果会不一致。 (3)幻读。解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会“惊奇的”发现了这些新数据,貌似之前读到的数据是“鬼影”一样的幻觉。

2、主键:

参考网页
主键,是索引的一种,并且是唯一性索引的一种。 (1)主键是NOT NULL和UNIQUE的 (2)主键可以是联合主键 (3)无特殊需求下Innodb建议使用与业务无关的自增ID作为主键(走过弯路) (4)主键可以,但是mysql官方建议有

3、外键:

参考网页
外键的使用条件: (1)两个表必须是InnoDB表,MyISAM表暂时不支持外键(据说以后的版本有可能支持,但至少目前不支持); (2)外键列必须建立了索引,MySQL 4.1.2以后的版本在建立外键时会自动创建索引,但如果在较早的版本则需要显示建立; (3)外键关系的两个表的列必须是数据类型相似,也就是可以相互转换类型的列,比如int和tinyint可以,而int和char则不可以; 外键的好处:可以使得两张表关联,保证数据的一致性和实现一些级联操作;

    原文作者:xsren
    原文地址: https://www.jianshu.com/p/9457c88c52d1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞