zoukankan      html  css  js  c++  java
  • MySQL高级丶

    文件

    • frm文件:存放表结构
    • myd文件:存放表数据
    • myi文件:存放表索引
    • 二进制日志文件log-bin
    • 错误日志log-error
    • 查询日志log

    逻辑架构

    分层可拔插(存储引擎也是可插拔的)

    1. 连接层
    2. 服务层
    3. 引擎层
    4. 存储层

    存储引擎

    show engines;
    show variables like '%storage_engine%';
    
    对比项 MyISAM InnoDB
    主外键 不支持 支持
    事务 不支持 支持
    行表锁 表锁,即使操作一行记录也会锁住整个表,不适合高并发的操作 行锁,操作时只锁某一行,不对其它行有影响,适合高并发的操作
    缓存 只缓存索引,不缓存绅士数据 不仅缓存索引,还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响
    表空间
    关注点 性能 事务
    默认安装 Y Y

    索引优化

    索引是高效获取数据的数据结构。排序&查询

    # 显示索引
    show index from tbl_emp;
    # 删除索引
    alter table  table_name drop index index_name
    # 删除主键索引
    alter table  table_name drop primary key
    #######################
    # 四种索引
    #######################
    # 1.普通索引
    alter table table_name add index index_username (username)
    # 2.唯一索引 (字段不允许重复,可有多个NULL)
    alter table  table_name add unique index_username (username)
    # 3.主键索引 (字段不允许重复,不允许NULL)
    alter table table_name add primary key  (username)
    # 4.全文索引
    alter table  table_name add fulltext index_username (username)
    

    索引分类:单值索引、唯一索引、复合索引

    索引结构:BTree索引、Hash索引、full-text索引、R-Tree索引

    需要建索引的情况:

    • 主键自动建立唯一索引
    • 频繁作为查询条件的字段应该创建索引
    • 查询中与其他表关联的字段,外键关系建立索引
    • 单键索引/组合索引的选择问题(高并发下倾向创建组合索引)
    • 查询中的排序字段,排序字段若通过索引去访问将大大提高排序速度(order by)
    • 查询中统计或者分组字段

    不要建索引的情况:

    • 表记录太少(三百万)
    • 经常增删改的表/频繁更新的字段不适合创建索引
    • where条件里用不到的字段不要创建索引
    • 数据重复且分布平均的字段(索引选择性越接近于1(比如性别字段的另一个极端),效率越高)

    性能分析

    • Mysql Query Optimizer查询优化器
    • MySQL常见瓶颈
      • CPU:CPU饱和一般发生在数据装入内存或从磁盘上读取数据的时候
      • IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
      • 服务器硬件性能瓶颈:top、free、iostat和vmstat查看系统的性能状态
    • Explain执行计划
      • 表的读取顺序(id)
      • 数据读取操作的操作类型(select_type)
      • 哪些索引可以使用(possible_keys)
      • 哪些索引被实际使用(key)
      • 表之间的引用(ref)
      • 每张表有多少行被优化器查询

    Explain执行计划字段解释

    • id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
      • id相同,(table)执行顺序由上到下
      • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
      • id同时存在相同与不同的情况,相同的部分,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
    • select_type:
      • SIMPLE:简单的select查询,查询中不包含子查询或者UNION
      • PRIMARY:查询中若包含任何复杂的子部分,最外层查询就被标记为PRIMARY
      • SUBQUERY:在select或where列表中包含了子查询
      • DERIVED:在from列表中包含的子查询标记为DERIVED(衍生),MySQL会递归查询这些子查询,把结果放在临时表里
      • UNION:若第二个select出现在union之后,则标记为UNION;若union包含在from子句的子查询中,外层select将被标记为DERIVED
      • UNION RESULT:从UNION表获取结果的select
    • table:显示这一行的数据是关于哪张表的
    • type:访问类型,最好到最差:system>const>eq_ref>ref>range>index>ALL
      • system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现
      • const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量
      • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
      • ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体
      • range:只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引,一般是在where语句中出现between、<、>、in等查询,这种范围性索引扫描比全表扫描要好。
      • index:遍历整个索引,比全表扫描要好点
      • ALL:最差,全表扫描,考虑建索引
    • possible_keys:显示可能应用在这张表中的索引,查询涉及到的字段上若存在索引,则将索引列出(不一定被查询实际使用)
    • key:实际使用的索引(查询中若使用了覆盖索引,则该索引仅出现在key列表中)
    • key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精度的情况下,长度越短越好。显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
    • ref:显示索引的哪一列被使用了,如果可能,是一个常数,哪些列或常量被用于查找索引列上的值。
    • rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的函数
    • Extra:剩下的执行情况就写在这个字段里边(十分重要的额外信息)
      • using filesort:文件排序,九死一生!mysql会对数据使用一个外部的索引排序,而不是按照表内的索引数据进行读取。MySQL中无法利用索引完成的排序操作称为”文件排序“
      • using temporary:新建了内部的临时表,十死无生。使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表,常见于order by和group by。(复合索引的个数和顺序要严格一致)
      • using index:好信号!表示相应的select操作中使用了覆盖索引,避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。覆盖索引:查询的列与所建的复合索引吻合。
      • using where
      • using join buffer
      • impossible where
      • select table optimized away
      • distinct

    索引分析案例

    Explain分析执行顺序

    image-20201129095802553

    索引单表优化案例

    CREATE TABLE IF NOT EXISTS `article`(
    `id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    `author_id` INT (10) UNSIGNED NOT NULL,
    `category_id` INT(10) UNSIGNED NOT NULL , 
    `views` INT(10) UNSIGNED NOT NULL , 
    `comments` INT(10) UNSIGNED NOT NULL,
    `title` VARBINARY(255) NOT NULL,
    `content` TEXT NOT NULL
    );
    
    insert into `article`(author_id,category_id,views,comments,title,content) values
    (1,1,1,1,'1','1'),
    (2,2,2,2,'2','2'),
    (1,1,3,3,'3','3');
    

    查询category_id为1且comments大于1的情况下views最多的autho_id

    image-20201129103236720

    image-20201129103322551

    索引两表优化案例

    CREATE TABLE IF NOT EXISTS `class`(
    `id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    `card` INT (10) UNSIGNED NOT NULL
    );
    CREATE TABLE IF NOT EXISTS `book`(
    `bookid` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    `card` INT (10) UNSIGNED NOT NULL
    );
    
    # 插入数据略(见文末)
    

    左连接,索引建在右表;右连接,索引建在左表;左右位置可以调换

    select * from book left join class on book.card=class.card
    explain select * from book left join class on book.card=class.card
    alter table `class` add index Y(`card`)
    drop index Y on `class`
    

    索引三表优化案例

    CREATE TABLE IF NOT EXISTS `phone`(
    `phoneid` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    `card` INT (10) UNSIGNED NOT NULL
    )ENGINE = INNODB;
    
    # 插入数据略(见文末)
    

    这种情况怎么处理

    explain select * from class left join book on class.card=book.card left join phone on class.card=phone.card;
    alter table `book` add index Y(`card`)
    alter table `phone` add index Z(`card`)
    

    结论:

    • 尽可能减少Join语句中NestedLoop的循环总次数(永远用小的结果集驱动大的结果集小表驱动大表)
    • 优先优化NestedLoop的内层循环
    • 保证join语句中被驱动表上join条件字段已经被索引
    • 当无法保证被驱动表的join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置

    索引失效

    CREATE TABLE staffs(
    id INT PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(24)NOT NULL DEFAULT'' COMMENT'姓名',
    `age` INT NOT NULL DEFAULT 0 COMMENT'年龄',
    `pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位',
    `add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
    )CHARSET utf8 COMMENT'员工记录表';
    
    insert into staffs(NAME,age,pos,add_time) values('z3',22,'manager',NOW());
    insert into staffs(NAME,age,pos,add_time) values('July',23,'dev',NOW());
    insert into staffs(NAME,age,pos,add_time) values('2000',23,'dev',NOW());
    
    alter table `staffs` add index idx_nameAgePos(`name`,age,pos);
    
    CREATE TABLE tbl_user(
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(20) DEFAULT NULL,
    `age`INT(11) DEFAULT NULL,
    `email` VARCHAR(20) DEFAULT NULL,
    PRIMARY KEY(`id`)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
    insert into tbl_user(NAME,age,email) values('1aa1',21,'b@163.com');
    insert into tbl_user(NAME,age,email) values('2aa2',222,'a@163.com');
    insert into tbl_user(NAME,age,email) values('3aa3',265,'c@163.com');
    insert into tbl_user(NAME,age,email) values('4aa4',21,'d@163.com');
    

    索引失效的情况

    1. 全值匹配我最爱:索引中的列和where中的条件刚好重合
    2. 最佳左前缀法则:根据复合索引,从左到右写where条件,最左边的不能少(带头大哥不能死,中间兄弟不能断)
    3. 不在索引列上做任何操作(计算,函数,自动或手动类型转换),会导致索引失效而转向全表扫描
    4. 不能使用索引中范围条件右边的列:and b>0用了范围条件了,b后面的c索引就失效了
    5. 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
    6. mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
    7. is null,is not null也无法使用索引
    8. like以通配符%开头,MySQL索引会失效而变成全表扫描(想解决用覆盖索引)
    9. 字符串不加单引号索引失效
    10. 少用or,用它来连接时索引会失效

    定值(常量)、范围(后会失效)、排序,一般order by是给个范围,group by基本都需要进行排序,会产生临时表

    一般性建议

    • 对于单键索引,尽量选择针对当前索引query过滤性更好的索引
    • 在选择组合索引时,当前query中过滤性最好的字段在索引字段顺序中,位置越靠前越好
    • 在选择组合索引时,尽量选择可能包含当前query中的where子句中更多字段的索引
    • 尽可能通过分析系统信息和调整query的写法来达到选择合适索引的目的

    慢SQL处理流程

    1. 慢查询的开启并捕获
    2. explain + 慢SQL分析
    3. Show Profile查询SQL在mysql服务器里面的执行细节和生命周期情况
    4. sql数据库服务器的参数调优

    查询截取分析

    查询优化

    1、永远小表驱动大表

    image-20201129204806302

    2、order by关键字优化(为排序使用索引)

    • 尽量使用index方式排序,避免使用filesort方式排序

    • filesort有两种算法:单路排序 和 双路排序

      • 双路排序:老版本的mysql,两次扫描磁盘
      • 单路排序:有风险
    • orderby时select*是大忌

    • 数据库服务器参数设置sort_buffer_size/max_length_for_sort_data

    image-20201129212715874

    3、group by关键字优化

    • 实质是先排序后进行分组,遵照索引建的最佳左前缀
    • 当无法使用索引列,增大sort_buffer_size/max_length_for_sort_data
    • where高于having,能写在where中的不要写在having中

    慢查询日志

    # 查看开启状况
    show variables like '%slow_query_log%';
    # 查看当前多少秒算慢sql
    show variables like '%long_query_time%';
    # 开启
    set global slow_query_log=1;
    # 设置慢的时间
    set global long_query_time=3;
    # 记录日志
    select sleep(4);
    # 另外mysqldumpslow工具
    

    批量数据脚本

    往表里插入一千万条数据

    # 函数及存储过程(见文末)
    
    show variables like '%log_bin_trust_function_creators%';
    set global log_bin_trust_function_creators=1;
    
    delimiter ;# 恢复设置
    select * from dept;
    call insert_dept(100,10);
    select * from emp;
    call insert_emp(100001,500000);
    

    Show Profile

    是mysql提供可以用来分析当前会话中语句执行的资源消耗情况,可以用于sql的调优测量

    # 默认是关闭,使用前需要开启
    show variables like 'profiling';
    set profiling=on;
    show profiles;
    show profile cpu,block io for query 3;
    # 出现以下4项必须优化
    # converting HEAP to MyISAM
    # Creating tmp table
    # Copying to tmp on disk
    # locked
    

    全局查询日志

    永远不要在生产环境开启此功能

    配置启用

    # 在MySQL的my.cnf中,设置如下
    # 开启
    general_log=1
    # 记录日志文件的路径
    general_log_file-/path/logfile
    # 输出格式
    log_output-FILE
    

    编码启用

    set global general_log=1;
    set global log_output='TABLE';
    select * from mysql.general_log;
    

    MySQL锁机制

    读锁(共享锁)/写锁(排它锁)/表锁(偏读)/行锁(偏写)/页锁

    • 表锁MyISAM
    # 简而言之:读锁会阻塞写,但是不会阻塞读;而写锁则会把读和写都阻塞。
    select * from mylock;
    lock table mylock read ,book write;
    show open tables;
    unlock tables;
    lock table mylock read;
    select * from mylock;
    # 当前session不能操作,其他session会阻塞等待,直到unlock tables;
    update mylock set name='a2' where id=1;
    select * from book;
    lock table mylock write;
    update mylock set name='a3' where id=1;
    show status like 'table%';# 反映锁竞争的情况
    
    • 行锁InnoDB
    # 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;(InnoDB与MyISAM最大的不同:一是支持事务,二是采用了行级锁)
    CREATE INDEX test_innodb_a_ind ON test_innodb_lock(a);
    CREATE INDEX test_innodb_lock_b_ind ON test_innodb_lock(b);
    SET autocommit=0;# 不自动提交事务,commit之前就会自动锁住操作的行
    select * from test_innodb_lock;
    update test_innodb_lock set b='4001' where a=4;
    COMMIT;
    
    • 无索引行锁升级为表锁(varchar不加单引号索引失效进而行锁升级为表锁)

    • 间隙锁危害(id不连续时,范围操作时,可能影响其他session执行)

    • 如何锁定一行

    SET autocommit=0;
    begin;
    select * from test_innodb_loc where a=8 for update;//锁定行
    commit;
    
    • 查看行锁情况
    show status like 'innodb_row_lock%';
    
    • 行锁相关优化建议
      • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
      • 合理涉及索引,尽可能缩小锁的范围
      • 尽可能使用较少检索条件,避免间隙锁
      • 尽量控制事务大小,减少锁定资源量和时间长度
      • 尽可能低级别事务隔离

    主从复制

    slave会从master读取binlog来进行数据同步

    1. master将改变记录到二进制日志(binary log),这些记录过程叫做二进制日志事件binary log events
    2. slave将master的binary log events拷贝到它的中继日志(relay log)
    3. slave重做中继日志中的事件,将改变应用到自己的数据库中,MySQL复制是异步的且串行化的

    配置文件进行配置主从复制(授权;show master/slave status;stop slave;)

    课程|SQL|

    击石乃有火,不击元无烟!!
  • 相关阅读:
    Exp9 Web安全基础
    EXP8 Web基础
    Exp7 网络欺诈防范
    Exp6 信息搜集与漏洞扫描
    Exp5 MSF基础应用
    Exp4 恶意代码分析
    Exp3 免杀原理与实践
    Exp2 后门原理与实践 20154317
    正则表达Regex替换标签
    正则表达式
  • 原文地址:https://www.cnblogs.com/rain2020/p/14059219.html
Copyright © 2011-2022 走看看