zoukankan      html  css  js  c++  java
  • MySQL(逻辑分层,存储引擎,sql优化,索引优化以及底层实现(B+Tree))

    一 , 逻辑分层

      

    连接层:连接与线程处理,这一层并不是MySQL独有,一般的基于C/S架构的都有类似组件,比如连接处理、授权认证、安全等。

    服务层:包括缓存查询、解析器、优化器,这一部分是MySQL核心功能,包括解析、优化SQL语句,查询缓存目录,内置函数(日期、时间、加密等函数)的实现。

    引擎层:负责数据存储,存储引擎的不同,存储方式、数据格式、提取方式等都不相同,这一部分也是很大影响数据存储与提取的性能的;对存储层的抽象。

    存储层:存储数据,文件系统。

    二 , 存储引擎

    查看数据库支持的存储引擎:show engines;

    如果要想查看数据库默认使用哪个引擎,可以通过使用命令: show variables like '%storage_engine%';

    指定数据库对象的引擎:
    create table tb(
    id int(4) auto_increment ,
    name varchar(5),
    dept varchar(5) ,
    primary key(id)
    )ENGINE=MyISAM AUTO_INCREMENT=1
    DEFAULT CHARSET=utf8 ;

    查看建表语句:show create table default_table;

    InnoDB,MyISAM的主要区别:

    InnoDB:在MySQL5.5开始作为默认的存储引擎,支持事务,行级锁,适合高并发场景,XA协议支持分布式事务。

    MyISAM:不支持事务,性能优先,表级锁,不适合高并发场景。

    三, sql优化

      3.1.1  mysql 内部实现索引原理(B+Tree)  参考

    https://blog.csdn.net/chuixue24/article/details/80027689

        3.1.1.1 ,  二叉树

          

         3.1.1.2 ,  B-Tree

          一棵m阶的B-Tree有如下特性: 
            1. 每个节点最多有m个孩子。 
            2. 除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。 
            3. 若根节点不是叶子节点,则至少有2个孩子 
            4. 所有叶子节点都在同一层,且不包含其它关键字信息 
            5. 每个非终端节点包含n个关键字信息(P0,P1,…Pn, k1,…kn) 
            6. 关键字的个数n满足:ceil(m/2)-1 <= n <= m-1 
            7. ki(i=1,…n)为关键字,且关键字升序排序。 
            8. Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)

              

            B-Tree中的每个节点根据实际情况可以包含大量的关键字信息和分支,如下图所示为一个3阶的B-Tree: 
            索引

    每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。

          模拟查找关键字29的过程:

        1. 根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】 
        2. 比较关键字29在区间(17,35),找到磁盘块1的指针P2。
        3. 根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
        4. 比较关键字29在区间(26,30),找到磁盘块3的指针P2。
        5. 根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】
        6. 在磁盘块8中的关键字列表中找到关键字29。

        分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。

          B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。

         3.1.1.3 ,  B+Tree(查询任意数据的次数是 n)

          B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。

    从上一节中的B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。

    B+Tree相对于B-Tree有几点不同:

    1. 非叶子节点只存储键值信息。
    2. 所有叶子节点之间都有一个链指针。
    3. 数据记录都存放在叶子节点中。

    将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示: 
    索引

    通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

    可能上面例子中只有22条数据记录,看不出B+Tree的优点,下面做一个推算:

    InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为〖10〗^3)。也就是说一个深度为3的B+Tree索引可以维护10^3 * 10^3 * 10^3 = 10亿 条记录。

    实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2~4层mysql的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。

    上面都应该知道B+Tree 了吧,所以我们在建立索引时,会生成一个B+Tree  如果我们在只查询索引字段时,sql 语句就直接去B+Tree 查,不会再去数据表中查了,这样提升性能是很重要的。 还有就是对于总是修改的字段不要对他建立索引,因为字段修改了,B+Tree 结构就要重构,这要是会降低性能的。

      3.1.1 索引分类:

          主键索引:是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候某一列定为主键时,就默认的创建了对应的主键索引。

          唯一索引:索引列的值必须唯一,但允许有空值。

          单列索引:对于表中的某一列,添加的索引。

          复合索引:多个列构成的索引(跟书的一级,二级,三级 目录一样),使用时要遵循最佳左前缀特性,如果我们创建了(area, age, salary)的复合索引,那么其实相当于创建了(area,age,salary)、(area,age)、(area)三个索引。

               因此我们在创建复合索引时应该将最常用作限制条件的列放在最左边,依次递减。

       3.2.2 如何正确的建立索引呢?

          3.2.2、创建索引      

             create 索引类型 索引名 on 表(字段)
             单值:  create index dept_index on tb(dept);
             唯一:create unique index name_index on tb(name) ;
             复合索引: create index dept_name_index on tb(dept,name);

            查询索引:
              show index from 表名 ;
              show index from 表名 G

            删除索引:
              drop index 索引名 on 表名 ;
              drop index name_index on tb ;

          3.2.2、SQL解析顺序
             接下来再走一步,让我们看看一条SQL语句的前世今生。
            首先看一下示例语句
                SELECT DISTINCT  .....   FROM .....  JOIN  .....  ON   .....  WHERE.....   GROUP BY    .....  HAVING  .....   ORDER BY   .....   LIMIT ..... 

                然而它的执行顺序是这样的

                FROM   .....  ON  .....    JOIN  .....   WHERE     ..... GROUP BY     .....  HAVING  .....    SELECT    DISTINCT   .....  ORDER BY   .....  LIMIT   .....

     
          3.2.3 如何建立索引
              

          一般说来,索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况.

          3.2.4     使用索引时,有一些技巧:

              1.索引不会包含有NULL的列

               只要列中包含有NULL值,都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此符合索引就是无效的。

             2. 索引要建立在经常进行select操作的字段上。而经常修改的字段,没没必要建立索引了,因为,你建立了索引会生成一个B+树,你修改了该索引的字段后,这个B+树就需要修改,反而对性能不是很好。

             3. 复合索引 : 复合索引,不要跨列或无序使用(最佳左前缀)

             4.like语句操作: 一般情况下不鼓励使用like操作,如果非使用不可,注意正确的使用方式。like ‘%aaa%’不会使用索引,而like ‘aaa%’可以使用索引。

                5. 不要在索引上进行任何操作(计算、函数、类型转换),否则索引失效

                6.不使用NOT IN 、<>、!=操作,但<,<=,=,>,>=,BETWEEN,IN是可以用到索引的

                7.索引要建立在经常进行select操作的字段上。

                   这是因为,如果这些列很少用到,那么有无索引并不能明显改变查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。

                8.索引要建立在值比较唯一的字段上。

                9.对于那些定义为text、image和bit数据类型的列不应该增加索引。因为这些列的数据量要么相当大,要么取值很少。

                10.在join操作中(需要从多个数据表提取数据时),mysql只有在主键和外键的数据类型相同时才能使用索引,否则及时建立了索引也不会使用。

     三, sql性能问题

        a.分析SQL的执行计划  : explain   ,可以模拟SQL优化器执行SQL语句,从而让开发人员 知道自己编写的SQL状况

        b.MySQL查询优化其会干扰我们的优化(mysql服务层有一个sql优化器),可以对我们写的sql进行优化,这是我们控制不了的。

        查询执行计划:  explain +SQL语句     explain SELECT * from book ;

          

        

    id : 编号
    select_type :查询类型
    table :表
    type :索引类型 system>const>eq_ref>ref>range>index>all ,要对type进行优化的前提:有索引 一般能达到range 就行。
    possible_keys :预测用到的索引
    key :实际使用的索引
    key_len :实际使用索引的长度
    ref :表之间的引用
    rows :通过索引查询到的数据量
    Extra :额外的信息 下面是他可能发出的情况

      i). using filesort : 性能消耗大;需要“额外”的一次排序(查询)  。常见于 order by 语句中。 解决:where哪些字段,就order by那些字段2

      ii). using temporary:性能损耗大 ,用到了临时表。一般出现在group by 语句中。 解决: 避免:查询那些列,就根据那些列 group by .

      iii). using index :性能提升; 索引覆盖(覆盖索引)。原因:不读取原文件,只从索引文件中获取数据 (不需要回表查询)
    只要使用到的列 全部都在索引中,就是索引覆盖using index

      iii).using where (需要回表查询)。

        假设age是索引列
        但查询语句select age,name from ...where age =...,此语句中必须回原表查Name,因此会显示using where.  解决 吧name  也添加到索引中去。

  • 相关阅读:
    Odoo安装教程2-创建新的插件模块第一讲
    Odoo安装教程1-创建第一个 Odoo 应用
    Odoo开发教程21-Odoo服务器端开发者模式
    Ubuntu 安装LAMP
    Roundcube Webmail信息泄露漏洞(CVE-2015-5383)
    Roundcube Webmail跨站脚本漏洞(CVE-2015-5381 )
    Roundcube Webmail File Disclosure Vulnerability(CVE-2017-16651)
    Roundcube 1.2.2
    XAMPP重置MySQL密码
    python importlib动态导入模块
  • 原文地址:https://www.cnblogs.com/xiaowangbangzhu/p/10435895.html
Copyright © 2011-2022 走看看