数据库面试题整理

数据库

如下是对面试常见面试题整理,来自知乎大神分享的pdf,引用部分连接已给出,若是有没有标注的,纯属意外,但愿提醒。这篇主要整理出来给本身看的
B/B+树html

B/B+java

1、B树:

  1. 定义:B 树又叫平衡多路查找树。一棵m阶的B 树 的特性以下:
  • 树中每一个结点最多含有m个孩子(m>=2);
  • 除根结点和叶子结点外,其它每一个结点至少有[ceil(m / 2)]个孩子(其中ceil(x)是一个取上限的函数);
  • 若根结点不是叶子结点,则至少有2个孩子(特殊状况:没有孩子的根结点,即根结点为叶子结点,整棵树只有一个根节点);
  • 全部叶子结点都出如今同一层,叶子结点不包含任何关键字信息
  • 每一个非终端结点中包含有n个关键字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中: 
    • Ki (i=1...n)为关键字,且关键字按顺序升序排序K(i-1)< Ki
    • Pi为指向子树根的接点,且指针P(i-1)指向子树种全部结点的关键字均小于Ki,但都大于K(i-1)。
    • 关键字的个数n必须知足: [ceil(m / 2)-1]<= n <= m-1。以下图所示:

image

B树中的每一个结点根据实际状况能够包含大量的关键字信息和分支(固然是不能超过磁盘块的大小,根据磁盘驱动(disk drives)的不一样,通常块的大小在1k~4k左右);这样树的深度下降了,这就意味着查找一个元素只要不多结点从外存磁盘中读入内存,很快访问到要查找的数据mysql

代码定义:
imageweb

  1. B树优点
    高度比平衡树低,因此IO磁盘操做次数少,查找更快面试

    当B树包含N个关键字时,B树的最大高度为l-1(由于计算B树高度时,叶结点所在层不计算在内),即:l - 1 = log┌m/2┐((N+1)/2 )+1。sql

    2、B+树

  2. 定义
  • 有n棵子树的结点中含有n-1 个关键字;
  • 全部的叶子结点中包含了所有关键字的信息,及指向含有这些关键字记录的指针,且叶子结点自己依关键字的大小自小而大的顺序连接。 (而B 树的叶子节点并无包括所有须要查找的信息)
  • 全部的非终端结点能够当作是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含须要查找的有效信息)

image

  1. B+树优点
  • B+-tree的磁盘读写代价更低:
    B+-tree的内部结点并无指向关键字具体信息的指针。所以其内部结点相对B 树更小。若是把全部同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的须要查找的关键字也就越多。相对来讲IO读写次数也就下降了。数据库

    举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点须要2个盘快。而B+ 树内部结点只须要1个盘快。当须要把内部结点读入内存中的时候,B 树就比B+ 树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。设计模式

  • B+-tree的查询效率更加稳定:
    因为非终结点并非最终指向文件内容的结点,而只是叶子结点中关键字的索引。因此任何关键字的查找必须走一条从根结点到叶子结点的路。全部关键字查询的路径长度相同,致使每个数据的查询效率至关。数组

3、 B*树

  1. 定义
    B-tree是B+-tree的变体,在B+树的基础上(全部的叶子结点中包含了所有关键字的信息,及指向含有这些关键字记录的指针),B树中非根和非叶子结点再增长指向兄弟的指针;B树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为2/3(代替B+树的1/2)

image

4、总结

  1. B树对于全库扫描具备优点,B树须要中序遍历的方法按序扫库,这样IO操做次数会比较多。而B+树直接从叶子结点挨个扫一遍就完了。另外B+树支持range-query很是方便,而B树不支持。这是数据库选用B+树的最主要缘由。
  2. 而B树对于成功查询颇有优点,由于它不用从根节点走到子节点就已经找到数据了。有不少基于频率的搜索是选用B树,越频繁查询的结点越往根上走,前提是须要对查询成功率作统计。
  3. mysql 底层存储是用B+树实现的。内存中B+树是没有优点的,可是一到磁盘,B+树的威力就出来了。由于它的非叶节点只存储关键字,那么单位磁盘上能够存储的关键字就更多,比起B树,须要的磁盘IO就更少。

在MySQL中,索引属于存储引擎级别的概念,不一样存储引擎对索引的实现方式是不一样的,这里主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式
索引服务器

1、MyISAM索引实现

MyISAM引擎使用B+Tree做为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图
image
假设咱们以Col1为主键,则图8是一个MyISAM表的主索引(Primary key)示意。能够看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是惟一的,而辅助索引的key能够重复
image
MyISAM的索引方式也叫作“非汇集”的,之因此这么称呼是为了与InnoDB的汇集索引区分。

2、InnoDB索引实现

