打卡Day1 数据库索引及优化

数据库认识

  • 数据库设计三范式
    1. 对属性的原子性,要求属性具有原子性,不可再分解
    2. 对记录的唯一性,要求记录有唯一标识,即实体的唯一性,不存在部分依赖
    3. 建立在第二范式之上,必须满足第二范式,非主键属性之间不存在函数依赖
  • 数据库的事物
    • 特性ACID

      1. 原子性(Atomicity,或称不可分割性)
      2. 一致性(Consistency)
      3. 隔离性(Isolation,又称独立性)
      4. 持久性(Durability)
    • 事务隔离级别

      隔离级别脏读(Dirty Read)不可重复读(Non-repeatable Read)幻读(Phantom)描述
      读未提交 (Read Uncommitted)可能可能可能允许一个事务读取另一个事务未提交的数据,隔离级别最低
      读已提交 (Read Committed)不可能可能可能一个事务只能读取已经提交的数据,是大多数数据库的默认隔离级别
      可重复读 (Repeatable Read)不可能不可能可能在同一事务中多次读取同样的数据结果一致,MySQL的默认级别
      串行化 (Serializable)不可能不可能不可能最高隔离级别,完全串行化执行,性能最差

索引

索引是数据库中一种特殊的数据结构,用于提高数据库表的查询效率,可以理解为是一种排好序的快速查找数据结构。它类似于书籍的目录,可以帮助数据库系统快速定位和访问数据,而不需要进行全表扫描。

  • 索引类型

    • 唯一索引:表上一个字段或者多个字段的组合建立的索引,这些字段组合起来能够确定唯一,允许存在空值(只允许存在一条空值)
    • 非唯一索引:表上一个字段或者多个字段的组合建立的索引,可以重复,不需要唯一
    • 主键索引:(主索引)根据主键pk_clolum(length)建立索引,不允许重复,不允许空值
    • 聚合索引:表中记录的物理顺序与键值的索引顺序相同
    • 非聚合索引:表中记录的物理顺序与键值的索引顺序无关
    • 全文索引:在某个字段设置全文索引后,根据特定语法查找满足条件的字段;
    • 普通索引:用表中的普通列构建的索引,没有任何限制
    • 组合索引:用多个列组合 构建的索引,但是在使用过程中有诸多规则,遵循最左前缀原则,顺序至关重要
    • Hash索引(Memory存储引擎)是通过索引列的值计算出hashCode,之后在相应的物理位置存取索引列的值,由于hashCode的唯一性,因此Hash索引不能进行范围查找或者是顺序查找
  • 索引优缺点

    • 索引的优点:
      • 大大加快数据的检索速度
      • 通过索引列对数据进行排序,降低数据排序的成本
      • 在使用分组和联合查询时,可以显著减少查询中分组和排序的时间
    • 索引的缺点:
      • 创建和维护索引需要耗费额外的存储空间
      • 在进行增删改操作时,需要同时维护索引,会降低写操作的性能
      • 索引过多会导致优化器在选择索引时需要耗费更多的时间

    索引的底层实现通常采用B+树结构,它能够保持数据稳定有序,并且方便按一定范围快速查找。

  • 索引创建

    可以创建多个普通索引,多个唯一索引,多个候选索引,一个主索引

    sql
    复制代码
    --MySQL创建索引 --主键索引 ALTER TABLE tableName ADD PRIMARY KEY(filedName); --唯一索引 CREATE UNIQUE INDEX indexName on tableName(fieldName[, fieldName ...]) --普通索引 CREATE INDEX indexName on tableName(fieldName[, fieldName ...]) ------------------------------------------------------------------------- --Oracle创建索引 --创建普通索引 CREATE INDEX PNN_GMA_APR_IDX_01 ON PNN_GMA_APR_T( PNN_PRC_NBR, PNN_APR_SID, PNN_APR_STA ) TABLESPACE TBS_CUR_IDX ONLINE; --创建唯一索引 CREATE UNIQUE INDEX PNN_GMA_APR_IDX_PK on PNN_GMA_APR_T( PNN_APR_PID ) GLOBAL TABLESPACE TBS_CUR_IDX ONLINE; ALTER TABLE PNN_GMA_APR_T ADD CONSTRAINT PNN_GMA_APR_IDX_PK PRIMARY KEY( PNN_APR_PID ) USING INDEX PNN_GMA_APR_IDX_PK ENABLE NOVALIDATE; ALTER TABLE PNN_GMA_APR_T MODIFY CONSTRAINT PNN_GMA_APR_IDX_PK ENABLE VALIDATE; --唯一索引创建解释 CREATE UNIQUE INDEX PNN_GMA_APR_IDX_PK on PNN_GMA_APR_T(PNN_APR_PID) GLOBAL TABLESPACE TBS_CUR_IDX ONLINE; 这条语句创建了一个名为PNN_GMA_APR_IDX_PK的唯一索引,该索引基于PNN_GMA_APR_T表的PNN_APR_PID字段。GLOBAL TABLESPACE TBS_CUR_IDX表示这个索引存储在全局表空间TBS_CUR_IDX中,ONLINE表示这个索引在创建后立即可用。 ALTER TABLE PNN_GMA_APR_T ADD CONSTRAINT PNN_GMA_APR_IDX_PK PRIMARY KEY(PNN_APR_PID) USING INDEX PNN_GMA_APR_IDX_PK ENABLE NOVALIDATE; 这条语句将PNN_GMA_APR_IDX_PK索引添加到PNN_GMA_APR_T表上,作为主键约束。ENABLE NOVALIDATE表示在添加约束时,不验证表中已有的数据是否满足这个约束。 ALTER TABLE PNN_GMA_APR_T MODIFY CONSTRAINT PNN_GMA_APR_IDX_PK ENABLE VALIDATE; 这条语句修改了PNN_GMA_APR_T表上的PNN_GMA_APR_IDX_PK主键约束,ENABLE VALIDATE表示在修改约束时,要验证表中已有的数据是否满足这个约束
  • 适合建索引的情形

    • 每张表都需要创建主键索引

      为什么? 底层索引树需要使用主键索引做聚簇,并且推荐使用自增ID做为主键 为什么是推荐自增ID做主键? 因为索引在底层实现里是有序的,如果是非自增ID,容易导致索引节点分裂重排。 主键不能是UUID吗? 理论上可以用UUID,但是UUID占用的字节长度为36,比8字节的bigint大了4倍,每个节点能存储的索引数量从1170降到390左右,三层B+树只能存储39039016=240万条,比使用bigint少了一个数量级,所以uuid做主键不仅仅占空间,还会让索引树变高,查询性能也会受影响,不推荐使用。

    • 频繁作为查询条件的字段应该创建索引
    • 查询中与其他表关联的字段,外键关系应当建立索引
    • 频繁更新的字段不适合创建索引—更新不止需要更新记录还要更新索引
    • 条件用不到的字段不创建索引
    • 查询中排序的字段,尽量包含在索引当中
    • 查询中统计或分组的字段,尽量包含在索引中
  • 不适合建索引的情形

    • 数据量太小的情况,有无索引区别不大
    • 经常做增删改的表—建索引虽然提高了查询效率,但更新性能大大降低
    • 数据大量重复且分布平均的字段,不适合当索引
  • 指定索引的方式

    • ORACLE

      1. /*+ INDEX(表名, 索引名) */
      2. /**+ LEADING(小表) USE_NL(大表) INDEX(大表 大表的索引)*/ 工作方式是从一张表中读取数据,访问另一张表(通常是索引)来做匹配,nested loops适用的场合是当一个关联表比较小的时候,效率会更高。 小表一般称为驱动表,可以是小表,也可以是小结果集;大表一般称为inner表,要求有索引。
      3. /*+ USE_HASH(大表A , 大表B ) */ 工作方式是将一个大表(通常是小一点的那个表)做hash运算,将列数据存储到hash列表中,从另一个表中抽取记录,做hash运算,到hash 列表中找到相应的值,做匹配
    • MySQL

      USE INDEX

    • TDSQL

      以上两种指定索引的方式都可以使用

  • 导致索引失效的几种情况

    • 没有where子句
    • 使用IS NULL或IS NOT NULL
    • where子句中使用函数
    • 使用like做左模糊查询(如果查询结果字段也在索引中的话,索引也是可以生效的)
    • where子句中使用不等于操作
    • 等于和范围索引不会被合并使用
    • 比较不匹配的数据类型,varchar类型会被隐式转换
    • 索引不符合最左匹配原则

