【面试纪录】博彦科技面试

2020-09-07 博彦科技公司三面试题记录一下


文章目录

      • mybatis 的一二级缓存
        • 一级缓存
          • 一级缓存的生命周期有多长?
          • 怎么判断某两次查询是完全相同的查询?
        • 二级缓存
      • mybatis 的索引
          • MyISAM存储引擎:
          • InnoDB存储引擎
          • MEMORY存储引擎
          • MERGE存储引擎
      • redis 和 数据库如何保持一致
          • 数据库与缓存双写情况下导致数据不一致问题
            • 场景一
            • 场景一解决方案
            • 场景二
            • 场景二解决方案
      • 微服务下的分布式事务
          • 什么是事务
          • 什么是分布式事务
          • 分布式事务的应用场景
          • CAP理论
      • ActiveMQ与RabbitMQ的区别
      • 如何保证线程安全
          • 线程安全:
          • 如何保证线程安全
      • sql优化
          • SQL优化的思路:
          • SQL优化的基本原则:
            • 索引的好处:
            • 索引的弊端:
            • 判断是否需要创建索引:
            • 索引语法:

mybatis 的一二级缓存

一级缓存

【面试纪录】博彦科技面试_第1张图片

Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。


一级缓存的生命周期有多长?
  1. MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
  2. 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。
  3. 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。
  4. SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用
怎么判断某两次查询是完全相同的查询?

mybatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询。

  1. 传入的statementId
  2. 查询时要求的结果集中的结果范围
  3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )
  4. 传递给java.sql.Statement要设置的参数值

二级缓存

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。

【面试纪录】博彦科技面试_第2张图片

SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了,如果我们配置了二级缓存就意味着:

  • 映射语句文件中的所有select语句将会被缓存。
  • 映射语句文件中的所欲insert、update和delete语句会刷新缓存。
  • 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
  • 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。

mybatis 的索引

MyISAM存储引擎:

知识来源:丿九尾狸猫关注

不支持事务、也不支持外键,优势是访问速度快,对事务完整性没有 要求或者以select,insert为主的应用基本上可以用这个引擎来创建表

支持3种不同的存储格式,分别是:静态表;动态表;压缩表

静态表:表中的字段都是非变长字段,这样每个记录都是固定长度的,优点存储非常迅速,容易缓存,出现故障容易恢复;
缺点是:占用的空间通常比动态表多(因为存储时会按照列的宽度定义补足空格)ps:在取数据的时候,默认会把字段后面的空格去掉,如果不注意会把数据本身带的空格也会忽略。

动态表:记录不是固定长度的,这样存储的优点是占用的空间相对较少;缺点:频繁的更新、删除数据容易产生碎片,需要定期执行OPTIMIZE
TABLE或者myisamchk-r命令来改善性能

压缩表:因为每个记录是被单独压缩的,所以只有非常小的访问开支

InnoDB存储引擎

该存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM引擎,写的处理效率会差一些,并且会占用更多的磁盘空间以保留数据和索引。
InnoDB存储引擎的特点:支持自动增长列,支持外键约束

MEMORY存储引擎

Memory存储引擎使用存在于内存中的内容来创建表。每个memory表只实际对应一个磁盘文件,格式是.frm。memory类型的表访问非常的快,因为它的数据是放在内存中的,并且默认使用HASH索引,但是一旦服务关闭,表中的数据就会丢失掉。

MEMORY存储引擎的表可以选择使用BTREE索引或者HASH索引,两种不同类型的索引有其不同的使用范围

MERGE存储引擎

Merge存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,merge表本身并没有数据,对merge类型的表可以进行查询,更新,删除操作,这些操作实际上是对内部的MyISAM表进行的。

redis 和 数据库如何保持一致

【面试纪录】博彦科技面试_第3张图片

数据库与缓存双写情况下导致数据不一致问题

知识来源:Simba_hua

场景一

当更新数据时,如更新某商品的库存,当前商品的库存是100,现在要更新为99,先更新数据库更改成99,然后删除缓存,发现删除缓存失败了,这意味着数据库存的是99,而缓存是100,这导致数据库和缓存不一致。

场景一解决方案

这种情况应该是先删除缓存,然后在更新数据库,如果删除缓存失败,那就不要更新数据库,如果说删除缓存成功,而更新数据库失败,那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。