虽然InnoDB也使用B+Tree做为索引结构,但具体实现方式却与MyISAM大相径庭。

  1. 第一个重大区别是InnoDB的数据文件自己就是索引文件
    image
    叶节点包含了完整的数据记录。这种索引叫作汇集索引。由于InnoDB的数据文件自己要按主键汇集,因此InnoDB要求表必须有主键(MyISAM能够没有)
  2. 第二个与MyISAM索引的不一样是InnoDB的辅助索引data域存储相应记录主键的值而不是地址
    image
    汇集索引这种实现方式使得按主键的搜索十分高效,可是辅助索引搜索须要检索两遍索引:首先检索辅助索引得到主键,而后用主键到主索引中检索得到记录。
  3. 了解不一样存储引擎的索引实现方式对于正确使用和优化索引都很是有帮助,例如知道了InnoDB的索引实现后,就很容易明白为何不建议使用过长的字段做为主键,由于全部辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段做为主键在InnoDB中不是个好主意,由于InnoDB数据文件自己是一颗B+Tree,非单调的主键会形成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段做为主键则是一个很好的选择。

    3、索引使用策略及优化

  4. 实例数据库
    image
  5. 最左前缀原理与相关优化
    索引:<emp_no, title, from_date>
  • 全列匹配
  • 最左前缀匹配。
    当查询条件精确匹配索引的左边连续一个或几个列时,如 或<emp_no, title>,因此能够被用到,可是只能用到一部分,即条件所组成的最左前缀。上面的查询从分析结果看用到了PRIMARY索引,可是key_len为4,说明只用到了索引的第一列前缀。
  • 查询条件用到了索引中列的精确匹配,可是中间某个条件未提供。
  • 查询条件没有指定索引第一列。
  • 匹配某列的前缀字符串
  • 范围查询。范围列能够用到索引(必须是最左前缀),可是范围列后面的列没法用到索引。同时,索引最多用于一个范围列,所以若是查询条件中有两个范围列则没法全用到索引
  • 查询条件中含有函数或表达式。
    很不幸,若是查询条件中含有函数或表达式,则MySQL不会为这列使用索引

索引的使用

3、用索引

  • 表数量比较多而且选择性高的状况考虑用索引
  • 索引长度不能过长,能够考虑某一个列的前缀作索引
  • 在使用InnoDB存储引擎时,若是没有特别的须要,请永远使用一个与业务无关的自增字段做为主键。为了插入数据的时候可以不开启新的磁盘页。
  1. 索引的建立、删除
  • 索引的类型:
    • UNIQUE(惟一索引):不能够出现相同的值,能够有NULL值
    • INDEX(普通索引):容许出现相同的索引内容
    • PROMARY KEY(主键索引):不容许出现相同的值
    • fulltext index(全文索引):能够针对值中的某个单词,但效率确实不敢恭维
    • 组合索引:实质上是将多个字段建到一个索引里,列值的组合必须惟一
  • 使用ALTER TABLE语句建立索性
//普通索引
alter table table_name add index index_name (column_list) ;
//惟一索引
alter table table_name add unique (column_list) ;
//主键索引
alter table table_name add primary key (column_list) ;
  • 使用CREATE INDEX语句对表增长索引
//CREATE INDEX可用于对表增长普通索引或UNIQUE索引,可用于建表时建立索引。

CREATE INDEX index_name ON table_name(username(length)); 
//若是是CHAR,VARCHAR类型,length能够小于字段实际长度;若是是BLOB和TEXT类型,必须指定 length。

//create只能添加这两种索引;
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
  • 删除索引
drop index index_name on table_name ;

alter table table_name drop index index_name ;

alter table table_name drop primary key ;
  1. 注意事项
  • 虽然索引大大提升了查询速度,同时却会下降更新表的速度。
  • 创建索引会占用磁盘空间的索引文件
  • 索引不会包含有NULL的列
  • 使用短索引
  • 索引列排序
  • like语句操做
  • 不要在列上进行运算
  • 不使用NOT IN 、<>、!=操做,但<,<=,=,>,>=,BETWEEN,IN是能够用到索引的
  • 索引要创建在常常进行select操做的字段上。
  • where的查询条件里有不等号(where column != …),mysql将没法使用索引。

