mysql学习--入门(三)

Mysql的锁

读写锁

在msyql中,处理并发读或者并发写的时候,采用了两种锁来解决并发问题。这两种锁就是读锁和写锁,也分别称为共享锁和排他锁。

  • 读锁:多个用户在同一时刻访问同一个资源,彼此之间互不干扰
  • 写锁:一个写锁会阻塞其它的写锁和读锁,即阻塞别人的访问。一个用户在写入的时候,防止其它用户读取或者写入。
锁粒度

给不同大小的资源上锁,就是控制锁的粒度。锁的资源越少或越小,则并发程度越高,需要的锁也越多。但同时系统开销也会增大。
大多数商业数据库提供的基本都是行级锁。
mysql提供了多种选择:

  • 表锁表锁是mysql当中最基本的锁策略、并且是开销最小的策略。正如其名字所说,当一个用户对一张表里的内容进行操作时,那么就会锁定整个表,其它用户想要对这个表进行读或者写操作时,都会遭到阻塞。
  • 行锁:一个表是由许多行数据组成的。行锁则是加在某一行数据上的。即锁的粒度更细了。一张表的锁的数量也增多了,所以行锁可以最大限度的支持并发控制,但却也带来了最大的锁开销

mysql的事务

事务是一组sql语句,这组sql语句要么全部成功执行,任何地方的失败都会导致整个事务回滚,即撤销已成功的sql语句。

事务例子
  • 小明现在要将卡里的200元存到微信钱包里。步骤如下:

      检查卡里余额 是否高于200元
      将卡里数值减去200
      微信钱包数值增加200
    

    上述三个操作,必须全部执行,或者全部不执行,不然都会导致小明平白无故的少钱,或者多钱。

  • 通常我们可以将sql语句放进

     start transaction;
     ...sql...
     ...sql...
     commit;
    
ACID
  • 原子性
  • 一致性
  • 隔离性
  • 持久性
事务的隔离级别
  • 读未提交
    事务中的修改,即时还没有提交,对其它事务也都是可见的。事务可以提交未提交的事务,这就是所谓的 “脏读”。一般使用的比较少。
  • 读已提交
    事务开始时,只能看见已经提交的事务所做的修改。即两次查询会读到不同的结果。即 “不可重复读问题”
  • 可重复读
    可重复读是Mysql的默认隔离界别。该级别保证了同一个事务中,多次读取同样记录的结果是一致的。但可重复读并不能解决“幻读”的问题。
    幻读指的就是当某个事务在读取某一个范围的记录时,另外一个事务在该范围内插入了新的记录,则当前事务再去读取的时候,可能就读的不是之前的行。InnoDB通过 多版本并发控制(MVCC) 解决了幻读的问题。
  • 可串行化
    serializable是最高的隔离级别。通过强制事务串行,避免了前面的幻读问题。它的解决方法,就是 加行级锁,所以可能会导致大量的超时和锁争用的问题。

在这里插入图片描述

Mysql的死锁

死锁指的是,两个或两个以上的事务,相互占用资源,并请求互相占用的资源,就产生了一个循环等待。

解决死锁
  • InnoDB能够检测到死锁的循环依赖,并立即返回一个错误。
  • 当查询出现锁等待超时的时候,放弃该锁请求。
  • InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务回滚。(大多数情况下都采用这种,简单的方式)
事务日志

事务日志的作用就是,预写数据。将数据的内存拷贝,先记录到事务日志中,等到后面再持久化到磁盘上

事务日志采用的是追加的方式,写日志的操作是磁盘一小块区域上的顺序I/O,而不是持久化数据到磁盘上的随机I/O,所以先记录到日志上更快

如果数据的修改已经记录到日志上,但数据还没有持久化到磁盘,那么此时系统即时崩溃,也可以在重启时通过事务日志恢复这一部分的数据。

mysql的事务

