python_面试题_DB相关问题

Posted gbq-dog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python_面试题_DB相关问题相关的知识,希望对你有一定的参考价值。

1.mysql部分

问题

问题1:mysql的存储引擎

问题2:mysql的索引机制

问题3:mysql的事务以及事务隔离级别

问题4:mvcc/GAP lock是做什么的

问题5:mysql的悲观锁与乐观锁

 

回答

问题1:

mysql的存储引擎

  • 在mysql中的查询语句为:mysql> show engines,
  • 查看当前mysql的默认存储引擎为:mysql> show variables like ‘%storage_engine%‘,
  • 查看某个表用了什么引擎则是:mysql> show create table 表名;

mysql的常用存储引擎

  • MyISAM:不支持事务,不支持外键,访问速度快,对事务完整性没有要求,设计之初主要为了查询操作。使用场景有:非事务型应用,只读类应用,空间类应用
  • InnoDB(MySQL 5.5后默认引擎):支持完整事务,支持外键约束,行级锁,设计之初处理大容量数据库操作,容易有IO瓶颈。使用场景:适用绝大多数场景
  • MEMORY:只存储表,数据放在内存中,掉电后数据不保留,访问速度快。使用场景:内容变化不频繁的代码表,或者是作为统计

 

问题2:

mysql的索引机制有如下几种

  • 聚集索引:就是按照每张表的主键构造一颗B+树,叶子节点村完整的行记录数据,未定义主键,则取第一个唯一索引,每张表只能拥有一个聚集索引。在多数情况下,查询优化器倾向于采用聚集索引。因为聚集索引能够在B+树索引的叶子节点上直接找到数据,并且可以快速查找数据。
  • 非聚集索引:是对聚集索引的一种补充,每张表可以有多个辅助索引,辅助索引与聚集索引区别为叶子节点不包含行记录的全部数据,除此之外还包含一个书签,可以指向对应的行数据。
  • 普通索引:普通的索引,index只是为了加速查找
  • 唯一索引:主键索引:加速查找+约束(不为空,不能重复) 唯一索引:加速查找+约束(不能重复)
  • 组合索引:多个索引联合查找,查找顺序从左到右查询。

备注:举例说明索引机制的使用场景(商城的会员卡系统,会员编号作为主键,会员编号就是唯一索引,用来创建B+树;会员姓名如果要创建索引,则就是普通索引;会员身份证信息如果要创建索引,则就是唯一索引)

补充知识:B+树与B树

(1.)B树

所谓的B树就是多路搜索树,任意一个非叶子节点最多有M个儿子,关键字分布在整颗树中,任何一个关键字出现且只出现一个节点中,搜索有可能在非叶子节点中结束,性能等价于二分查找,时间复杂度为O(lgN)

如:(M=3)

技术图片

(2.)B+树

所谓的B+树,就是B树的变体,也是多路搜索树,非叶子节点的字数指针与关键字个数相同,非叶子节点子树指针指向子树,所有的叶子节点都增加一个链指针,所有的关键字都在叶子节点中出现,不可能在非叶子结点命中,非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层,比较适合文件索引系统

如:(M=3)

技术图片

 

问题3:

mysql的事务遵循4个条件(ACID),有原子性:不可分割性;一致性;隔离性;持久性;在实际使用中,使用事务保证一个事务中的操作,要么全部完成,要么全部不完成,保证数据的安全性

其中事务隔离级别分为以下几种

  • RU:既未提交读,容易出现脏读情况,既事务A读到了事务B没有提交的数据;
  • RC:既提交读,为解决脏读问题,容易出现提交重复的情况,既事务A在执行的过程中,有可能事务B提交了数据,事务读取的数据和之前不一致;
  • RR:既可重复读,为解决上一级的数据不一致问题,该级别采用MVCC(多版本并发操作)解决重复读,一般RR级别会产生幻读的问题,既同一个事务多次执行同一个select,读取到的数据行发生了改变,这是因为数据行发生了行数减少或者新增;所谓幻读就是同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会“惊奇的”发现了这些新数据,貌似之前读到的数据是“鬼影”一样的幻觉。搞笑的比喻就是,假如,中午去食堂打饭吃,看到一个座位是空的,便屁颠屁颠的去打饭,回来后,发现这些座位都还是空的(重复读),窃喜。走到跟前刚准备坐下时,却惊现一个恐龙妹,严重影响食欲。仿佛之前看到的空座位是“幻影”一样。但是mySQL中RR级别引入了GAP LOCK(间隙锁)的概念,可在RR级别即可解决幻读的问题;另外一个特性是mysql里的MVCC只解决读-写的阻塞问题,写-写依然还是阻塞的。
  • SE:既可序列化,事务相当于串行操作,解决了脏读、不可重复读、幻读等问题,但对性能和效率的影响很大

 

问题4:

MVCC:既多版本并发操作,基本原理就是某个时间点的快照,每行数据都存在一个版本,每次数据更新时都更新该版本,修改是copy出当前版本随意修改,各个事务之间无干扰,保存时则比较版本号,如果成功,则覆盖原纪录,失败则放弃并回滚。需要注意的是mysql里的MVCC只解决读-写的阻塞问题,写-写依然还是阻塞的

