事务隔离级别

四种隔离级别:
[1] READ UNCOMMITTED(read uncommitted)读取未提交的数据
[2] READ COMMITTED(read committed)读取已提交的数据
[3] REPEATABLE READ(repeatble read)不可重复读
[4] SERIALIZABLE (serializable)序列化
隔离级别介绍
当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non reapeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。
[1] 脏读:
所谓脏读,就是读了未提交的数据。举个例子:B事务读取了A事务尚未提交(commit)的数据,不巧的是,之后A事务回滚(rollback)了,这就叫脏读。

[2] 不可重复读:
所谓不可重复读,就是一个事务多次读取某一行,但是这一行在多次读取之中的值不一致(这个事务并为对这行作任何修改操作)。举例:A事务多次读取某一行,在它多次读取操作之间,B事务修改了这一行,这就是不可重复读。

[3] 幻像:
所谓幻像,在一个尚未提交的事务的读取的行的范围中插入新行或删除现有行。举例:B事务对A事务读取的行范围内插入新行或删除行,而A事务之后还会访问这些行(即操作会受到这些行的影响),这样A事务中就发生了幻像。

隔离的实现
隔离的实现主要有读写锁和MVCC(Multi-Version Concurrency Control)多版本并发处理方式。
1.读写锁
最简单直接的事务隔离实现方式,每次读操作需要获取一个共享锁,每次写操作需要获取一个写锁。共享锁之间不会产生互斥,共享锁和写锁之间、以及写锁与写锁之间会产生互斥。当产生锁竞争时,需要等待其中一个操作释放锁后,另一个操作才能获取到锁。
show status like ‘table%’;
这里有两个状态变量记录MySQL内部表级锁定的情况,两个变量说明如下:
Table_locks_immediate:产生表级锁定的次数;

Table_locks_waited:出现表级锁定争用而发生等待的次数;
读请求先到锁等待队列,写请求后到,写锁也会插到读锁请求之前
默认情况下是写优先级要大于读优先级

共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE
排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE

  1. MVCC
    在读写锁中,读和写的排斥作用大大降低了事务的并发效率,于是人们又提出了能不能让读写之间也不冲突的方法,就是读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了。不同的事务session会看到自己特定版本的数据,即使其他的事务更新了数据,但是对本事务仍然不可见,本事务看到的数据始终是第一次查询到的数据。在数据库中,这个快照的处理方式叫多版本并发控制(Multi-Version Concurrency Control)。这种方式真正实现了非阻塞读,只有在写操作时才需要加行级锁,因此并发效率更高。
    在各个数据库系统的,MVCC的实现机制不尽相同,下面来详细介绍一下InnoDB是如何实现MVCC的,主要讨论可重复读级别的实现。
    首先,需要了解两个概念:ReadView、undo log、可见性判断算法
    ReadView
    ReadView其实就是上面提到的快照,每个事务在启动后第一次执行查询时会创建一份快照,一个事务快照的创建过程可以概括为:
  2. 查看当前所有的未提交并活跃的事务,存储在数组中
  3. 选取未提交并活跃的事务中最小的XID,记录在快照的xmin中
  4. 选取未提交事务中最大的XID,记录快照在xmax中
    ReadView主要是用来做可见性判断的,即通过ReadView可以知道:哪些事务的提交结果对当前事务可见,哪些事务的提交结果对当前事务不可见。关于如何判断可见性,后面部分会对可见性判断算法做出介绍。不过,我们可以先思考一个问题,在可重复读隔离级别中,哪些事务的提交结果对当前事务可见呢?
    undo log
    刚才介绍了ReadView的基础概念,提到了ReadView是事务的快照,但是通过ReadView仅仅能知道哪些事务的提交结果对当前事务可见,可是还是不知道当前事务的数据快照在哪啊。undo log就是来解决这个问题的。
    undo log就是我们通常说的回滚日志,undo log存放的是数据的历史记录,也可以叫数据的快照。

当一个事务要提交修改时:
1.会用排他锁锁定该行
2.将该行修改前的值Copy到undo log segment(回滚段)。
3.修改当前行的值,将该行的回滚指针指向undo log中修改前的行。
总结
隔离的实现主要有读写锁和MVCC(Multi-Version Concurrency Control)多版本并发处理方式。MVCC方式由于其读写不冲突的方式,相当于读写锁效率更高。

undo log和ReadView通过可见性判断算法实现了基本的MVCC,从而实现了事务的隔离。

如何避免死锁??
几种避免死锁的常用方法:
(1)在应用中,如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会。
(2)在程序以批量方式处理数据的时候,如果事先对数据排序,保证每个线程按固定的顺序来处理记录,也可以大大降低出现死锁的可能。
(3)在事务中,如果要更新记录,应该直接申请足够级别的锁,即排他锁,而不应先申请共享锁,更新时再申请排他锁,因为当用户申请排他锁时,其他事务可能又已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁。
(4)在REPEATABLE-READ隔离级别下,如果两个线程同时对相同条件记录用SELECT…FOR UPDATE加排他锁,在没有符合该条件记录情况下,两个线程都会加锁成功。程序发现记录尚不存在,就试图插入一条新记录,如果两个线程都这么做,就会出现死锁。这种情况下,将隔离级别改成READ COMMITTED,就可避免问题。
(5)当隔离级别为READ COMMITTED时,如果两个线程都先执行SELECT…FOR UPDATE,判断是否存在符合条件的记录,如果没有,就插入记录。此时,只有一个线程能插入成功,另一个线程会出现锁等待,当第1个线程提交后,第2个线程会因主键重出错,但虽然这个线程出错了,却会获得一个排他锁。这时如果有第3个线程又来申请排他锁,也会出现死锁。对于这种情况,可以直接做插入操作,然后再捕获主键重异常,或者在遇到主键重错误时,总是执行ROLLBACK释放获得的排他锁。