场景二

在高并发的情况下,如果当删除完缓存的时候,这时去更新数据库,但还没有更新完,另外一个请求来查询数据,发现缓存里没有,就去数据库里查,还是以上面商品库存为例,如果数据库中产品的库存是100,那么查询到的库存是100,然后插入缓存,插入完缓存后,原来那个更新数据库的线程把数据库更新为了99,导致数据库与缓存不一致的情况

场景二解决方案

遇到这种情况,可以用队列的去解决这个问,创建几个队列,如20个,根据商品的ID去做hash值,然后对队列个数取摸,当有数据更新请求时,先把它丢到队列里去,当更新完后在从队列里去除,如果在更新的过程中,遇到以上场景,先去缓存里看下有没有数据,如果没有,可以先去队列里看是否有相同商品ID在做更新,如果有也把查询的请求发送到队列里去,然后同步等待缓存更新完成。
这里有一个优化点,如果发现队列里有一个查询请求了,那么就不要放新的查询操作进去了,用一个while(true)循环去查询缓存,循环个200MS左右,如果缓存里还没有则直接取数据库的旧数据,一般情况下是可以取到的。

微服务下的分布式事务

什么是事务

事务是指由一组操作组成的一个工作单元,这个工作单元具有原子性(atomicity)一致性(consistency)隔离性(isolation)持久性(durability)

  • 原子性:执行单元中的操作要么全部执行成功,要么全部失败。如果有一部分成功一部分失败那么成功的操作要全部回滚到执行前的状态。
  • 一致性:执行一次事务会使用数据从一个正确的状态转换到另一个正确的状态,执行前后数据都是完整的。
  • 隔离性:在该事务执行的过程中,任何数据的改变只存在于该事务之中,对外界没有影响,事务与事务之间是完全的隔离的。只有事务提交后其它事务才可以查询到最新的数据。
  • 持久性:事务完成后对数据的改变会永久性的存储起来,即使发生断电宕机数据依然在。
什么是分布式事务

在分布式系统中一次操作由多个系统协同完成,这种一次事务操作涉及多个系统通过网络协同完成的过程称为分布式事务。这里强调的是多个系统通过网络协同完成一个事务的过程,并不强调多个系统访问了不同的数据库,即使多个系统访问的是同一个数据库也是分布式事务,如下图:
【面试纪录】博彦科技面试_第4张图片
另外一种分布式事务的表现是,一个应用程序使用了多个数据源连接了不同的数据库,当一次事务需要操作多个数据源,此时也属于分布式事务,当系统作了数据库拆分后会出现此种情况
上面两种分布式事务表现形式第一种用的最多
【面试纪录】博彦科技面试_第5张图片

分布式事务的应用场景

【面试纪录】博彦科技面试_第6张图片

CAP理论

如何进行分布式事务控制?CAP理论是分布式事务处理的理论基础,了解了CAP理论有助于我们研究分布式事务的处理方案。
CAP理论是:分布式系统在设计时只能在一致性(Consistency)、可用性(Availability)、分区容忍性(Partition Tolerance)中满足两种,无法兼顾三种。
通过下图来理解CAP理论
【面试纪录】博彦科技面试_第7张图片

  • 一致性(Consistency):服务A、B、C三个结点都存储了用户数据, 三个结点的数据需要保持同一时刻数据一致性。
  • 可用性(Availability):服务A、B、C三个结点,其中一个结点宕机不影响整个集群对外提供服务,如果只有服务A结点,当服务A宕机整个系统将无法提供服务,增加服务B、C是为了保证系统的可用性。
  • 分区容忍性(PartitionTolerance):分区容忍性就是允许系统通过网络协同工作,分区容忍性要解决由于网络分区导致数据的不完整及无法访问等问题。
    分布式系统不可避免的出现了多个系统通过网络协同工作的场景,结点之间难免会出现网络中断、网延延迟等现象,这种现象一旦出现就导致数据被分散在不同的结点上,这就是网络分区。