GAP lock:间隙锁,用于解决mySQL中RR级别的幻读问题,如果一个事务操作的是一个区间的数据,会锁住这个区间所有的记录,即使这个记录不存在,这个时候另一个会话去插入这个区间的数据,就必须等待上一个结束。需要注意的是在此有可能会产生GAP死锁,例如下图

技术图片

 

问题5:

无论悲观锁和乐观锁都是并发控制主要采用的技术手段,都是为了保证数据库中的数据的完整性与一致性

悲观锁:对数据被外界修改持保守态度 (悲观),因此,在整个数据处理过程中,将数据处于锁定状态。 悲观锁的实现,往往依靠数据库提供的锁机制,而悲观锁一般流程如下

  1. 在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)
  2. 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。
  3. 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

悲观锁主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载,还有会降低了并行性。

技术图片
要使用悲观锁,我们必须关闭 mysql 数据库的自动提交属性,因为 MySQL 默认使用 autocommit 模式,也就是说,当你执行一个更新操作后,MySQL 会立刻将结果进行提交。 set autocommit=0;
    //1.开始事务            
    begin;/begin work;/start transaction; (三者选一就可以)              
    //2.查询出商品信息               
    select status from t_goods where id=1 for update;              
    //3.根据商品信息生成订单                
    insert into t_orders (id,goods_id) values (null,1);              
    //4.修改商品status为2               
    update t_goods set status=2;              
    //5.提交事务             
    commit;/commit work;   
       上面的查询语句中,我们使用了 selectfor update 的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在 t_goods 表中,id 为 1 的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。
       上面我们提到,使用 selectfor update 会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB 默认行级锁。行级锁都是基于索引的,如果一条 SQL 语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。
悲观锁应用

乐观锁:假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。

  1. 当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。
  2. 当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对
  3. 如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。
技术图片
数据库表设计
task 表有三个字段,分别是 id,value、version

实现
1. 先读 task 表的数据(实际上这个表只有一条记录),得到 version 的值为 versionValue
2. 每次更新 task 表中的 value 字段时,为了防止发生冲突,需要这样操作
update task set value = newValue,version =  versionValue + 1   where version = versionValue;
只有这条语句执行了,才表明本次更新 value 字段的值成功
如假设有两个节点 A 和 B 都要更新 task 表中的 value 字段值,差不多在同一时刻,A 节点和 B 节点从 task 表中读到的 version 值为 2,那么 A 节点和 B 节点在更新 value 字段值的时候,都操作
update task set value = newValue,version = 3 where version = 2;
实际上只有 1 个节点执行该 SQL 语句成功,假设 A 节点执行成功,那么此时 task 表的 version 字段的值是 3,B 节点再操作
update task set value = newValue,version = 3 where version = 2;
这条 SQL 语句是不执行的,这样就保证了更新 task 表时不发生冲突。
乐观锁应用

 

2.MongoDB部分

问题:

问题1:MongoDB的存储引擎以及底层存储机制

问题2:MongoDB的版本变迁既特性

 

回答:

问题1:

存储引擎如下:

  • MMAP:
  • WiredTiger:3.2版本默认存储引擎都改为了wiredtiger,特性为:文档级别锁,解决了锁粒度过大的问题,磁盘数据压缩,删除数据时,数据会立即删除,MongoDB3.0在多线程、批量插入场景下较之于MongoDB2.6有大约4-7倍的增长
  • RocksDB:特性:顺序写入:LSM Tree结构,随机写入转换为顺序写入;速度稳定:和WiredTiger相比,写速度稳定
  • Memory

底层存储机制:空间局部性原理,B树

 

问题2:

MongoDB 3.0特性(2015年):顺序写入:LSM Tree结构,随机写入转换为顺序写入,速度稳定:和WiredTiger相比,写速度稳定

MongoDB 4.0特性(2018年6月):多文档事务支持,4.2版本开始支持分片集群分布式事务

 

3.Redis部分

问题:

问题1:redis支持的数据类型常用使用场景

问题2:redis的存储机制以及常用集群

 

回答

问题1:

redis支持的数据类型主要有string,list,set,zset,hash

redis的常用使用场景

  • 作为热数据的缓存数据库,缓存高频次访问的数据,降低数据库的IO
  • 分布式架构,做session共享
  • 利用zset类型可以做存储排行榜
  • 利用list做简易MQ或存储最新的n个数据

 

问题2:

redis的存储机制有

  • list键:双向链表
  • hash键:字典dict
  • zset键:跳跃表zskiplist
  • ziplist:节省内存空间

常用集群方式有

  • Twitter开发的twemproxy
  • 豌豆荚开发的codis
  • redis官方的redis-cluster

ok

以上是关于python_面试题_DB相关问题的主要内容,如果未能解决你的问题,请参考以下文章

python面试题

十条Python面试题陷阱,看看你是否会中招

python经典面试题:想找工作?这些面试题你会了吗?

python面试题

程序员面试必备的20条Python经典面试题

python的一道面试题 __call__ 的使用.