mysql提供了两种事务型的存储引擎,分别是InnoDB和NDB Cluster

  • 自动提交mysql默认采用自动提交的模式。也就是我们平时写的sql语句,其实默认是处于事务当中的。我们可以通过变量 autocommit来启动或者禁用。1或者ON代表启动,0或者OFF代表禁用。
  • 混合使用存储引擎:在实际设计表的过程中,我们要考虑事务回滚,针对不同的表采用不同的存储引擎。(例如InnoDB和MyISAM)。非事务型的存储引擎如MyISAM,如果想要回滚(即撤销变更),那么会导致数据库处于不一致的状态,难以修复。
  • 隐式和显式的锁定:一般执行事务时,例如之前的commit和rollback,都是一种隐式锁定。通过特定语句Locks,Tables这种的锁定称为显式锁定。一般不要使用显式锁定。

多版本的并发控制

mysql的大多数事务型存储引擎实现的都不是简单的行级锁,一般都同时实现了多版本并发控制MVCC。MVCC是一种设计模式,或者说是思想。所以不同的数据库系统可能对于MVCC实现的方式不一样。

  • 作用: 其在很多情况下避免了加锁操作,实现了非阻塞的读,只锁定必要的行。

  • 实现: InnoDB的MVCC,是通过让每行记录都多了两列,一列是该行创建时间,一列该行删除时间。这里的时间指的其实是系统版本号,每开启一个事务,系统版本号就会递增。事务开始时刻的系统版本号,用来和每行记录的版本号进行比较。

      select:行的查找系统版本号要比事务的的系统版本号小,这样才能确保读取行的时候,该行是已经存在,准确的。
      		  或者行的删除版本号要比事务的系统版本号要大,这样才能确保在事务要删除的行,在事务执行完前,还是存在的。
      Insert:为新插入的每一行保存当前系统版本号,作为行版本号
      Delete:为删除的每一行保存当前系统版本号,作为删除版本号
      Update:为更新的行,将系统版本号更新为当前的行版本号
    

    保存这两个额外的系统版本号,可以使大多数读操作不用加锁,而且可以保证只会读到符合条件的行。但相应的,增大了每行的额外存储。

  • 位置: MVCC只能用在不可重复读和读已提交两个隔离级别中使用。

Mysql为什么默认使用可重复读这个隔离级别

这个是有历史原因的。在mysql5.0版本之前,做mysql的主从集群的话,mysql有一个binlog文件,一个记录数据库修改的文件。当时这个binlog文件只有一个statement格式。对于这个文件,在当时的mysql如果使用读已提交这个隔离级别的话,就会产生一个问题,下面举一个例子来说明。

	有两个隔离级别都是RC的事务,正在执行值的插入和删除操作
     A事务             B事务
  删除值Value            /
	   /			插入值Value
	   / 			提交事务
	回滚事务             /

对于上面的操作,我们的mysql数据库存在主从机的情况下,会存在一个问题。对于master机来说,它对于上面事务的理解先删除再插入,所以数据库中会存在这个值。而对于slave机来说,它理解的binlog文件中,会理解成先插入,再删除,导致数据库中不存在这个值。

所以mysql当时选择采用了可重复读这个隔离级别。在可重复读这个隔离级别中,有加入一个间隙锁,即在执行删除操作时,会将其锁住,直至其完成。才会去执行后面的插入方法。

在mysql5.1之后,binlog推出了row的格式,基于行的sql复制,使得其不会出现上面的顺序问题。

所以对于一般的互联网项目,仅仅需要采用读已提交这个隔离就够了,因为这个隔离级别的并发读更低。

数据库的范式

  • 第一范式,属性不可拆分,即同一列不能有多个值(或者可以理解为多个子列)。
  • 第二范式,在满足第一范式的前提下,属性需要完全依赖于主属性。比如:现在主键是(A,B),那非主属性必须要完全依赖于(A,B),不能单单依赖于A或者B
  • 第三范式,在满足第二范式的前提下,必须消除传递函数依赖。这是为了减少冗余的。比如student表,course表和grade表,你在course表里出现的课程名,就没必要在grade表里出现了。因为通过grade表里的课程号,就可以找到课程名了。
  • BCNF,是3NF的修正版,满足 
     1.所有非主属性对每一个码都是完全函数依赖。
     2.所有的主属性对每一个不包含它的码,也是完全函数依赖。
     3.没有任何属性完全函数依赖于非码的任何一组属性。
    主属性不可以通过传递函数,决定另一个主属性!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值