索引的数据结构

MySQL

  • 所选数据结构 B+树,用多叉树尽量保证磁盘I/O操作最少
    • 非叶子节点只存储key和指针,不存数据,页号节点大小默认为16kb,能存储更多的索引项(可存储上千个),内存缓存的索引也更多,磁盘访问少

    • 树矮,由于第一个特性,3层的树高即可存储两千多万数据,查找任意数据时最多仅需3次磁盘IO。

      储存数据容量计算 假设主键是bigint占8字节,指针占6字节,那么单个非叶子节点可存储的索引数量是161024 / 14 ,假设每条数据记录占1kb,那单个叶子节点可存的记录数是16条,三层树高的B+树即可存11701170*16 = 2190万条;

    • 叶子节点使用双向链表串联,不用从根节点查找,范围查询时为顺序IO,速度比随机IO快。

  • 为什么不选择其他数据结构
    1. 哈希表 等值查询O(1) 比B+树O(log n)块
      • 不支持范围查询,只能全表扫
      • 不支持排序,得查出全部结果再排序
      • 存在hash冲突
    2. 红黑树/AVL树
      • 二叉树在数据量大时树高上涨快,同样的数据量树高比B+树多得多,磁盘IO增多查询很慢
    3. B树
      • 非叶子节点会存储全部数据,每个节点存储的数据量较少,导致相同数据量下也会有更大的层高,磁盘IO更多导致查询更慢
      • 叶子节点间无关联,范围查询时需要回到上层节点再往下查找