1、事务隔离级别

  1. Mysql隔离级别
  • 未提交读:事务能够读取未提交数据,称为脏读
  • 提交读:一个事物只能看见其余已提交事务的修改过的数据
  • 可重复读:保证同一事务屡次读取一数据结果是一致的。解决了脏读,但会致使幻读。幻读就是一个事务在读取某一个范围数据时,另外一个事务在该范围内插入数据。以前事务再次读取范围内数据,会致使幻读。可重复读是Mysql默认隔离级别
  • 可串行化:最高隔离级别,强制事务串行化。避免了幻读,可是并发很差
  1. 数据库特性
  • 原子性(Atomicity):事务里的
    全部操做要么所有作完,要么都不作
  • 一致性(Consistency):从一个一致性状态到另外一个一致性状
    态。

    例如现有完整性约束 a+b=10,若是一个事务改变了a,那么必须
    得改变b,使得事务结束后依然知足 a+b=10,不然事务失败。

  • 隔离性(Isolation):一个事务所作的修改在最终提交之前,对其它事务不可见。

    好比现有有个交易是从A 帐户转100 元至 B 帐户,在这个交易还
    未完成的状况下,若是此时 B 查询本身的帐户,是看不到新增长的
    100元的。

  • (Durability) 持久性
    持久性是指一旦事务提交后,它所作的修改将会永久的保存在数据库
    上,即便出现宕机也不会丢失。

    2、sql优化

  1. 查询优化,避免全表扫描
  2. in和not in慎用
  3. 拆分大的update和insert,提升并发性
  4. 避免使用!=,<,>
  5. 避免对null判断,不然会致使引擎放弃索引而全表扫描
  6. 避免用or
  7. 避免表达式或者函数,不然全表扫描
  8. exists代替in
  9. 避免更新汇集列
  10. 尽可能使用数字字段而不是字符字段

    3、实践中如何优化sql

  11. sql语句以及索引优化,如上
  12. 数据库表结构优化
  • 尽可能不要用double
  • 尽可能不用text
  • 尽可能使用timestamp而不是datetime

    4、数据库范式

  1. 1nf:属性原子性
  2. 2nf:消除非主属性对部分吗依赖
  3. 3nf:消除对码的传递依赖
  4. bcnf:消除子属性对部分码依赖
  5. 4nf:消除多值依赖

5、数据库链接池

链接池

  1. 早期咱们怎么进行数据库操做
  • ①装载数据库驱动程序;
  • ②经过jdbc创建数据库链接;
  • ③访问数据库,执行sql语句;
  • ④断开数据库链接。
Public void FindAllUsers(){
              //一、装载sqlserver驱动对象
              DriverManager.registerDriver(new SQLServerDriver());             
              //二、经过JDBC创建数据库链接
              Connection con =DriverManager.getConnection("jdbc:sqlserver://192.168.2.6:1433;DatabaseName=customer", "sa", "123");            
              //三、建立状态
              Statement state =con.createStatement();           
              //四、查询数据库并返回结果
              ResultSet result =state.executeQuery("select * from users");           
              //五、输出查询结果
              while(result.next()){
                     System.out.println(result.getString("email"));
              }            
              //六、断开数据库链接
              result.close();
              state.close();
              con.close();
        }

程序开发过程当中,存在不少问题:首先,每一次web请求都要创建一次数据库链接。创建链接是一个费时的活动,每次都得花费0.05s~1s的时间,并且系统还要分配内存资源。这个时间对于一次或几回数据库操做,或许感受不出系统有多大的开销。但是对于如今的web应用,尤为是大型电子商务网站,同时有几百人甚至几千人在线是很正常的事。在这种状况下,频繁的进行数据库链接操做势必占用不少的系统资源,网站的响应速度一定降低,严重的甚至会形成服务器的崩溃。不是危言耸听,这就是制约某些电子商务网站发展的技术瓶颈问题。其次,对于每一次数据库链接,使用完后都得断开。不然,若是程序出现异常而未能关闭,将会致使数据库系统中的内存泄漏,最终将不得不重启数据库。还有,这种开发不能控制被建立的链接对象数,系统资源会被毫无顾及的分配出去,如链接过多,也可能致使内存泄漏,服务器崩溃。

  1. 技术演进出来的数据库链接池
       咱们本身尝试开发一个链接池,来为上面的查询业务提供数据库链接服务:
  • ①   编写class 实现DataSource 接口
  • ②   在class构造器一次性建立10个链接,将链接保存LinkedList中
  • ③   实现getConnection  从 LinkedList中返回一个链接
  • ④   提供将链接放回链接池中方法
public class MyDataSource implements DataSource {
              //链表 --- 实现栈结构
              privateLinkedList<Connection> dataSources = new LinkedList<Connection>();
 
              //初始化链接数量
              publicMyDataSource() {
                     //一次性建立10个链接
                     for(int i = 0; i < 10; i++) {
                            try {
                               //一、装载sqlserver驱动对象
                               DriverManager.registerDriver(new SQLServerDriver());
                               //二、经过JDBC创建数据库链接
                               Connection con =DriverManager.getConnection(
                                  "jdbc:sqlserver://192.168.2.6:1433;DatabaseName=customer", "sa", "123");
                               //三、将链接加入链接池中
                               dataSources.add(con);
                            } catch (Exception e) {
                               e.printStackTrace();
                            }
                     }
              }
 