ActiveMQ与RabbitMQ的区别

  1. ActiveMQ/ApolloMQ
      优点:老牌的消息队列,使用Java语言编写。对JMS支持最好,采用多线程并发,资源消耗比较大。如果你的主语言是Java,可以重点考虑。
      缺点:由于历史悠久,历史包袱较多,版本更新很缓慢。集群模式需要依赖Zookeeper实现。最新架构的产品被命名为Apollo,号称下一代ActiveMQ,目前案例较少。
  2. RocketMQ/Kafka   
      优点:专为海量消息传递打造,主张使用拉模式,天然的集群、HA、负载均衡支持。话说还是那句话,适合不适合看你有没有那么大的量。
      缺点:所谓鱼和熊掌不可兼得,放弃了一些消息中间件的灵活性,使用的场景较窄,需关注你的业务模式是否契合,否则山寨变相使用很别扭。除此之外,RocketMQ没有.NET下的客户端可用。RocketMQ身出名门,但使用者不多,生态较小,毕竟消息量能达到这种体量的公司不多,你也可以直接去购买阿里云的消息服务。Kafka生态完善,其代码是用Scala语言写成,可靠性比RocketMQ低一些。
     3. RabbitMQ   
      优点:生态丰富,使用者众,有很多人在前面踩坑。AMQP协议的领导实现,支持多种场景。淘宝的MySQL集群内部有使用它进行通讯,OpenStack开源云平台的通信组件,最先在金融行业得到运用。
      缺点:Erlang代码你Hold得住不? 虽然Erlang是天然集群化的,但RabbitMQ在高可用方面做起来还不是特别得心应手

如何保证线程安全

知识来源:麦克劳林

线程安全:

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

如何保证线程安全
  1. 使用线程安全的类;
  2. 使用synchronized同步代码块,或者用Lock锁;
    由于线程安全问题,使用synchronized同步代码块 原理:当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。 另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
  3. 多线程并发情况下,线程共享的变量改为方法局部级变量;

线程安全线程同步

sql优化

知识来源:星朝

SQL优化的思路:
  1. 优化更需要优化的sql;
  2. 定位优化对象的性能瓶颈:优化前需了解查询的瓶颈是IO还是CPU,可通过PROFILING很容易定位查询的瓶颈。
  3. 明确优化目标;
  4. 从Explain入手;
  5. 多使用profile;
SQL优化的基本原则:
  1. 永远用小结果集驱动大结果集; From子句中sql解析顺序为从右向左,执行时会以最左边的表为基础表循环与右边表数据做笛卡尔积,所以以小结果集驱动能减少循环次数,从而减少对被驱动结果集的访问,从而减少被驱动表的锁定。
  2. 尽可能在索引中完成排序; 排序算法有两种: a.查出排序字段和行指针,排序,再通过行指针获得行数据所需列,返回结果集; b.取出所有排序列数据,在排序缓冲区中排完序直接返回结果集。 索引排序是利用索引的有序性对数据排序的。
  3. 只取出子集需要的colums
  4. 仅仅使用最有效的过滤条件;
  5. 尽可能避免复杂的Join和子查询;
索引的好处:
  1. 提高数据检索效率,降低数据库的IO成本。
  2. 降低数据排序成本:要求排序字段和索引键字段一致。
  3. 降低数据分组成本:因为分组之前会先排序,同意如果分组字段与索引字段一致,会降低分组消耗的成本。
索引的弊端:
  1. 索引是独立于基础数据的数据库对象,因此它会占用存储空间。
  2. 数据新增、更新会导致索引的同步更新,所以会增加数据新增、更新所消耗的成本。
判断是否需要创建索引:
  1. 较为频繁的作为查询条件的字段需要创建索引;
  2. 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
  3. 更新非常频繁的字段不适合创建索引;
  4. 不会出现在where子句中的字段不要创建索引;
索引语法:
  1. 唯一索引
ALTER TABLE tableName ADD UNIQUE indexName (column);     
CREATE UNIQUE INDEX indexName ON tableName (column);
  1. 普通索引
 ALTER TABLE tableName ADD INDEX indexName(column);     
 CREATE INDEX indexName ON tableName(column);
  1. 主键索引
 ALTER TABLE tableName ADD PRIMARY KEY (column);
  1. 全文索引
 ALTER TABLE tableName ADD FULLTEXT (column);
  1. 组合索引
ALTER TABLE tableName ADD INDEX indexName(col1,col2,...);     

你可能感兴趣的:(_,面試,分布式,面试,mysql,数据库)