MySQL的架构介绍
mysql 简介
- MySQL是一个关系型数据库管理系统(RDBMS),由瑞典MySQL AB公司开发,目前属于Oracle公司
- MySQL是一种关联数据库管理系统,将数据保存在不同的表里,提高速度和灵活性
- MySQL是开源的,支持处理千万条记录,使用标准的SQL数据语言形式
- MySQL可以允许在多个系统上,支持多种编程语言,包括C/C++/python/java/perl/php/eiffel/ruby/tcl等
- MySQL对PHP 有很好的支持,PHP是目前最流行的web开发语言
- MySQL支持5000万条记录的数据仓库,32位系统表文件最大可支持4GB,64位支持8TB
- MySQL可定制,采用了GPL协议,可以修改源码开发自己的MySQL系统
MySQLLinux安装
- 从官方下载MySQL Linux 版本下的客户端和服务端
- 安装
rpm -qa|grep -i mysql //检查当前系统是否安装过MySQL,安装过会出现MYSQL 安装的版本
rpm -ivh MySQL服务端版本 //安装服务端
/user/bin/mysqladmin -u root password 123456 //设置用户root的密码
rpm -ivh MySQL客户端版本 //安装客户端
//检验安装是否成功
cat /etc/passwd|grep mysql //检验mysql 用户
cat /etc/group|grep mysql //检验MySQL组
mysqladmin --version //检验MySQL版本信息
- mysql 启动和停止
service mysql start //启动
mysql -u root -p 123456//进入MySQL
exit //退出
service mysql stop//停止
ps -ef|grep mysql //查看MySQL数据库文件的存放路径
- mysql 自启动服务
check config mysql on //设置开机自启动MySQL
checkconfig --list|grep mysql //查看MySQL的运行级别
ntsysv //[*]MySQL,表示开机后会自启动MySQL
- 安装目录
ps -rf|grep mysql //在Linux下查看安装目录
路径 | 解释 |
---|---|
/var/lib/mysql/ | mysql数据库文件的存放路径 |
/usr/share/mysql | 配置文件目录 |
/usr/bin | 相关命令目录 |
/etc/init.d/mysql | 启停相关脚本 |
- 修改配置文件位置
cp my-default.cnf /etc/my.cnf //拷贝到/etc/my.cnf中
service mysql stop
service mysql start //重启动
- 修改字符集和数据存储路径
show variables like '%char%'; //查看字符集,默认在客户端和服务器都是latin1
vim 配置文件 //对配置文件进行编辑,更改需要设置的字符集然后保存退出
- 配置文件
- 二进制日志log-bin:主要记录主从复制
- 错误日志 log-error:默认是关闭的,记录严重的警告和错误信息,每次启动和关闭的详细信息等
- 查询日志 log:默认关闭,记录查询的SQL语句,如果开启会降低MySQL的整体性能,记录日志会占用内存,消耗系统资源
- 数据文件:Windows系统和Linux系统下的各个数据库文件;frm文件:存放表结构;myd文件存放表数据;myi文件存放表索引
MySQL逻辑
MySQL和其他数据库相比,其架构可以在多种不同场景中应用,主要体现在存储引擎的架构上,插件式的村塾引擎架构将查询处理和其他的系统任务以及数据的存储提取相分离,可以根据业务的需求和实际需要选择合适的引擎。
- 连接层
最上层是一些客户端和连接服务,包含本地sock通信和大多数基于C/S工具实现的,类似于tcp/ip的通信。主要完成一些类似于连接、处理、授权认证及相关的安全方案。在该层引入了线程池的概念,为通通过认证安全接入的客户端提供线程,同样可以在该层上实现基于SSL 的安全连接,服务器也会为安全接入的每个客户端验证他所具有的操作权限 - 服务层
主要完成大多数的核心服务功能,如SQL接口,完成缓存查询,SQL的分析和优化及部分内置函数的执行,所有跨存储引擎的功能压在这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化,如确定查询表的顺序,是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内部缓存,如果缓存空间足够大,在解决大量读操作的环境中可以很好地提升系统性能。 - 引擎层
存储引擎层,存储引擎真正的负责MySQL中数据的存储和提取,服务器通过API 与存储引擎进行通信,不同的存储引擎具有的功能不同,可以根据自己的实际需求进行选取,常用的是InnoDB和MyISAM。可以通过show engines;查看数据库的存储引擎 - 存储层
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
- 比较存储引擎
对比项 | MyISAM | InnoDB |
---|---|---|
主外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
行表锁 | 表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作 | 行锁,操作时只锁某一行,不对其他行有影响,适合高并发的操作 |
缓存 | 只缓存索引,不缓存真实数据 | 缓存索引和真实数据,对内存要求较高,内存大小对性能有决定性的印象 |
表空间 | 小 | 大 |
关注点 | 性能 | 事务 |
默认安装 | 是 | 是 |
- 阿里巴巴和淘宝
- Percona对MySQL数据库服务器进行了改进,在功能和性能上提升了在高负载情况下的innodb的性能,为DBA提供一下非常有用的性能诊断工具,有更多的参数和命令控制服务器行为
- Percona新建了一款存储引擎xtradb,完全可以替代innodb,并且在性能和并发上做的更好
- 阿里巴巴原先大部分MySQL数据库使用的是Percona原先xtradb并加以修改,现在又开发了 AliSql+AliRedis
索引优化分析
性能下降,SQL 慢,执行时间长,等待时间长的原因
- 查询语句书写复杂,不好
- 索引(单值或复合)失效
- 关联查询大多join,除非是数据库设计缺陷或者不得已的需求下,尽量少用join
- 服务器调优及各个参数设置(缓冲、线程数等)存在问题
join语句查询
- SQL执行加载顺序
select 7
distinct 8
<select_list >
from 1
<left_table > <join_type >
join 3
<right_table >
on 2
<join_condition >
where 4
<where_condition>
group by 5
<group_by_list>
having 6
<having_condition>
order by
<order_by_condition> 9
limit <limit number> 10
- join 7种查询
索引简介
- 索引(BTree)
- 帮助mysql高校获取数据的数据结构,排好序的快速查找数据结构 。打个比方,拿汉语字典的目录页(索引)打比方,我们可以按拼音、笔画、偏旁部首等排序的目录(索引)快速查找到需要的字。
- 目的:为了提高查询效率,可以类比字典
- 数据库系统维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据
- 影响: 在表中有大量数据的前提下,创建索引速度会很慢;在索引创建完毕后,对表的查询性能会发幅度提升,但是写性能会降低
- 索引本身内存也很大,不可能去那不存储在内存中,往往以索引文件的形式存储在磁盘上
- 创建索引的本质:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
平常所说的索引,若未特别指明,都是指B树(多路搜索树,不一定是二叉树)结构组织的索引。其中聚集索引/次要索引/复合索引/前缀索引/唯一索引默认使用B+树索引,此外还有哈希索引等
- 索引的优缺点
- 优点:提高数据检索的效率,降低数据库的io成本;通过索引列对数据进行排序,降低数据排序的成本,降低了cpu的消耗
- 缺点:索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,索引列也会占用内存空间;更新表时,mysql不仅保存数据,也保存岁因文件中每次更新的索引列字段,都会调整因为更新所带来的键值变化后的索引信息。索引会降低表的更新速度。
- 面对大数据量的表,需要建立最优化的索引列。
- 索引的分类
- 单值索引:一个索引只包含单个列,一个表可以有多个索引列
- 唯一索引:索引列的值必须唯一,但允许有空值
- 复合索引:一个索引包含多个列
- 基本语法
//创建
create [unique] INDEX indexname ON tablename(columnname(length) );
或者
alter tablename add [unique] INDEX [indexname] ON (columnname(length));
//删除
DROP INDEX [indexname] ON tablename;
//查看
SHOW INDEX FROM tablename
//有四种方式来添加数据表的索引:
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list); //该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list);//这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。
ALTER TABLE tbl_name ADD INDEX index_name (column_list);//添加普通索引,索引值可出现多次。
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list);//该语句指定了索引为 FULLTEXT ,用于全文索引。
- 索引的结构 参考链接
- BTree
B-树索引又称为 BTREE 索引,目前大部分的索引都是采用 B-树索引来存储的。B-树索引是一个典型的数据结构,其包含的组件主要有以下几个:
- 叶子节点:包含的条目直接指向表里的数据行。叶子节点之间彼此相连,一个叶子节点有一个指向下一个叶子节点的指针。
- 分支节点:包含的条目指向索引里其他的分支节点或者叶子节点。
- 根节点:一个 B-树索引只有一个根节点,实际上就是位于树的最顶端的分支节点。
如上图,是一颗b+树,最上层是树根,中间的是树枝,最下面是叶子节点,浅蓝色的块叫做block块,这是操作系统一次IO往内存中读的内容,一个块对应四个扇区,可以看到每个磁盘块包含几个数据项和指针,如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。
基于这种树形数据结构,表中的每一行都会在索引上有一个对应值。因此,在表中进行数据查询时,可以根据索引值一步一步定位到数据所在的行。
B-树索引可以进行全键值、键值范围和键值前缀查询,也可以对查询结果进行 ORDER BY 排序。但 B-树索引必须遵循左边前缀原则,要考虑以下几点约束:
查询必须从索引的最左边的列开始。
查询不能跳过某一索引列,必须按照从左到右的顺序进行匹配。
存储引擎不能使用索引中范围条件右边的列。
- 哈希索引
- 哈希(Hash):把任意长度的输入(又叫作预映射,pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。
- 哈希索引也称为散列索引或 HASH 索引。MySQL 目前仅有 MEMORY 存储引擎和 HEAP 存储引擎支持这类索引。其中,MEMORY 存储引擎可以支持 B- 树索引和 HASH 索引,且将 HASH 当成默认索引。
HASH 索引不是基于树形的数据结构查找数据,而是根据索引列对应的哈希值的方法获取表的记录行。哈希索引的最大特点是访问速度快,但也存在下面的一些缺点:
MySQL 需要读取表中索引列的值来参与散列计算,散列计算是一个比较耗时的操作。也就是说,相对于 B- 树索引来说,建立哈希索引会耗费更多的时间。
不能使用 HASH 索引排序。
HASH 索引只支持等值比较,如“=”“IN()”或“<=>”。
HASH 索引不支持键的部分匹配,因为在计算 HASH 值的时候是通过整个索引值来计算的。
- 索引使用场景
- 适合使用的场景
主键自动建立唯一索引;
频繁作为查询条件的字段应该创建索引;
查询中与其他表关联的字段,外键关系建立索引;
频繁更新的字段不适合创建索引;
where条件里用不到的字段不创建索引;
单键/组合索引最好选组合索引,尤其是高并发下;
查询中排序的字段,排序字段若通过索引去访问会大大提高排序速度;
查询中统计或者分组字段;
- 不适合使用的场景
表记录太少;
经常增删改的表;
数据重复且分布平均的表字段 ,索引应该为最经常查询和最经常使用的字段
索引的性能分析
- MySQL Query Optimizer
- mysql有专门负责优化select语言的优化器模块,通过计算分析系统中收集到的统计信息,为客户端请求的query提供他认为最优的执行计划,但实际上不一定是最优执行计划
- 当客户端向mysql请求query,命令解析器模块完成请求分类。区分出是select语句并转发给MySQL Query Optimizer首先会对整条语句进行优化,处理掉一些常用表达式的预算,转换常量值,并对查询条件进行简化和转换,去掉一些无用或简单条件,调整结构等。然后分析query中的hint信息,看显示hint信息是否可以完全确定该query的执行计划,如果hint不足以完全确定执行计划,会读取涉及对象的统计信息,根据query写相应的计算分析,得出执行计划。
- MySQL 常见瓶颈
- CPU:CPU在饱和,一般发生在数据装入或从磁盘上读取数据时
- IO:磁盘I/O瓶颈发生在装入数据远远大于内存容量的时候
- 服务器硬件的性能瓶颈:服务器硬件难以支持mysql 数据管理,或者服务器硬件性能参数未达到配置要求,可以使用top,free,iostat和vmstat来查看此时系统的性能状态
explain 查询执行计划
实现的功能:
- 表的读取顺序如何
- 数据读取操作有哪些操作类型
- 哪些索引可以使用
- 哪些索引被实际使用
- 表之间是如何引用
- 每张表有多少行被优化器查询
expain出来的信息有10列,分别是id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra
- 语法:
explain 查询语句
- id:决定表的执行顺序
SELECT识别符, SELECT的查询序列号,包含一组数字,表示查询中执行select字句或者操作表的顺序
- id相同时,执行顺序由上至下
- id不同时,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
- id相同不同同时存在:如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
- NULL 最后执行
- select_type
显示查询中每个select子句的类型
- SIMPLE(简单SELECT,不使用UNION或子查询等)
- PRIMARY(子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)
- UNION(在第二个SELECT 出现在UNION 之后,则被标记为UNION,在UNION 包含FROM 子句的子查询中,外层SELECT 则被标记为DERIVED,UNION中的第二个或后面的SELECT语句)
- DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)
- UNION RESULT(UNION的结果,union语句中第二个select开始后面所有select)
- SUBQUERY(子查询中的第一个SELECT,结果不依赖于外部查询)
- DEPENDENT SUBQUERY(子查询中的第一个SELECT,依赖于外部查询)
- DERIVED(派生表的SELECT, FROM子句的子查询,会增加系统负担)
- UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)
- table
显示这一步所访问数据库中表名称(显示这一行的数据是关于哪张表的),有时不是真实的表名字,可能是简称
- type
对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好),一般来说得保证查询至少达到range级别,最好能达到ref
- ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行,从硬盘数据文件中读取
- index: Full Index Scan,index与ALL区别为index类型只遍历索引树,从索引文件中读取
- range:只检索给定范围的行,使用一个索引来选择行,where后面的<,>in,between等
- ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值,返回匹配某个单独值的所有行
- eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
- const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system
- NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
- possible_keys
指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示 null)
该列完全独立于EXPLAIN输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用。
如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询
- key
显示MySQL实际决定使用的键(索引),必然包含在possible_keys中。
如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
如果查询的字段跟建立的索引字段、个数、顺序一致,会出现覆盖索引现象。
- key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的),不损失精确性的情况下,长度越短越好
- ref
列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
- rows
估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
- Extra
该列包含MySQL解决查询的额外详细信息,有以下几种情况:
- Using where:不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤
- Using index:表示相应的select操作使用了覆盖索引,避免访问了表的数据行,如果同时出现using where 表明索引被用来执行索引键值的查找;如果没有同时出现,表明索引用来读取数据而非执行查找动作
- Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by ; order by
- Using filesort:当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序”,说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行调用
- Using join buffer:该值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。
- Impossible where:这个值强调了where语句会导致没有符合条件的行(通过收集统计信息不可能存在结果)。
- Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行
- distinct:查找distinct 值,当mysql找到了第一条匹配的结果时,将停止该值的查询,转为后面其他值查询
索引优化
- join 语句的优化
- 尽可能减少join语句中的NestedLoop 的循环总次数,小结果集驱动大结果集
- 优先优化NestedLoop的内层循环
- 保证join语句中被驱动表上join条件字段已经被索引
- 当无法保证被驱动表的join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer 的设置 - 索引失效
- 全值匹配,带头大哥不能丢
- 最佳前缀,查询从索引的最左前列开始并且不跳过索引中的列
- 不再索引列上做任何操作,包括计算、函数、自动或手动的类型转换,会导致索引失效而转向全表扫描
- 存储引擎不能使用索引中范围条件右边的列
- 尽量使用覆盖索引,只访问索引的查询,即索引列和查询列一致,减少select *
- 在使用不等于 !=或者<>的时候索引失效,变成全表扫描
- is null 和 is not null会导致索引失效
- like 通配符以%开头会变层全表扫描,但使用覆盖索引会解决
- 字符串不加单引号索引失效
- 少用or,用它来连接会导致索引失效
间隙锁的危害
- 当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,innodb会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内单不存在的记录,叫做“间隙”GAP
- innodb也会给这个间隙加锁,这种锁机制叫做间隙锁
- 危害:query执行过程中通过范围查找会锁定整个范围内的所有索引键值,即使这个键值不存在,当锁定一个范围键值后,不存在的键值也会被无辜的锁定,造成在锁定时无法插入锁定键值范围的任何数据,在某些场景下可能会对性能造成很大危害。
如何锁定一行?
begin;
select xxx for update; //锁定某一行后,其他的操作被阻塞,直到锁定行的会话提交后才能执行其他操作
commit;
innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会更高一些,但是在整体并发处理能力远远优于myisam的表级锁定。但是innodb的行级锁定很脆弱,使用不当整体性能会下降
分析行锁定
通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况
- innodb_row_lock_current_waits: 当前正在等待锁定的数量;
- innodb_row_lock_time: 从系统启动到现在锁定总时间长度;
- innodb_row_lock_time_avg: 每次等待所花平均时间;
- innodb_row_lock_time_max: 从系统启动到现在等待最长的一次所花的时间;
- innodb_row_lock_waits:系统启动后到现在总共等待的次数;
当等待次数很高而且每次等待时长长,需要分析系统 (show profile)
show status like 'innodb_row_lock%';
优化检索
- 尽可能让所有数据检索通过索引完成,避免无索引行锁升级为表锁
- 合理设计索引,尽量缩小锁的范围
- 尽可能减少检索条件,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度
- 尽可能低级别事务隔离
页锁:开销和加锁时间介于表锁和行锁之间,会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般
主从复制
- 复制的基本原理
slave会从master读取binlog来进行数据同步
- 主库master记录二进制日志,每次准备提交事物完成数据库更新前,先记录二进制日志,记录二进制日志后,主库会告诉存储引擎可以提交事物了
- 备库slave将主库master的二进制日志binary log复制到本地的中继日志中,这个过程叫binary log events
- 备库slave 将master的binary log events拷贝到中继日志relay log中。
-slave 重做中继日志的时间,将改变应用到自己的数据库中国,MySQL复制是异步且串行化的。
- 复制的基本原则
- 每个slave只有一个master
- 每个slave只有一个唯一的服务器ID
- 每个master可以有多个slave
- 复制的最大问题(延时)
- 产生延迟原因?
主节点如果执行一个很大的事务(更新千万行语句,总之执行很长时间的事务),那么就会对主从延迟产生较大的影响
网络延迟,日志较大,slave数量过多。
主上多线程写入,从节点只有单线程恢复 - 处理办法:
大事务:将大事务分为小事务,分批更新数据。
减少Slave的数量,不要超过5个,减少单次事务的大小。
MySQL 5.7之后,可以使用多线程复制,使用MGR复制架构
- 一主一从常见配置
- MySQL版本一致且后台以服务运行
- 主从都配置在[mysql]结点下,都是小写
- 主机修改my.ini配置文件
- 从机修改my.cnf