ORACLE

  • 所选数据结构B*树

执行计划

MySQL

  • explain SQL statement

  • 开启慢查询日志 show variable like '%slow_query_log%; set global slow_query_log = 1

  • 执行计划分析

    字段含义重要说明
    id查询标识符SQL执行顺序的标识,id值越大优先级越高,id相同则从上往下执行
    select_type查询类型SIMPLE(简单查询), PRIMARY(主查询), SUBQUERY(子查询), DERIVED(派生表查询)UNION(联合查询),UNION RESULT(联合查询结果)
    table表名显示这一行数据是关于哪张表的
    type访问类型system > const > eq_ref > ref > range > index > ALL (从左到右性能降低) system:表只有一条数据 const:主键索引或唯一索引,只匹配一行数据 eq_ref:唯一性索引扫描 ref:非唯一性索引扫描 range:范围扫描 index:全索引扫描 all:全表扫描
    possible_keys可能使用的索引显示可能应用在这张表上的索引
    key实际使用的索引实际使用的索引,如果为NULL则没有使用索引
    key_len索引使用的字节数可能的最大长度,并非实际使用长度,越少越好
    ref被使用的索引字段
    rows扫描行数预计要读取的行数
    filtered过滤百分比表示符合查询条件的数据百分比
    Extra额外信息包含不适合在其他列中显示但很重要的额外信息 using filesort 说明使用外部索引排序,并不是使用表内索引排序 using temporary 使用临时表,性能损耗大 using index 使用覆盖索引,避免回表 using where 使用查询 using index condition 使用索引下推,减少回表

