读书笔记:MySql性能调优与架构设计2019.3.11-至今

数据库优化:
      需求和架构及业务实现优化:55%
      Query语句的优化:30%
      数据库自身的优化:15%
 mysql锁定机制:锁机制的实现直接影响数据库的处理并发能力和性能,并且是否保证数据的一致性而各种共享资源在被并发访问变得有序所设计的一种规则
      mysql:各引擎使用三种类似(级别)的锁定机制:行级锁、页级锁和表级锁

      行级锁定:锁定的对象颗粒度很小,所以发生锁定资源争用概率也小,能够给与应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能
      缺点:每次获取锁和释放锁需要消耗资源,也是最容易发送死锁

      表级锁:最大颗粒度的锁,获取锁和释放锁的速度很快,可以很好避免困扰我们的死锁问题
      缺点:锁定资源争用概率也会高,并发能力减弱

      页级锁:颗粒度介于行级锁和表级锁之间,和行级锁一样,会发生死锁,资源开销也在两者之间
      
      innodb使用行级锁,myisam使用表锁
      MyISAM 存储引擎只支持表锁,MySQL 的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。

        对于读操作,可以增加读锁,一旦数据表被加上读锁,其他请求可以对该表再次增加读锁,但是不能增加写锁。(当一个请求在读数据时,其他请求也可以读,但是不能写,因为一旦另外一个线程写了数据,就会导致当前线程读取到的数据不是最新的了。这就是不可重复读现象)

        对于写操作,可以增加写锁,一旦数据表被加上写锁,其他请求无法在对该表增加读锁和写锁。(当一个请求在写数据时,其他请求不能执行任何操作,因为在当前事务提交之前,其他的请求无法看到本次修改的内容。这有可能产生脏读、不可重复读和幻读)

        读锁和写锁都是阻塞锁。

        如果t1对数据表增加了写锁,这是t2请求对数据表增加写锁,这时候t2并不会直接返回,而是会一直处于阻塞状态,直到t1释放了对表的锁,这时t2便有可能加锁成功,获取到结果。      
     读锁定:当一个新的客户端申请读锁定资源的时候,需要满足两个条件
        1>请求锁定的资源当前没有被写锁定
        2>写锁定等待队列中没有更高优先级的写锁定等待
     写锁定:首先检查是否有锁定相同资源的信息存在
        
       行锁定:
        innodb的行级锁分为:共享锁和排它锁,而在锁定机制的实现过程中为了让行级锁订和表级锁定共存,innodb同样适用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排它锁这两种

          当一个事务需要给自己需要资源加共享锁,如果正在被共享锁锁定着,自己可以再加一个,不过不能加排它锁
          如果需要锁定的资源被一个排它锁占有后,则只能等待该锁定释放资源之后自己才能获取资源并添加自己的锁。
          而意向锁作用是当一个事务在需要获取资源锁定的时候,如果遇到自己需要的资源已经被排他锁占用的时候,该事务可以需要锁定行的表上面添加一个合适的意向锁(共享/排他)。
          意向共享锁可以并存多个,但是意向排它锁只能有一个存在

  mysql和oracle锁的区别:
    oracle是通过需要锁定某行记录所在物理block上事务槽上表级锁定信息,而innodb的锁定时通过指向数据记录的某一索引键之前和最后一个索引键之后的空域空间上标记锁定信息而实现的,被称为间隙锁,因为query执行过程通过范围查找,会锁定整个范围内所有的索引键值,即使这个键值并不存在
    间隙锁的缺点:当锁定一个范围键值后,即使某些不存在的键值也会被锁定,无法造成锁定时候插入任何数据。在某些场景下会对性能造成影响。innodb给出的解释是阻止幻读的出现。
    除了间隙锁,通过索引实现锁定的方式还存在其他几个较大的隐患:
      1.当query无法利用索引的时候,innodb会放弃使用行锁而改用表锁,造成并发性能降低
      2.当query使用索引并不包含所有过滤条件时候,数据检索使用到的索引建指向的数据可能有部分并不属于该query结果集的行列,但是也会被锁定,因为间隙锁是锁定范围
      3.当query在使用索引定位数据时候,如果使用索引建一样但访问数据行不同时候(索引只是过滤条件一部分),一样被锁


    innodb检测到系统产生死锁之后会通过判断产生死锁两个事务中较小的事务回滚,而让另一个较大事务成功完成
    判断依据:计算两个事务各自插入、更新、或者删除的数据量判断两个事务大小,如果产生死锁涉及不止是innodb时候,无法检测该死锁,这时候只能通过锁定超时限制解决该死锁
    MyISAM表锁优化:
      主要考虑如何提高并发
      1.缩短锁定时间
        尽量简化复杂Query,建立足够高效索引,数据检索更迅速,只存放必要的信息,控制字段类型,降低并发数(减少对MySQL的访问),某些高并发的场景可以通过应用进行排队队列机制.
        调整读写优先级,根据实际需求确保重要操作更有限执行。
        对于静态(更改不频繁)的数据库数据,充分利用QueryCache或者memcached缓存服务极大可能的提高访问效率.

    Innodb行锁优化:
       a>尽可能让数据检索通过索引完成,避免innodb因为无法通过索引键加锁而升级为表级锁
       b>合理设计索引,加锁尽可能准确,尽可能缩小锁定范围,避免造成不必要的锁定而影响其他Query执行
       c>控制事务大小,减少锁定资源量和锁定时间长度
       d>尽量使用较低级别的事务隔离
       e>减少基于范围的数据检索过滤条件,避免因为间隙锁带来锁定了不该锁定的记录
     减少死锁的建议:
       a>在同一个事务中,尽可能做到一次锁定所需要所有资源,防止产生死锁
       b>对于非常容易死锁的部分,可以升级锁颗粒度

    系统锁定争用情况查询:
       实现表锁定争用状态:show status like 'table%'     _immediate:产生表级锁定次数  _waited:出现表级争用锁定而等待次数

       查看行级锁: show status like 'innodb_row_lock%' 

 数据库的Query的优化
      1.只取出自己需要的列数据。 
        因为:首先返回数据都是需要通过网络传输的,如果数据量大事对网络带宽的一个浪费,还有是如果需要排序的Query,mysql的排序算法是一次性将所需要的Columns全部取出,在排序区进行排序后直接将数据返回给请求客户端,如果把不需要的Conlumns也取出来,会极大的浪费排序过程需要的内存
      2.仅仅使用最有效的过滤条件。
        因为:索引键增长,索引占用的空间会更大,代表我们访问该索引所需要读取的数据量会更多
      3.尽量避免复杂的join和子查询 
         因为Query语句设计到的表越多,所需要锁定的资源就越多,所阻塞的其他线程也就越多,所以一般将比较复杂的Query拆分为简单的Query语句分步执行,每次锁定的资源也就会少很多,所阻塞其他线程也会少一些
         如果拆分语句网络交互就变多,总体消耗变大,查询时间不久反而更长了?
           这种情况可能存在,可以分析一下,一个复杂的join Query语句在执行的时候,所需要锁定的资源比较多,可能被别人阻塞的概率也就越大,如果是简单的,这些时间就小,所以较为复杂的Query可能在执行之前被阻塞而浪费更多的时间。而且我们所服务的不只是淡淡一个Query请求,还有很多,在并发系统牺牲单个Query的短暂响应时间而提高整体处理能力也是非常值得。
             优化本身就是一个平衡与取舍的艺术。

 4.尽可能让数据检索通过索引完成,避免innodb因为无法通过索引键加锁而升级为表级锁

     首先Explain入手看执行计划
      profiling:定位一条Query的性能瓶颈,判断是消耗CPU计算太多,还是需要的IO操作太多
         1>通过set profiling =1; 开启关闭profiler功能
         2>执行Query之后,获取系统中保存Query的profile   showprofiles;
         3>通过Query_ID获取某个Query详细profile信息;  show profile cpu,block io for query ID;


    合理设计并利用索引
      四种类型的索引:B-Tree、Hash、Fulltext、R-Tree

       B-Tree:使用最频繁的索引类型,除了Archive之外都支持,B-Tree索引的物理文件大多是以B Tree树结构存储的,所有实际需要的数据都存放在Tree的Leaf Node,而且到了任何一个Node的最短路径长度都完全相同。
        Innodb存储实际上是B+Tree,在每一个Leaf Node上面存放索引键相关信息外还存储了与该Leaf Node相邻的后一个Leaf Node指针信息,这主要是加快检索相邻Leaf Node的效率考虑
        Innodb存储引擎有两种不同形式的索引,一种是主键索引,另一种是和其他存储引擎存放的普通B-Tree索引,在主键索引,Nodes存放是表的实际数据,不仅仅包括主键字段的数据,还包括其他字段数据,整个数据以主键值有序的排序,而普通的B-Tree只是在Nodes放了索引建相关信息,还放了innodb的主键值
        所以:在innodb如果通过主键来访问数据效率非常高,如果其他索引来说先是查找索引相关信息,再通过主键值通过主键索引获取响应的数据行
        MyISAM的主键索引和非主键索引差别很小,只不过主键索引是一个唯一且非空的键。与innode的B-Tree存储结构也基本相同,主要区别是MyISAM在Nodes上面除了存放索引信息外,再存放能直接定位到MyISAM数据中相应数据行的信息,但是不会存放主键的键值信息

       Hash:检索效率非常高,可以一次定位,不需要像B-Tree需要需要从根节点再到枝节点最后才能访问到叶节点这样多次IO访问,效率远高于B-Tree
         缺点:仅仅满足'=','IN'和'<=>'查询,不能使用范围查询
               无法利用来排序操作
               不能利用部分索引建查询
               Hsh索引遇到大量Hsh值相等情况性能不一定比B-Tree索引高
       
       Full-text:全文检索,目前仅在MyISAM存储引擎支持,仅在CHAR.VARCHAR和TEXT这三种数据类型的列可以建Full-text索引,一般来说代替效率低LIKE'%--%'操作,但是对中文支持不太好,而且创建消耗资源比较大

       R-Tree:主要是解决空间数据检索的问题
      
      索引的好处:能够提高数据检索效率,降低数据库的IO成本,还有一个重要的用途,降低数据的排序成本,每个索引中索引数据都是按照索引键值进行排序存放的,Query语句包含排序可以直接取数据不需要排序,极大的降低CPU资源的消耗

      索引的弊端:如果频繁建立索引,在更新表中信息所带来明显的资源消耗就是增加了更新所带来的IO和调整索引所致的计算量,索引还会带来存储空间资源消耗的增长

      什么情况下创建索引:
       1>较频繁的作为查询条件字段应该创建索引
       2>唯一性太差的不适合单独创建所以,即使频繁作为查询条件,因为即使创建索引,Mysql优化器大多数不会选择使用,即使使用了,由于索引字段中每个值都含有大量记录,mysql索引进行访问的时候会带来大量随机IO,甚至有时候会出现重复IO
         原因:MYSQL会按照索引键的键值顺序依次来进行访问,一般来说每个数据页中大都会存放多条记录,但是这些记录大多数不会和你所使用的索引键值顺序一致,举例:查找A、B索引键的某些数据,先通过A找到第一条满足记录后,读取这条记录所在的X数据页,然后继续找,发现Y页也会满足,然后丢弃X数据页,直到查找完。然后轮到B,这个时候发现正在查的在X数据页,实际上已经出现重复读取X数据页两次,以后还会出现重复读,无意增大了存储引擎的IO访问量

         还有如果一个键值对应太多数据记录,也就是该键值会返回占整个表比例很大的记录,因为根据索引产生的都是随机IO,其效率低于全表查找顺序IO,会造成整体IO性能下降
       3>更新非常频繁的不能适合创建索引


      索引的限制:
        BLOB和TEXTT类型的列只能创建前缀索引
        使用不等于(!=或者<>)的时候MySql无法使用索引
        过滤字段使用了函数运算后(abs(column)),MySql无法使用索引
        join语句中join条件字段类型不一致的时候无法使用索引
        like操作无法使用索引
        使用非等值查询的时候MySql无法使用Hash索引

        其中前缀索引是提供了一个减少优化索引自身的功能,可以仅仅使用某个字段的签名部分作为索引键,达到减少索引占用的存储空间和提高索引访问的效率。但是前缀索引仅仅适用于字段前缀比较随机重复性小的字段,不然的话过滤性会降低,通过索引访问数据量就会增大,虽然通过前缀索引减少存储空间消耗但是会造成Query访问效率极大降低,得不偿失

     Join的实现原理及优化思路:
        实现原理:通过驱动表的结果集作为循环基础数据,然后通过该及结果集数据作为过滤条件到下一个表中查询数据,然后合并结果,依次循环下去


       

猜你喜欢

转载自blog.csdn.net/ligupeng7929/article/details/88575286
今日推荐