MySQL的日志
MySQL日志:
主要包含:错误日志、查询日志、慢查询日志、事务日志、二进制日志;
日志是mysql数据库的重要组成部分。日志文件中记录着mysql数据库运行期间发生的变化;也就是说用来记录mysql数据库的客户端连接状况、SQL语句的执行情况和错误信息等。当数据库遭到意外的损坏时,可以通过日志查看文件出错的原因,并且可以通过日志文件进行数据恢复。
错误日志
在mysql数据库中,错误日志功能是默认开启的。并且,错误日志无法被禁止。默认情况下,错误日志存储在mysql数据库的数据文件中。错误日志文件通常的名称为hostname.err。其中,hostname表示服务器主机名
log-warnings是定义是否将警告信息也定义至错误日志中。
SHOW GLOBAL VARIABLES LIKE ‘%log%’;查询日志有关的相关信息
查询日志:
默认情况下查询日志是关闭的。由于查询日志会记录用户的所有操作,其中还包含增删查改等信息,在并发操作大的环境下会产生大量的信息从而导致不必要的磁盘IO,会影响mysql的性能的。如若不是为了调试数据库的目的建议不要开启查询日志
慢查询日志:
慢查询日志是用来记录执行时间超过指定时间的查询语句。通过慢查询日志,可以查找出哪些查询语句的执行效率很低,以便进行优化。一般建议开启,它对服务器性能的影响微乎其微,但是可以记录mysql服务器上执行了很长时间的查询语句。可以帮助我们定位性能问题的。
long_query_time(默认为10秒)
set global slow_query_log=1; #开启慢查询日志
二进制日志:
二进制日志也叫作变更日志,主要用于记录修改数据或有可能引起数据改变的mysql语句,并且记录了语句发生时间、执行时长、操作的数据等等。所以说通过二进制日志可以查询mysql数据库中进行了哪些变化。

MySQL索引:
BTree索引
Mysql数据库中使用最频繁的索引类型,基本所有存储引擎都支持BTree索引。正如其名,这类索引的物理文件大多就是以BTree结构来存储的
hash索引:
a 仅仅能满足"=",“IN"和”<=>"查询,不能使用范围查询
b 其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引
c 只有Memory存储引擎显示支持hash索引
3、FULLTEXT索引(现在MyISAM和InnoDB引擎都支持了)
4、R-Tree索引(用于对GIS数据类型创建SPATIAL索引)
从物理存储角度
聚集索引:
  该索引中键值的逻辑顺序决定了表中相应行的物理顺序。
  聚集索引确定表中数据的物理顺序。聚集索引类似于电话簿,后者按姓氏排列数据。由于聚集索引规定数据在表中的物理存储顺序,因此一个表只能包含一个聚集索引。但该索引可以包含多个列(组合索引),就像电话簿按姓氏和名字进行组织一样。
非聚集索引:  
  该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同。
  索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。

从逻辑角度
1、主键索引:主键索引是一种特殊的唯一索引,不允许有空值
2、普通索引或者单列索引
3、多列索引(复合索引):复合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用复合索引时遵循最左前缀集合
4、唯一索引或者非唯一索引
5、空间索引:空间索引是对空间数据类型的字段建立的索引,MYSQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING、POLYGON。MYSQL使用SPATIAL关键字进行扩展,使得能够用于创建正规索引类型的语法创建空间索引。创建空间索引的列,必须将其声明为NOT NULL,空间索引只能在存储引擎为MYISAM的表中创建
php实现多继承-trait语法
自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。
Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。
从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
Linux查看负载
负载(load)是linux机器的一个重要指标,直观了反应了机器当前的状态。如果机器负载过高,那么对机器的操作将难以进行。
Linux的负载高,主要是由于CPU使用、内存使用、IO消耗三部分构成。任意一项使用过多,都将导致服务器负载的急剧攀升。
查看服务器负载有多种命令,w或者uptime都可以直接展示负载
这两个命令只是单纯的反映出负载,linux提供了更为强大,也更为实用的top命令来查看服务器负载。
top命令能够清晰的展现出系统的状态,而且它是实时的监控,按q退出。
Tasks行展示了目前的进程总数及所处状态,要注意zombie,表示僵尸进程,不为0则表示有进程出现问题。
Cpu(s)行展示了当前CPU的状态,us表示用户进程占用CPU比例,sy表示内核进程占用CPU比例,id表示空闲CPU百分比,wa表示IO等待所占用的CPU时间的百分比。wa占用超过30%则表示IO压力很大。
Mem行展示了当前内存的状态,total是总的内存大小,userd是已使用的,free是剩余的,buffers是目录缓存。
Swap行同Mem行,cached表示缓存,用户已打开的文件。如果Swap的used很高,则表示系统内存不足。
三大特性是:封装、继承、多态
所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
封装是面向对象的特征之一,是对象和类概念的主要特性。 简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
所谓继承是指可以让某个类型的对象获得另一个类型的对象的属性的方法,它支持按级分类的概念。
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。
多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。
五大基本原则
单一职责原则SRP(Single Responsibility Principle)
是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。
开放封闭原则OCP(Open-Close Principle)
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,
那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。
替换原则(the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,
也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。
依赖原则(the Dependency Inversion Principle DIP) 具体依赖抽象,上层依赖下层。
假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:这样就达到
了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。一个常见的问题就是编译A模块时需要直接包含到B模块的cpp文件,而编译B时同样要直接包含到A的cpp文件。
接口分离原则(the Interface Segregation Principle ISP)
模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值