Oracle

  1. explain plan for SQL statement;
  2. SELECT * FROM TABLE (dbms_xplan.display());
  • 执行计划分析

    类别参数/列名/操作说明示例/备注
    显示格式BASIC仅显示操作ID和名称FORMAT=>'BASIC'
    TYPICAL默认格式,显示成本、谓词等优化器标准输出
    ALL显示分区、别名、投影等完整信息详细分析使用
    ADVANCEDALL格式+OUTLINE数据深度调试使用
    基本列Id操作序号,决定执行顺序从0开始,子操作缩进显示
    Operation操作类型(如TABLE ACCESS)见下方操作类型详解
    Name对象名称(表/索引)EMPLOYEESPK_EMP_ID
    Rows (E-Rows)预估返回行数(基数)对比A-Rows判断统计准确性
    Bytes预估返回字节数
    Cost (%CPU)优化器估算成本值成本越低越好
    Time预估执行时间
    Pstart/Pstop分区访问起止编号分区表范围扫描时使用
    扩展统计Starts操作执行次数Starts>1可能效率低
    A-Rows实际返回行数SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(null,null,'ALLSTATS LAST'))
    A-Time实际耗时(含子操作)
    Buffers逻辑读块数
    Reads物理读块数
    Writes物理写块数
    OMem/1Mem最优/一次传递内存需求
    Used-Mem实际PGA内存使用
    Used-Tmp临时表空间使用量
    计算公式缓冲区命中率(Buffers-Reads)/Buffers*100理想值>95%
    基数准确率A-Rows/E-Rows接近1为准确
    每执行行数A-Rows/Starts判断执行效率
    表访问TABLE ACCESS FULL全表扫描适合大批量数据,小表可接受
    TABLE ACCESS BY INDEX ROWID索引回表需搭配索引扫描
    TABLE ACCESS SAMPLE采样扫描统计分析使用
    索引扫描INDEX UNIQUE SCAN唯一索引扫描最高效,返回0-1行
    INDEX RANGE SCAN索引范围扫描最常用,支持范围查询
    INDEX FULL SCAN索引全扫描避免回表,覆盖索引
    INDEX FAST FULL SCAN索引快速全扫描类似全表扫,读索引块
    INDEX SKIP SCAN跳跃扫描复合索引前导列选择性低
    连接方式NESTED LOOPS嵌套循环连接驱动表小,被驱动表有索引
    HASH JOIN哈希连接大数据集,需内存支持
    SORT MERGE JOIN排序合并连接已排序数据或需排序输出
    CARTESIAN JOIN笛卡尔积应避免,检查连接条件
    谓词类型access访问谓词(定位数据)出现在索引扫描、连接操作
    filter过滤谓词(筛选数据)访问后过滤,消耗CPU
    并行执行IN-OUT数据流方向P->S, P->P, S->P
    PQ Distrib分布方式HASH, RANGE, BROADCAST
    TQ表队列标识并行进程间通信
    PCWC/PCWP并行合并操作与子/父操作合并执行
    关键判断Starts>1且A-Rows小可能存在低效操作如NESTED LOOPS被驱动表重复扫描
    Buffers/A-Rows过大逻辑读效率低检查索引或过滤条件
    E-Rows与A-Rows差异大统计信息过时需收集统计信息
    驱动表过大NESTED LOOPS性能差考虑改为HASH JOIN

什么是覆盖索引? 是指二级索引中已经包含查询所需要的字段,直接从二级索引取数据,不用再回表查询。减少I/O操作,减少内存占用,查询更快 什么是回表? 回表是指使用二级索引查询时,因为二级索引中只存索引列的值和主键值,拿不到其他字段的数据,当需要查询其他字段时,就需要拿到主键再去聚簇索引中查询一遍数据行,这个过程就叫回表。 什么是索引下推? 把部分查询条件下推到存储引擎层,在引擎层就把不符合条件的数据过滤掉,减少回表查询

锁机制

  • 读锁/共享锁(S锁)阻塞写,不阻塞读
    • 表加S锁 上锁的线程,不允许再对库的其他表进行操作,只允许读当前表;其他线程不允许对加锁的表进行更新,会线程等待。
    • 手动添加共享锁 select * from tableName where id = 1 in share mode;
  • 写锁/排它锁(X锁)阻塞写和读
    • 表加X锁 上锁的线程,可以对当前表进行操作;其他线程不允许对表操作,即使读也会线程等待
    • 行加X锁 当前行只允许一个线程操作,其他线程被阻塞,允许读
  • 间隙锁Next-Key锁
    • 使用范围条件请求共享锁或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内并不存在的间隙记录,也会进行加锁
  • 注意事项
    • 索引失效会导致行锁升级为表锁
  • 分析行锁
    • show status like ‘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 等待总次数
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP