zoukankan      html  css  js  c++  java
  • 《高性能mysql》读书笔记:mysql架构与历史

    1、Mysql逻辑架构

    1.1 三层架构

    第一层:服务层,连接处理、授权认证、安全认证等;
    第二层:查询解析、分析、优化、缓存等;
    第三层:存储引擎,负责数据存储和提取;

    1.2 并发控制

    1.2.1 读写锁:
    读锁又叫共享锁,读锁是共享的,相互不阻塞的,多个客户在同一时刻可以同时读取同一个资源,互不干扰。
    写锁又叫排它锁,写锁是排他的,写锁会阻塞其他的写锁和读锁。

    ---------------------------------------------
    补充:
    读锁(共享锁)是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改,直到所有共享锁都释放了才行。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。

    写锁(排他锁)如果事务T对数据A加上排他锁后,则其他事务不能再对A加任何类型的锁。获准排他锁的事务既能读取数据,也能修改数据。

    事务可以通过以下语句给sql加共享锁和排他锁:
    共享锁:select …… lock in share mode;
    排他锁:select …… for update;

    1.2.2 琐粒度:
    表锁:锁定整张表。只有没有写锁时,其他读取的用户才能获得读锁。
    行锁:行级锁可以最大程度地支持并发处理。InnoDB实现了行级锁。行级锁由存储引擎层实现。

    ---------------------------------------------
    补充:
    MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);
    BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;
    InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

    1.3 事务

    ACID表示事务的原子性、一致性、隔离性和持久性。
    原子性:一个事务被视为一个不可分割的最小工作单元。整个事务的所有工作要么全部提交成功,要么全失败回滚。
    一致性:数据库总是从一个一致性的状态转换至另外一个一致性的状态。
    隔离性:一个事务所做的修改再最终提交以前,对其他事务是不可见的。
    持久性:一旦事务提交,则其所做的修改就会永久保存到数据库中。

    ----------------------------------------------
    补充:
    隔离性和持久性很好理解,但是原子性和一致性容易混淆。一致性和原子性的区别:
    原子性和一致性的的侧重点不同:原子性关注状态,要么全部成功,要么全部失败,不存在部分成功的状态;而一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见。

    1.3.1 隔离级别
    read uncommitted(未提交读)
    事务的修改,即使没有提交,对其他事务也都是可见的。事务可以读取其他事务未提交的数据,会产生脏读。

    read committed(提交读,不可重复读)
    事务的修改,只有提交了,对其他事务才是可见的。一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别又叫不可重复读,因为两次执行同样的查询,可能得到不一样的结果。

    repeatable read(可重复读)
    解决了脏读,保证了在同一个事务中多次读取同样记录的结果是一致的。可重复读无法解决另外幻读问题。幻读是指,一个事务在读取某个范围的记录时,另外一个事务又在该范围插入了新的记录,当之前的事务再次读取该范围的记录时,就会产生幻行。InnoDB使用多版本并发控制MVCC解决了幻读问题。
    可重复读是mysql默认的级别。

    serializable(可串行化)
    可串行化是最高的事务隔离级别。它会在读取的每一行数据上都加锁,强制事务串行化执行,避免了幻读问题。但是可能导致大量超时和锁争用问题。实际应用中很少使用这个级别。

    -------------------------------------------------
    补充:
    事务隔离级别中的不可重复读和幻读问题容易混淆。

    不可重复读:事务前后两次select可能会返回不同的结果,即第一次select以后,如果中间有其他事务提交了对数据的修改,那么第二次select就会看到不同的值,与第一次select的结果不一致。不可重复读指的就是这种情况,就是多次select的结果可能不一样。

    幻读:一个事务在读取某个范围的记录时,另外一个事务又在该范围插入了新的记录,当之前的事务再次读取该范围的记录时,就会产生多余的数据行。

    不可重复读是由于其他事务提交了对数据的修改导致的。而幻读是由于其他事务提交了对数据的新增导致的。

    这里书中有误,mysql目前在repeatable read级别已经解决了幻读问题。首先,读取数据分为快照读和实时读。

    快照读:一般的 select * from .... where ... 语句都是快照读。快照读不会加锁。
    实时读:
    select * from .... where ... FOR UPDATE 加排它锁;
    select * from .... where ... LOCK IN SHARE MODE,加共享锁;
    包括插入insert、删除delete、更新update,都会给数据行加上排它锁。

    当MySQL的数据引擎是innodb引擎时,默认的隔离级别是可重复读,普通读的幻读问题的解决,主要是通过mvcc机制实现的,普通select语句查询时总是查询事务开始时的数据状态,也就是快照读来解决的。而实时读的幻读问题是通过查询时先对数据加上next key lock,锁定范围,防止其他事务插入数据,来解决幻读。

    Record Lock:单个行记录上的锁。
    Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
    Next-Key Lock:1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。

    测试:
    1)先关闭事务的自动提交,然后打开两个会话模拟事务操作来测试不可重复读和幻读问题。可以发现,repeatable级别,不管是普通读(快照读)还是实时读,都不会产生幻读问题,也就是前后两次查询结果都是一致的。

    2)但是在普通读时会存在这样一个问题,即一个事务查询后,结果里没有记录A,但另一个事务刚好插入记录A并提交了,这时当前事务再插入记录A就会报错。原因很简单,普通读读的是快照版本,但插入数据是实时的,而数据已经被另一个事务插入了,就会报重复插入的错误。或许这个是普通读的幻读遗留问题。

    3)同样地,如果当前事务A不是使用的普通读,而是实时读,那么就不会存在这样的问题。因为实时读会加锁,其他事务在这个范围是没法插入数据的,会等待当前事务释放锁。而当当前事务提交后,数据已经插入了,那其他事务获得锁后再执行插入就会报错。所以,当前事务会正常执行的。

    测试相关的sql命令:
    SELECT @@tx_isolation; #查询当前事务隔离级别
    SHOW VARIABLES like 'autocommit'; #查看是否自动提交事务
    SET autocommit = 0; #设置不自动提交事务
    BEGIN #开始事务
    SELECT ... FROM ... #快照读
    SELECT ... FROM ... LOCK IN SHARE MODE; #实时读,加共享锁
    SELECT ... FROM ... FOR UPDATE; #实时读,加排它锁
    COMMIT; #提交事务

    1.3.2 死锁

    概念:死锁是指多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。死锁发生后,只有部分或者完全回滚其中一个事务,才能打破死锁。对于事务型系统,这是无法避免的。

    InnoDB检测死锁:
    1)检测到死锁的循环依赖,返回一个错误;
    2)锁等待超时后,放弃锁请求;

    InnoDB处理死锁:
    将持有最少行级排他锁的事务进回滚;

    ------------------------------------------------------
    补充:
    查看发生死锁的事务线程,手动kill掉发生死锁的线程,让其回滚,释放掉锁资源。
    步骤:
    1)查看所有正在等待锁的事务,可以看出哪些事务正在等待哪些锁,等待哪些事务
    SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

    2)查看所有的锁
    SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

    3)查看所有正在执行的事务,根据blocking的事务id,可以查出线程id
    SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

    4)杀死阻塞的线程
    KILL thread_id;

    5)查看所有线程,可以验证是否杀掉
    SHOW FULL PROCESSLIST;

    1.3.3 事务日志
    事务日志可以帮助存储引擎提交事务执行的效率。使用事务日志,修改数据时只需要修改内存拷贝,再把修改操作步骤记录到硬盘上的事务日志中即可。修改的数据可以慢慢刷回到磁盘中。这样的好处,写日志是在文件末尾追加,是一小块区域的顺序I/O,速度很快;而写数据则是整个磁盘的随机I/O,太耗时。
    如果数据的修改已经记录到事务日志并持久化了,即使数据本身没有写回磁盘,此时系统崩溃,存储引擎在重启时也能根据事务日志自动化恢复修改的数据。

    ---------------------------------------------
    补充:
    InnoDB事务日志包括redo log和undo log。redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作。

    1.3.4 mysql中的事务
    mysql提供两种事务型存储引擎:InnoDB和NDB Cluster。
    自动提交AUTOCOMMIT
    默认采用自动提交,如果不是显示地开始一个事务,每个查询都被当作一个事务。
    查看自动提交:SHOW VARIABLES LIKE 'AUTOCOMMIT';
    设置自动提交:SET AUTOCOMMIT = 1;

    不建议混合使用存储引擎,可能导致事务失败无法回滚。
    InnoDB显示锁定语句:
    共享锁/S琐:SELECT ... LOCK IN SHARE MODE;
    排它锁/X琐:SELECT ... FOR UPDATE;

    1.4 多版本并发控制

    MVCC是基于提升并发性能的考虑,是通过保存数据在某个时间点的快照来实现的。很多数据库系统都实现了MVCC,但是实现机制各不相同。

    InnoDB的MVCC实现机制:
    在每行记录后面保存两个隐藏的列:一个保存行的创建时间,一个保存行的过期时间。用系统版本号代替时间值。每开始一个新的事务,系统版本号自动递增。
    InnoDB会根据以下两个条件检查每行记录:
    1)只查找系统版本号小于或等于当前事务版本的数据行;
    2)行的删除版本号要么未定义,要么大于当前事务版本号;
    MVCC只在REPEATABLE READ和READ COMMITED两个隔离级别下工作。

  • 相关阅读:
    编码
    TCP
    Http
    信息安全
    https基本原理
    Android之ListView异步加载图片且仅显示可见子项中的图片
    android 一些数据转换方法
    Android 关于 OnScrollListener 事件顺序次数的简要分析
    图片的内存缓存控制
    Android实现图片宽度100%ImageView宽度且高度按比例自动伸缩
  • 原文地址:https://www.cnblogs.com/alan6/p/13559198.html
Copyright © 2011-2022 走看看