• 数据库面试题


    数据库的架构

    如何设计一个关系型数据库(数据库的架构)

    索引

    索引的简介

    MySQL 官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。

    索引的数据结构(B+ 树)

    • B+ 树结构图

    • B+ 树的特点
    1.非叶子节点的子树指针与关键字个数相同
    2.非叶子节点的子树指针P[i],指向关键字值[K[i],k[i+1])的子树
    3.非叶子节点仅用来索引,数据都保存在叶子节点中(查询只在叶子节点结束)
    4.所有叶子节点均有一个链指针指向下一个叶子节点(便于数据库的范围统计)
    
    • B+ 树更适合用来做存储索引的原因
    1.B+树的磁盘读写代价更低(树的深度低,IO次数少)
    2.B+树的查询效率更加稳定(所有查询结果都会在叶子节点结束)
    3.B+树更有利于对数据库的扫描(通过数据指针横向扫描数据)
    

    密集索引和稀疏索引的区别

    • 密集索引文件中的每个搜索码值都对应一个索引值(即可根据索引值找到对应的一条数据,因此每张表只能创建一个密集索引)

    • 稀疏索引文件只为索引码的某些值建立索引项(即根据索引值找到对应一条数据的地址或主键,再根据地址或主键找到数据)

    MySQL两种存储引擎的索引区别

    参考的博客

    • MyISAM
    在MyISAM中不管是主键索引唯一键索引、普通索引,其索引都属于稀疏索引
    
    • Innodb
    在Innodb中 有且仅有一个密集索引 
    Innodb中选取规则:
    1.如果一个主键被定义了,则该主键作为密集索引
    2.若该主键没有被定义,则该表的第一个唯一非空索引作为密集索引
    3.若不满足上述条件 则Innodb内部会生成一个隐藏主键(密集索引)
    4.非主键索引存储相关键位和其对应的主键值,包含两次查找(第一次根据索引值找到对应数据的主键,然后根据主键找到相应的数据)
    
    • 两种引擎索引的实例图

    • 两种引擎会建立不同的文件
    1.建立一个MyISAM引擎的数据表 test1 会建立三个文件
    test1.frm   存储表的结构信息
    test1.MYI  存储表的索引信息
    test1.MYD 存储表的数据
    2.建立一个InnoDB引擎的数据表 test2 会建立两个文件
    test2.frm 存储表的结构信息
    test2.ibd 存储表的数据和索引
    

    联合索引的最左匹配原则

    参考的博客

    select * from t where a=1 and b=1 and c =1;     #这样可以利用到定义的索引(a,b,c)
    
    select * from t where a=1 and b=1;     #这样可以利用到定义的索引(a,b,c)
    
    select * from t where a=1;     #这样也可以利用到定义的索引(a,b,c)
    
    select * from t where b=1 and c=1;     #这样不可以利用到定义的索引(a,b,c)
    
    select * from t where a=1 and c=1;     #这样不可以利用到定义的索引(a,b,c)
    

    也就是说通过最左匹配原则你可以定义一个联合索引,但是使得多中查询条件都可以用到该索引。
    值得注意的是,当遇到范围查询(>、<、between、like)就会停止匹配。也就是:

    select * from t where a=1 and b>1 and c =1;     #这样a,b可以用到(a,b,c),c不可以
    

    总结

    • 在 InnoDB 中联合索引只有先确定了前一个(左侧的值)后,才能确定下一个值。如果有范围查询的话,那么联合索引中使用范围查询的字段后的索引在该条 SQL 中都不会起作用。

    • 值得注意的是,in= 都可以乱序,比如有索引(a,b,c),语句 select * from t where c =1 and a=1 and b=1,这样的语句也可以用到最左匹配,因为 MySQL 中有一个优化器,他会分析 SQL 语句,将其优化成索引可以匹配的形式,即 select * from t where a =1 and a=1 and c=1

    索引是建立得越多越好吗

    • 不是
    1.数据量小的表不需要建立索引,建立会增加额外的索引开销
    2.数据变更需要维护索引,因此更多的索引意味着更多的维护成本
    3.更多的索引意味着也需要更多的空间
    

    数据库锁的分类

    • 按锁的粒度划分,可分为表级锁,页级锁,行级锁
    • 按锁级别划分,可分为共享锁(读锁)、排他锁(写锁)
    • 按加锁方式划分,可分为自动锁、显示锁
    • 按操作划分,可分为DML锁、DDL锁
    • 按使用方式划分,可分为乐观锁、悲观锁

    MySQL两种存储引擎的锁区别

    1.MyISAM默认用的表级锁,不支持行级锁
    2.InnoDB默认用的是行级锁,也支持表级锁
    

    注意

    当InnoDB做增删改查的数据没用到索引时,会上表级锁,否则默认上的是行级锁
    

    数据课事务的四大特性

    ACID

    • 原子性(Atomic)
    • 一致性(Consistency)
    • 隔离性(Isolation)
    • 持久性(Durability)

    事务隔离级别及对应解决的并发问题

    • 更新丢失——MySQL所有事务隔离级别在数据库层面上均可避免

    • 脏读——READ-COMMITTED 事务隔离级别以上可避免(oracle默认的事务隔离级别)

      脏读解释:一个事务读到了另一个尚未提交的事务更改后的数据
      
    • 不可重复读 ——REPEATABLE-READ事务隔离级别以上可避免(MySQL默认的事务隔离级别)

      不可重复读解释:一个事务重复读同一数据时,数据不一致
      
    • 幻读——SERIALIZABLE事务隔离级别可避免

      幻读解释:一个事务进行更新时,有另外一个事务进行了增加或删除操作并提交了,导致原本更新的行数增加了或减少了
      

    InnoDB可重复读隔离级别下如何避免幻读

    使用next-key锁(行锁+gap锁)

    gap锁的解释:也叫范围锁,会锁定一个范围,但不包括记录本身。当一个范围被gap锁锁住时,无法往这个范围进行写操作(增删改)。
    

    只有在隔离级别REPEATABLE-READ和SERIALIZABLE的时候才会使用gap锁,低于隔离级别REPEATABLE-READ不会使用gap锁

    gap锁的使用情况

    1. 当使用唯一索引或主键索引时

      • 如果where条件全部命中,则不会使用gap锁,只会加行级锁

        #id为主键或为唯一键
        delete into tbl_name where id=1;
        #此时不会使用gap锁,因为该记录是唯一的,不会因为别的事务增加一条id=1记录(唯一键约束无法增加),造成幻读
        
      • 如何where条件部分命中或者全不命中,则会加gap锁

        #id为主键或为唯一键,但记录中没有id=2的数据,有id=1,id=3的数据
        delete into tbl_name where id=2;
        #此时会使用gap锁,gap锁的范围会是id的值:1<被锁的范围<=3,在这个范围另一个无法进行写操作,从而避免幻读了
        
    2. 当使用非唯一索引或者不走索引时

      • 非唯一索引

        #id为非唯一索引,有id=1,id=2,id=3的数据
        delete into tbl_name where id=2;
        #此时会使用gap锁,gap锁的范围会是id的值:1<被锁的范围<=3(因为id=2的记录不是唯一的)
         ,在这个范围另一个无法进行写操作,从而避免幻读了
        
      • 不走索引

        #id该字段没有索引,有id=1,id=2,id=3的数据
        delete into tbl_name where id=2;
        #此时会使用gap锁,gap锁的范围会是整张表,因为不走索引无法确定id=2具体在哪个范围
        

      关于group by与having

      group by的语法

      1.group by里出现某个表的字段,select里面的列要么是该group by里出现的列,要么是别的表或者带有列函数的列
      2.列函数形如:count(),sum(),max()等统计函数,列函数对于group by子句定义的每个组各返回一个结果

      #查询所有同学的学号(group by 出现的字段)、姓名(别的表的字段)、
      选课数(带有列函数的列)、总成绩(带有列函数的列)
      select s.student_id,stu.name,count(s.course_id),sum(s.score)
      from 
      	score s,
      	student stu
      where
      s.student_id=stu.student_id
      group by student_id;
      

      having的语法

      1. 通常与group by子句一起使用
      2. where过滤行,having过滤组
      3. 出现在同一sql的顺序:where>group by>having
      #查询没有学全所有课的同学的学号、姓名
      select s.student_id,stu.name
      from 
      	score s,
      	student sut
      where s.student_id=stu.student_id
      group by s.student_id
      having count(s.score_id)<
      (select s.score_id from s);
      

      三大范式

      参考的博客

      三大范式通俗解释:

      (1)简单归纳:

        第一范式(1NF):字段不可分;
        第二范式(2NF):有主键,非主键字段依赖主键;
        第三范式(3NF):非主键字段不能相互依赖。

      (2)解释:

        1NF:原子性。 字段不可再分,否则就不是关系数据库;;
        2NF:唯一性 。一个表只说明一个事物;
        3NF:每列都与主键有直接关系,不存在传递依赖。

  • 相关阅读:
    oracle 索引
    oracle 子查询因子化 浅谈(with的使用)
    大数据的遐想
    数据挖掘(算法概要链接)
    orcale 修改字段属性
    sql 对一张表进行按照不同条件进行多次统计
    oracle dblink
    oracle作业
    SQL效率的几点心得
    提高SQL语句的性能
  • 原文地址:https://www.cnblogs.com/shouyaya/p/13478716.html
Copyright © 2020-2023  润新知