最近我觉得自己各项技能都是随着工作而学习的,感觉总有一些知识没有掌握,特准备在基于《Mysql权威指南》和《高性能Mysql》这两本书来系统学习下,把每次学习的笔记整理成博客的形式。一是为了加深自己的印象,二是为了给自己提供更好的学习能力,三是和大家共同分享。
1、逻辑架构
第一层:并不是Mysql独有,大多数基于网络客户端、服务器工具,例如:连接处理、授权认证、安全等
第二层:核心服务层,包括查询解析、分析、优化、缓存、内置函数,所有跨域存储引擎都在这一层:存储过程、触发器、视图
第三层:包含存储引擎,负责Mysql数据的存储和提取。
2、Mysql的并发控制
Mysql在服务器层和存储引擎层的的并发控制。
2.1 读写锁
在处理并发读写时可以通过实现一个由两种类型的锁组成的锁系统来解决问题,这两种类型的锁被称为共享锁和排他锁(exclusive lock),也叫读锁和写锁
描述下锁的概念:读锁是共享的,写锁是排他的。
在实际数据库应用中每时每刻都在发生锁定,Mysql锁内部管理透明。
2.3 锁粒度
一种提高共享资源并发性的方式就是让锁定对象更有选择性。尽量只锁定需要修改的部分数据。更理想的方式只对修改的数据片进行精确锁定。锁定的数据量越少并发程度越高。
加锁也是消耗资源的,锁的各种操作,包括获得锁、检测锁是否是否已解除、释放锁等。
所谓的锁策略就是锁的开销和数据的安全性之间寻求平衡,这种平衡当然也会影响性能。
Mysql提供了多种选择,每种存储引擎都可以实现自己的锁策略和锁粒度。下面来介绍两种锁策略:
1、表锁
Mysql基本策略,开销最小。
2、行级锁
最大程度的支持并发,同时也带来了最大的锁开销。行级锁只在存储引擎层实现,而Mysql服务器层没有实现。服务器层完全不了解存储引擎中的锁实现。
所有的存储引擎都以自己的方式显现了锁机制。
3、事务
ACID原则
原子性atomicity、一致性consistency、隔离性isolation、持久性durability
1、隔离级别
read uncommitted(未提交读),事务可以读取到未提交的数据,称为脏读
read committed,一个事务未提交之前所做的对其他事务都是不可见的
repeatable read,解决了脏读问题,该级别保证了同一个事务中多次读取同样记录的结果是一致的。但是理论上无法解决幻读,所谓幻读是,当某个事务正在读取某一个范围的记录时,另外一个事务又向该范围插入一条新的记录,当之前事务再次读取时会产生幻行。Mysql的InnoDB和XtrsDB存储引擎通过多版本并发控制(MVVC,multiversion concurrency control )解决了幻读问题。
serializable可串行化,串行执行,隔离级别最高,超时和锁争用严重,不常用
2、死锁
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环。当事务试图以不同的顺序锁定资源时,就可能产生死锁。多个事务同时锁定同一个资源时也会产生死锁。
例如:
事务1:
start transaction;
update goods set price = 20 where goods_id = 4 and date=’2015-4-22’;
update goods set price = 30 where goods_id = 3 and date = ‘2015-4-22’;
commit;
事务2:
start transction;
update goods set price = 40 where goods_id = 3 and date = ‘2015-4-22’;
update goods set price = 50 where goods_id = 4 and date = ‘2015-4-22’;
commit;
为了解决这种问题,数据库系统实现了各种死锁检测和死锁超时的机制,越复杂的系统,比如:InnoDB存储引擎,越能检测到死锁的循环依赖,并立即返回一个错误。
还有一种解放方式,当查询的时间达到锁等待超时的设定后放弃锁请求。InnoDB目前处理死锁的方法是:将持有最少行级锁排他锁事务进行回滚。
锁的行为和顺序和存储引擎相关。以同样的顺序执行语句,有些存储引擎会产生死锁有些不会。死锁有双重原因:真正的数据冲突;存储引擎的实现方式。
3、事务日志
提高事务效率,如果修改的已经记录到事务日志并持久化,但数据本身还没回写磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这部分修改的数据。具体实现方式视存储引擎而定。
4、Mysql中的事务
自动提交(autocommit)
Mysql默认采用自动提交模式,可以通过设置autocommit变量来启用或禁用自动提交模式:
show VARIABLES like ‘autocommit’;
1或on表示启用,0或off表示关闭。
Mysql可以通过执行 set transaction isolation level命令来设置隔离级别,新的隔离级别会在下一个事务开始的时候生效。
例如:set session transaction isolation level read committed;
在事务中混合使用存储引擎
如果在事务中混合使用了事务型和非事务型的表(InnoDB和Myisam)正常提交是不会有问题的,如果事务回滚,非事务型的表变更是无法撤销的,这会导致数据库处于不一致状态。
隐式和显式锁定
InnoDB在事务执行过程中,随时都可以执行锁定,锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在同一时刻被释放,前面描述的都是隐式锁,InnoDB会根据隔离级别在需要的时候自动加锁。
另外,InnoDB也支持通过特定的语句进行显示锁定
select … lock in share mode – 共享锁
select … for update – 排他锁
Mysql也支持lock tables和unlock tables,这都是在服务器层实现的,和存储引擎无关。
5、多版本并发控制
Mysql的大多数事务型存储引擎实现都不是简单的行级锁。基于提升并发性考虑,一般都同时实现了多版本并发控制(MVCC),包括Oracle、PostgreSQL。不过实现各不相同。
可以认为MVCC是行级锁一个变种,但是他很多情况下避免了加锁操作,开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。
MVCC的实现是通过保存数据在某一个时间点快照来实现的。也就是说不管实现时间多长,每个事物看到的数据都是一致的。
分为乐观(optimistic)并发控制和悲观(pressimistic)并发控制。下面来说说是如何工作的:
InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现。这两个列一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是真实的时间而是系统版本号,每开始一个新的事务,系统版本号都会自动新增。事务开始时刻的系统版本号会作为事务的版本号,用来查询到每行记录的版本号进行比较,下面看下REPEATABLE READ隔离级别下MVCC如何工作:
SELECT
InnoDB会根据以下条件检查每一行记录:
a.InnoDB只查找版本早于当前事务版本的数据行,这样可以确保事务读取的行要么是在开始事务之前已经存在要么是事务自身插入或者修改过的
b.行的删除版本号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行在事务开始之前未被删除
只有符合上述两个条件的才会被查询出来
INSERT
InnoDB为新插入的每一行保存当前系统版本号作为行版本号
DELETE
InnoDB为删除的每一行保存当前系统版本号作为行删除标识
UPDATE
InnoDB为插入的一行新纪录保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为删除标识
保存这两个版本号使大多数操作都不用加锁。使数据操作简单,性能很好,并且能保证只会读取到复合要求的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作和一些额外的维护工作。
MVCC只在REPEATABLE READ和COMMITTED READ
6、Mysql的存储引擎
6.1 InnoDB存储引擎
数据存储在表空间,表空间是由InnoDB管理的一个黑盒子,由一系列数据文件组成,在Mysql4.1以后版本,InnoDB可以将表的数据和索引放在单独的文件中。
InnoDB采用MVCC来支持高并发,默认隔离级别是REPEATEABLE READ,并且通过间隙锁(nex-key locking)策略防止幻读的出现,使得InnoDB不仅仅锁定查询涉及的行还会对索引中的间隙进行锁定,防止幻影行的插入。
InnoDB基于聚簇索引建立,对主键查询有很高的性能,不过他的二级索引(非主键索引)必须能包含主键列,如果主键列很大的话其他的所有索引都会很大。因此如果索引较多的话主键当尽可能的小。
6.2 Myisam存储引擎
Myisam会将表存储在两个文件中:数据文件和索引文件,可存储的行记录数一般受限于可用的磁盘空间或者操作系统中单个文件的最大尺寸。
特性:
1、加锁与并发
对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排他锁
2、修复
Mysql可用手工或自动执行检查和修复操作,但不同于事务回复及崩溃修复。执行表的修复可能导致一些数据丢失,而且修复操作非常慢。
3、索引特性
即使是BLOB和TEXT等长字段也可以基于其前500个字符创建索引。也支持全文索引,这是一种基于分词创建的索引
大数据量
当我们创建或者管理很多InnoDB数据的数据量在3~5TB之间或者更大,这是但台机器的量不是一个分片(shard),这些系统运行得还不错。如果数据量继续增长到10TB可能需要建立数据仓库,Infobright是Mysql数据仓库最成功的解决方案。
转换表的存储引擎:
1、alter table mytable engine = InnoDB;执行时间长,消耗I/O能力,同时会在原表上加锁,将会失去原引擎相关的所有特性,例如:如果将一张InnoDB表转换为Myisam再转回去,原InnoDB表上所有的外键将丢失。
2、导出与导入
使用mysqldump工具将数据导出到文件,然后修改文件create table语句的存储引擎选项,同时修改表名,因为同一个数据库中不能存在相同的表名,即使使用不同的存储引擎。
3、创建与查询
综合上述两种方法的高效和安全,不需要导出整个表的数据,而是先创建一个新的存储引擎表。然后利用insert … select语法来到数据
create table innodb_table like myisam_table;
alter table innodb_table engine=InnoDB;
insert into innodb_table select * from myisam_table;
数据量大考虑分批处理
start transaction;
insert into innodb_table select * from myisam_table where id between x and y;
commit;