              @Override
              publicConnection getConnection() throws SQLException {
                     //取出链接池中一个链接
                     finalConnection conn = dataSources.removeFirst(); // 删除第一个链接返回
                     returnconn;
              }
 
              //将链接放回链接池
              publicvoid releaseConnection(Connection conn) {
                     dataSources.add(conn);
                     }
       }
//查询全部用户
       Public void FindAllUsers(){
              //一、使用链接池创建数据库链接
              MyDataSource dataSource = new MyDataSource();
              Connection conn =dataSource.getConnection();        
              //二、建立状态
              Statement state =con.createStatement();           
              //三、查询数据库并返回结果
              ResultSet result =state.executeQuery("select * from users");           
              //四、输出查询结果
              while(result.next()){
                     System.out.println(result.getString("email"));
              }            
              //五、断开数据库链接
              result.close();
              state.close();
              //六、归还数据库链接给链接池
              dataSource.releaseConnection(conn);
        }
  1. 链接池还要考虑更多的问题
  • 并发问题:使用synchronized关键字
  • 多数据库服务器和多用户:设计一个符合单例模式的链接池管理类,在链接池管理类的惟一实例被建立时读取一个资源文件,其中资源文件中存放着多个数据库的url地址等信息。
  • 事务处理:设置connection的autocommit属性为false 而后显式的调用commit或rollback方法来实现。可采用每个事务独占一个链接来实现,这种方法能够大大下降事务管理的复杂性。
  • 链接池的分配与释放
  • 链接池的配置与维护

问题

1. 500 万数字排序,内存只能容纳 5 万个,如何排序,如何优化?
  • 方案一:位图法
    java代码
    对这些数进行位图排序,只须要约5000000/8 = 625000byte,就是0.625M,排序后输出。可是该方法具备局限性,须要知道这些数据中的最大值。并且要考虑数据疏密程度,若是最大值1000000,而只有100个元素,那么效率会变得很是低
  • 方案二:归并排序法
    多路归并代码
    ,多路归并就是从多个有序数列中归并。
  1. 将500万的数据,分红40个有序文件,分别在内存中排序,而后对这40个有序文件进行归并排序。
  2. 读取每一个文件中第一个数(每一个文件的最小数),存放在一个大小为40的data数组中
  3. 选择data数组中最小的数min_data,及其相应的文件索引(来自哪一个文件)index
  4. 将min_data写入到文件result,而后更新数组data(根据index,读取该文件的下一个数代替min_data)
  5. 判读是否全部数据都读取完毕,不然返回到2步。
2. 平时怎么写数据库的模糊查询

前缀查询,例如“abc%”,仍是索
引策略的问题

3. 数据库链接池(druid)、线程池做用等等

对于共享资源,有一个很著名的设计模式:资源池(resource pool)。该模式正是为了解决资源的频繁分配﹑释放所形成的问题。为解决上述问题,能够采用数据库链接池技术。数据库链接池的基本思想就是为数据库链接创建一个“缓冲池”。预先在缓冲池中放入必定数量的链接,当须要创建数据库链接时,只需从“缓冲池”中取出一个,使用完毕以后再放回去。咱们能够经过设定链接池最大链接数来防止系统无尽的与数据库链接。更为重要的是咱们能够经过链接池的管理机制监视数据库的链接的数量﹑使用状况,为系统开发﹑测试及性能调整提供依据。线程池也是如此,由于频繁开启关闭线程会下降系统资源,因此能够用线程池达到资源共享,统一管理线程资源的目的。初始化一个比较大的线程池,每当程序须要开启新的线程时,会到线程池中申请。一样的咱们能够初始化适当的线程数来达到最大的资源利用率

4. 数据库设计通常设计成第几范式

我的认为是第三范式,此时数据冗余较小,范式级别太高对查询效率也不利

5. 表(三个字段:姓名,id,分数)要求查出平均分大于 80 的 id 而后分数降序排

序。

select id from table group by id having avg(score) > 80 order by avg(score) desc
6. 一个整形数组中,找出第三大的数

利用快排

/找出第K大的数
int find_k_big(int A[], int low, int high, int k)
{
    if(low < high)
    {
        int pivot_pos = partion2(A, low, high);
        if(pivot_pos + 1 == k)
            return A[pivot_pos];
        else if(pivot_pos + 1 > k)
            find_k_big(A, low, pivot_pos - 1, k);
        else
            find_k_big(A, pivot_pos + 1, high, k);
    }
    else
        return -1;
7. 悲观锁实现

悲观锁

8. 建表的原则
9. 索引的内涵和用法
相关文章
相关标签/搜索