查询优化器 :MySQL会对咱们的SQL语句进行优化;html
存储引擎mysql
查询数据库支持的存储引擎 show engines;
SQL优化sql
索引数据库
B-Tree (平衡多路查找树):能够显著减小定位记录时所经历的中间过程,从而加快存取速度服务器
一个M阶的B-Tree具备的特性 > 每一个节点最多有m个孩子 > 根节点的孩子数>=2(前提:树的高度大于一) > 除了根节点和叶子结点,其余节点的孩子数(ceil(m/2));向上取整 > 全部的叶子结点都在一层 > 各个结点包含n个关键字信息:(P0,K1,P1,K2,P2......Kn,Pn) 其中 - Ki(i=1,2......n)为关键字,且K(i-1)<Ki,即从小到大排序 - 关键字的个数n必须知足:[ceil(m/2)-1,m-1] - Pi指向子树,且指针P(i-1)所指向的子树结点中全部关键字均小于Ki。即:父结点中任何关键字的左孩子都小于它,右孩子大于它
B-Tree示例图:数据结构
B+Tree :B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构架构
B+Tree相对于B-Tree有几点不一样: 非叶子节点只存储键值信息; 全部叶子节点之间都有一个链指针; 数据记录都存放在叶子节点中;
B+Tree示例图:并发
单列;一个表能够有多个单值索引分布式
建立索引高并发
-- 给users表的name属性建立索引 create index name_index on users(name) ; alter table users add index name_index(name); -- 查询索引 show index from users(表名); -- 删除索引 drop index name_index(索引名) on users(表名);
单列;一个表只有一个惟一索引;主键默认惟一索引
-- 给users表的id属性建立索引 create unique index id_index on users(id) ; alter table users add unique index id_index(id);
多列;
-- 给users表的birthday,name属性建立索引 -- 先找birthday,若是birthday相同则再去name找 create index birthday_name_index on users(birthday,name) ; alter table users add index birthday_name_index(birthday,name);
主键索引不能为null,单值索引能够为null;
聚簇索引:主索引叶子结点指向数据,辅助索引叶子结点指向对应数据的主索引;
非聚簇索引:索引表和数据表分开,主索引和辅助索引的叶子结点都指向数据;
优点:以快速检索,减小I/O次数,加快检索速度;根据索引分组和排序,能够加快分组和排序;
劣势:索引自己也是表,所以会占用存储空间。索引的维护和建立须要时间成本,这个成本随着数据量增大而增大;构建索引会下降数据表的修改操做(删除,添加,修改)的效率,由于在修改数据表的同时还须要修改索引表。
使用关键字explain ,分析分析SQL执行计划
mysql> explain select * from user; +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 3 | | +----+-------------+-------+------+---------------+------+---------+------+------+-------+
-- 建表准备数据 CREATE TABLE course( cid INT(2), cname VARCHAR(10), tid INT(2) ); CREATE TABLE teacher( tid INT(2), tname VARCHAR(10), tcid INT(2) ); CREATE TABLE teacher_card( tcid INT(2)teacher_card, tcdesc VARCHAR(10) );
-- 查询课程编号为2 或 教师证为3的老师 mysql> explain SELECT * -> FROM teacher t,course c, teacher_card tc -> WHERE t.tid=c.tid AND t.tcid= tc.tcid AND (c.cid=2 OR tc.tcid=3); +----+-------------+-------+------ | id | select_type | table | ... | 1 | SIMPLE | t | ... | 1 | SIMPLE | tc | ... | 1 | SIMPLE | c | ... -- 在链接查询中,id值相同 -- t - tc - c 已知记录数顺序 3- 3- 4;==>执行顺序:记录数少的先执行; -- 1- 查询产生的笛卡尔积 -- > 3 * 3 = 9 ;9 * 4 = 36 -- 2- 若是记录数多的先执行 -- > 4 * 3 = 12 ; 12 * 3= 36 -- 虽然最后的笛卡尔积数量不变,可是中间产生笛卡尔积数量不同,第一种中间记录数少,占用的资源少; -- 查询教英语的老师描述 mysql> EXPLAIN SELECT tc.tcdesc -> FROM teacher_card tc -> WHERE tc.tcid = (SELECT t.tcid FROM teacher t -> WHERE t.tid = (SELECT c.tid FROM course c WHERE c.cname = '英语')); +----+-------------+-------+------+ | id | select_type | table | ... | 1 | PRIMARY | tc | ... | 2 | SUBQUERY | t | ... | 3 | SUBQUERY | c | ... -- 在子查询中,id有 3-2-1 执行顺序 c-t-tc -- 越内层的子查询,越先执行
id : id值相同 ,从上往下顺序执行 ;id值不一样,从大到小顺序执行;
select_type
type
SYSTEM>CONST> EQ_REF> REF>RANGE>IANDEX> ALL(要对type优化,前提是有索引)
SYSTEM:只有一条数据的系统表、主查询的衍生表只有一条数据;(查询结果只有一条)
-- 例如,这条SQL的主查询知足条件为system类型 mysql>ALTER TABLE teacher ADD INDEX tid_index(tid); mysql> EXPLAIN SELECT * -> FROM (SELECT * FROM teacher t WHERE t.tid = 1) t_derived; +----+-------------+------------+--------+---------------+-----------+---------+------+-- | id | select_type | table | type | possible_keys | key | key_len | ref | +----+-------------+------------+--------+---------------+-----------+---------+------+--- | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | | 2 | DERIVED | t | ref | tid_index | tid_index | 5 | | +----+-------------+------------+--------+---------------+-----------+---------+------+--
CONST:在主键索引或惟一索引的状况下,只查询一条数据;(查询结果只有一条)
mysql> ALTER TABLE teacher ADD PRIMARY KEY tid_primary(tid); -- 主键索引是tid,type是const mysql> EXPLAIN SELECT * FROM teacher t WHERE t.tid = 1; +----+-------------+-------+-------+-------------------+---------+---------+-------+------ | id | select_type | table | type | possible_keys | key | key_len | ref | rows +----+-------------+-------+-------+-------------------+---------+---------+-------+------ | 1 | SIMPLE | t | const | PRIMARY,tid_index | PRIMARY | 4 | const | 1 +----+-------------+-------+-------+-------------------+---------+---------+-------+----- -- 查询tcid ,type 是 all mysql> EXPLAIN SELECT * FROM teacher t WHERE t.tcid = 1; +----+-------------+-------+------+---------------+------+---------+------+------+------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra +----+-------------+-------+------+---------------+------+---------+------+------+------- | 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 3 | Using +----+-------------+-------+------+---------------+------+---------+------+------+-------
EQ_REF:惟一性索引; 对于每一个索引键的查询,必须返回有且只有一条数据; (查询结果多条)
-- 将列改成惟一性索引 mysql> ALTER TABLE teacher ADD UNIQUE INDEX tcid_index(tcid); mysql> ALTER TABLE teacher_card ADD PRIMARY KEY tcid_primary(tcid); -- 此时teacher和teacher_card的数据条数为3 -- 查询索引键,若两边数据条数一致(必须是惟一性索引) mysql> EXPLAIN SELECT t.tcid FROM teacher t,teacher_card tc WHERE t.tcid = tc.tcid; +----+-------------+-------+--------+---------------+------------+---------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref +----+-------------+-------+--------+---------------+------------+---------+------------- | 1 | SIMPLE | t | index | tcid_index | tcid_index | 5 | NULL | 1 | SIMPLE | tc | eq_ref | PRIMARY | PRIMARY | 4 | mydb3.t.tcid +----+-------------+-------+--------+---------------+------------+---------+-------------
REF:非惟一性索引; 对于每一个索引键的查询,返回n条数据(能够n=0);(查询结果多条)
-- 给teacher添加一条数据 -- 查询索引键,若两边数据条数不一致(能够为非惟一性索引) mysql> EXPLAIN SELECT t.tcid FROM teacher t,teacher_card tc WHERE t.tcid = tc.tcid; +----+-------------+-------+--------+---------------+------------+---------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref +----+-------------+-------+--------+---------------+------------+---------+------------- | 1 | SIMPLE | t | index | tcid_index | tcid_index | 5 | NULL | 1 | SIMPLE | tc | ref | PRIMARY | PRIMARY | 4 | mydb3.t.tcid +----+-------------+-------+--------+---------------+------------+---------+-------------
RANGE:检索指定范围的行的索引键 (关键字in有时候会失效);(查询结果多条)
mysql> EXPLAIN SELECT t.tcid FROM teacher t WHERE t.tcid > 1; +----+-------------+-------+-------+---------------+------------+---------+------+------+- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | +----+-------------+-------+-------+---------------+------------+---------+------+------+- | 1 | SIMPLE | t | range | tcid_index | tcid_index | 5 | NULL | 3 | +----+-------------+-------+-------+---------------+------------+---------+------+------+-
INDEX:查询数据的索引键;
ALL:查询全部数据;
possible_keys:预测用到的索引(实际可能发生索引失效)
key:实际用到的索引
key_len :索引的计算长度
# char和varchar类型key_len计算公式 varchr(N)变长字段且容许NULL =\ N * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段) varchr(N)变长字段且不容许NULL = \ N * ( character set:utf8=3,gbk=2,latin1=1)+2(变长字段) char(N)固定字段且容许NULL = N * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL) char(N)固定字段且容许NULL = N * ( character set:utf8=3,gbk=2,latin1=1) # 与数据类型长度无关 # 数值数据的key_len计算公式: TINYINT容许NULL = 1 + 1(NULL) TINYINT不容许NULL = 1 SMALLINT容许为NULL = 2+1(NULL) SMALLINT不容许为NULL = 2 INT容许为NULL = 4+1(NULL) INT不容许为NULL = 4 # 日期时间型的key_len计算:(针对mysql5.5) DATETIME容许为NULL = 8 + 1(NULL) DATETIME不容许为NULL = 8 TIMESTAMP容许为NULL = 4 + 1(NULL) TIMESTAMP不容许为NULL = 4
ref:指明当前表参照的字段(null,const常量,参照的字段)
-- tc 表参照了mydb3.t.tcid 字段 mysql> EXPLAIN SELECT t.tcid FROM teacher t,teacher_card tc WHERE t.tcid = tc.tcid; +----+-------------+-------+--------+---------------+------------+---------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref +----+-------------+-------+--------+---------------+------------+---------+------------- | 1 | SIMPLE | t | index | tcid_index | tcid_index | 5 | NULL | 1 | SIMPLE | tc | ref | PRIMARY | PRIMARY | 4 | mydb3.t.tcid +----+-------------+-------+--------+---------------+------------+---------+-------------
rows:实际查询的记录数
mysql> EXPLAIN SELECT t.tcid FROM teacher_card tc,teacher t WHERE t.tcid = tc.tcid; +----+-------------+-------+--------+---------------+------------+---------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref +----+-------------+-------+--------+---------------+------------+---------+------------- | 1 | SIMPLE | t | index | tcid_index | tcid_index | 5 | NULL | 1 | SIMPLE | tc | ref | PRIMARY | PRIMARY | 4 | mydb3.t.tcid +----+-------------+-------+--------+---------------+------------+---------+------------- +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 3 | Using index | | 1 | Using where; Using index | +------+--------------------------+ mysql> SELECT t.tcid FROM teacher_card tc,teacher t WHERE t.tcid = tc.tcid; +------+ | tcid | +------+ | 1 | | 2 | | 3 | +------+
extra
Using filesort:没法利用索引来完成的排序
-- 单列索引 CREATE TABLE a( a INT, b INT, c INT, INDEX a_index(a), INDEX b_index(b), INDEX c_index(c) ); -- 查询的 a 可是额外用b排了序,若是用a排序则不会出现Using filesort mysql> EXPLAIN SELECT * FROM a WHERE a='' ORDER BY b; +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 1 | Using where; Using filesort | +------+--------------------------+ -- 复合索引 不能跨列 要知足最佳左前缀,不然出现Using filesort DROP INDEX a_index ON a; DROP INDEX b_index ON a; DROP INDEX c_index ON a; ALTER TABLE a ADD INDEX a_b_c_index(a,b,c); -- 知足最佳左前缀,没有跨列 mysql> EXPLAIN SELECT * FROM a WHERE a='' ORDER BY b; +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 1 | Using where; Using index | +------+--------------------------+ -- 跨列 mysql> EXPLAIN SELECT * FROM a WHERE a='' ORDER BY c; +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 1 | Using where; Using index; Using filesort | +------+--------------------------+ -- 不知足最佳左前缀 mysql>EXPLAIN SELECT * FROM a WHERE b='' ORDER BY c; +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 1 | Using where; Using index; Using filesort | +------+--------------------------+
(补充: 回表查询:指的是在索引中没有要查询的数据,须要回到表中查询)
-- 单表优化 -- 建立表 CREATE TABLE book( bid INT NOT NULL PRIMARY KEY, NAME VARCHAR(20), authorid INT NOT NULL, publicid INT NOT NULL, typeid INT NOT NULL ); -- 查询authorID=1,typeid=2或3的 bid mysql> EXPLAIN SELECT bid FROM book WHERE typeid IN (2,3) AND authorid = 1 ORDER BY typeid DESC; +----+-------------+-------+------+---------------+------+---------+------+------+----------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | | +----+-------------+-------+------+---------------+------+---------+------+------+----------- | 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 5 | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+ Extra ------------------+ Using where; Using filesort ------------------+ -- 优化 1 添加索引,(消除了Using filesort,提升了type等级index) ALTER TABLE book ADD INDEX b_t_a_index(bid,typeid,authorid); mysql> EXPLAIN SELECT bid FROM book WHERE typeid IN (2,3) AND authorid = 1 ORDER BY typeid DESC; +----+-------------+-------+------+---------------+------+---------+------+------+----------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | | +----+-------------+-------+------+---------------+------+---------+------+------+----------- | 1 | SIMPLE | book | index | NULL | NULL | 12 | NULL | 5 | +----+-------------+-------+------+---------------+------+---------+------+------+----------- ------------------+ Extra ------------------+ Using where; Using index;Using filesort ------------------+ -- 优化2 -- 修改索引顺序(提升了type等级 ref) -- 含IN 的查询放到后面,避免可能的失效 ALTER TABLE book ADD INDEX atb_index(authorid,typeid,bid); mysql> EXPLAIN SELECT bid FROM book WHERE authorid = 1 AND typeid IN (2,3) ORDER BY typeid DESC; +----+-------------+-------+------+---------------+-----------+---------+-------+------+----- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | +----+-------------+-------+------+---------------+-----------+---------+-------+------+----- | 1 | SIMPLE | book | ref | atb_index | atb_index | 4 | const | 2 | +----+-------------+-------+------+---------------+-----------+---------+-------+------+--------------------------+ Extra | ---------------------+ Using where; Using index | ---------------------+
EXPLAIN SELECT * FROM teacher t , course c WHERE t.tid = c.tid AND c.cname = '语文'; -- 或者 EXPLAIN SELECT * FROM teacher t LEFT JOIN course c ON t.tid = c.tid WHERE c.cname = '语文'; +----+-------------+-------+------+---------------+------+---------+------+------+----------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | +----+-------------+-------+------+---------------+------+---------+------+------+----------- | 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 3 | | 1 | SIMPLE | c | ALL | NULL | NULL | NULL | NULL | 4 | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ Extra --------------------+ Using where; Using join buffer | --------------------+ -- 优化 1 添加索引 -- 给哪张表添加索引 --> 知足原则"小表驱动大表"; -- --> 索引常用,不常改变
IN和EXIST的使用:
order by 的使用
Using filesort 分为双路排序[1扫描排序字段,2扫描其它字段]、单路排序[扫描全度字段](根据磁盘IO次数)
能够经过切换排序方法和增大排序使用的buffer
大小
保证排序的一致性
避免使用SELECT *
记录mysql中超过阀值(long query time)默认10秒;
-- 查看慢查询日志是否开启 show variables like "%slow_query_log%"; -- 临时开启 set globle slow_query_log = 1; -- 永久开启 -- 修改mysql的配置文件 -- slow_query_log = 1 -- slow_query_log_file = /var/mysql/slow.log 制定一个路径寻访日志 -- 查看阀值大小 show variables like "%long_query_time%"; -- 临时设置大小(重启生效) set globle long_query_time = 时长(s); -- 永久设置大小(重启生效) -- 修改mysql的配置文件 -- long_query_time = 时长 -- 查询超过阀值的SQL show globle status like "%slow_queris%"; -- 数量 -- mysql的工具,查看慢日志记录 mysqldumpslow
分析执行SQL的在各环节的执行时间
-- 查看执行SQL的时间和query_id show profiles -- show variables like "%profiling%"; -- 开启profiling属性 set profiling = on; -- 分析执行SQL的在各环节的执行时间 show profile all for query 1; -- 1是查出来的query_id
能够记录全部执行的SQL
-- 查看是否开启全局日志 show variables like "%general_log%"; -- 开启全局日志,记录到mysql.general_log表中 set globle general_log = 1; set globle log_output = 'table';
解决资源共享形成的并发问题 参考[mydddfly]博客;
开销 | 加锁速度 | 死锁 | 粒度 | 并发性能 | |
---|---|---|---|---|---|
行级锁 | 小 | 快 | 不会死锁 | 大(锁冲突几率高) | 低 |
表级锁 | 大 | 慢 | 会出现死锁 | 小(锁冲突几率低) | 高 |
页面锁 | 中 | 中 | 会出现死锁 | 中 | 中 |
MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)
MyISAM在执行查询语句(SELECT)前,会自动给涉及的全部表加读锁,在执行更新操做(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁;
-- 经过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺状况; -- 值越大争夺越严重 mysql> show status like 'table%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Table_locks_immediate | 46 | | Table_locks_waited | 0 | +-----------------------+-------+
如何给表加锁
-- lock table 表名 read/write 给表加上读/写锁 mysql> lock table teacher read; -- 查看表是否使用锁 mysql> show open tables; +--------------------+----------------------------------------------+--------+-------------+ | Database | Table | In_use | Name_locked | +--------------------+----------------------------------------------+--------+-------------+ | mysql | time_zone_transition_type | 0 | 0 | | mydb3 | teacher | 1 | 0 | +--------------------+----------------------------------------------+--------+-------------+
(为了容许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁)
-- 能够经过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺状况: show status like 'innodb_row_lock%'; Show innodb status; -- 共享锁 select * from teacher where tid = 1 lock in share mode; -- 排它锁 select * from teacher where tid = 1 for update;
MySQL之间数据复制的基础是二进制日志文件(binary log file)。一台MySQL数据库一旦启用二进制日志后,其做为master,它的数据库中全部操做都会以“事件”的方式记录在二进制日志中,其余数据库做为slave经过一个I/O线程与主服务器保持通讯,并监控master的二进制日志文件的变化,若是发现master二进制日志文件发生变化,则会把变化复制到本身的中继日志中,而后slave的一个SQL线程会把相关的“事件”执行到本身的数据库中,以此实现从数据库和主数据库的一致性,也就实现了主从复制;
主服务器配置
-- 修改配置文件 [mysqld] log-bin = mysql-bin server-id = 1 -- 建立用户并受权 mysql> CREATE USER 'flynn'@'localhost' IDENTIFIED BY '123456';#建立用户 mysql> GRANT REPLICATION SLAVE ON *.* TO 'flynn'@'localhost';#分配权限 mysql>flush privileges; #刷新权限 -- 查看master状态,记录二进制文件名和位置: show master status;
从服务器配置、
-- 配置文件 server-id = 2 -- 重启mysql,打开mysql会话,执行同步SQL语句 mysql> CHANGE MASTER TO -> MASTER_HOST='', -> MASTER_USER='', -> MASTER_PASSWORD='', -> MASTER_LOG_FILE='二进制文件名', -> MASTER_LOG_POS='位置'; -- 启动slave 同步 mysql> start slave; -- 查看状态 mysql> show slave status
对主服务器配置约束
[mysqld] # 不一样步哪些数据库 binlog-ignore-db = '' # 只同步哪些数据库,除此以外,其余不一样步 binlog-do-db = ''
自增的主键是连续的在插入过程当中尽可能减小页分裂,即便进行页分裂,也只会分裂不多的一部分。而且能减小数据的移动,每次插入都是插入到最后。总之就是减小分裂和移动的频率;
InNoDB使用的是聚簇索引,使用辅助索引要查询两次才能拿到数据;而主键索引只须要查一次;
B-Tree的非叶子结点也会存储数据,这样致使了非叶子结点上存储的指针变少,数据量大时只能增长树的高度(即增长了IO访问的次数);