前言

题目答案总结并非标准,仅供参考,如果有错误或者更好的见解,欢迎留言讨论!

b29285668bd5799193f3927d910f317e.png

事务

1、什么是事务?事务的特性(ACID)

什么是事务:事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功,要么失败)。

事务特性分为四个:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持续性(Durability)简称ACID。

1、原子性:事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做。

2、一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。

3、隔离性:一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。

4、持久性:也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。

2、事务的隔离级别有几种,最常用的隔离级别是哪两种?

并发过程中会出现的问题:

  • 丢失更新:是不可重复读的特殊情况。如果两个事物都读取同一行,然后两个都进行写操作,并提交,第一个事物所做的改变就会丢失。

  • 脏读:一个事务读取到另一个事务未提交的更新数据。

  • 幻读也叫虚读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。

  • 不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任。

事务的隔离级别有4种:

1、未提交读(Read uncommitted)

  • 定义:就是一个事务读取到其他事务未提交的数据,是级别最低的隔离机制。

  • 缺点:会产生脏读、不可重复读、幻读。

2、提交读(Read committed)

  • 定义:就是一个事务读取到其他事务提交后的数据。Oracle默认隔离级别。

  • 缺点:会产生不可重复读、幻读。

3、可重复读(Repeatable read)

  • 定义:就是一个事务对同一份数据读取到的相同,不在乎其他事务对数据的修改。MySQL默认的隔离级别。

  • 缺点:会产生幻读。

4、串行化(Serializable)

  • 定义:事务串行化执行,隔离级别最高,牺牲了系统的并发性。

  • 缺点:可以解决并发事务的所有问题。但是效率地下,消耗数据库性能,一般不使用。

772c869834cc3624d6041fe355cf9db0.png

缓存

3、分布式缓存的典型应用场景?

  • 页面缓存,用来缓存Web页面的内容片段,包括HTML、CSS 和图片等,多应用于社交网站等。

  • 应用对象缓存,缓存系统作为ORM框架的二级缓存对外提供服务,目的是减轻数据库的负载压力,加速应用访问。

  • 状态缓存,缓存包括Session会话状态及应用横向扩展时的状态数据等,这类数据一般是难以恢复的,对可用性要求较高,多应用于高可用集群。

  • 并行处理,通常涉及大量中间计算结果需要共享。

  • 事件处理,分布式缓存提供了针对事件流的连续查询(continuous query)处理技术,满足实时性需求。

  • 极限事务处理,分布式缓存为事务型应用提供高吞吐率、低延时的解决方案,支持高并发事务请求处理,多应用于铁路、金融服务和电信等领域。

数据库

4、MongoDB与Mysql的区别?

两种数据库的区别:

  • 传统的关系型数据库,数据是以表单为媒介进行存储的。

  • 相比较Mysql,Mongodb以一种直观文档的方式来完成数据的存储。

Mongodb的鲜明特征:

  • 自带GirdFS的分布式文件系统,这也为Mongodb的部署提供了很大便利。

  • Mongodb内自建了对map-reduce运算框架的支持,虽然这种支持从功能上看还算是比较简单的,相当于MySQL里GroupBy功能的扩展版,不过也为数据的统计带来了方便。

  • Mongodb在启动后将数据库中得数据以文件映射的方式加载到内存中,如果内存资源相当丰富的话,这将极大的提高数据库的查询速度。

Mongodb的优势:

  • Mongodb适合那些对数据库具体格式不明确或者数据库数据格式经常变化的需求模型,而且对开发者十分友好。

  • Mongodb官方就自带一个分布式文件系统,Mongodb官方就自带一个分布式文件系统,可以很方便的部署到服务器机群上。

Mongodb的缺陷:

  • 事务关系支持薄弱。这也是所有NoSQL数据库共同的缺陷,不过NoSQL并不是为了事务关系而设计的,具体应用还是很需求。

  • 稳定性有些欠缺

  • 方便开发者的同时,对运维人员提出了更高的要求。

Mongodb的应用场景:

  • 表结构不明确且数据不断变大:MongoDB是非结构化文档数据库,扩展字段很容易且不会影响原有数据。内容管理或者博客平台等,例如圈子系统,存储用户评论之类的。

  • 更高的写入负载:MongoDB侧重高数据写入的性能,而非事务安全,适合业务系统中有大量“低价值”数据的场景。本身存的就是json格式数据。例如做日志系统。

  • 数据量很大或者将来会变得很大:Mysql单表数据量达到5-10G时会出现明细的性能降级,需要做数据的水平和垂直拆分、库的拆分完成扩展,MongoDB内建了sharding、很多数据分片的特性,容易水平扩展,比较好的适应大数据量增长的需求。

  • 高可用性:自带高可用,自动主从切换(副本集):

不适用的场景:

  • MongoDB不支持事务操作,需要用到事务的应用建议不用MongoDB。

  • MongoDB目前不支持join操作,需要复杂查询的应用也不建议使用MongoDB。

  • 在带“_id”插入数据的时候,MongoDB的插入效率其实并不高。如果想充分利用MongoDB性能的话,推荐采取不带“_id”的插入方式,然后对相关字段作索引来查询。

关系型数据库和非关系型数据库的应用场景对比:

关系型数据库适合存储结构化数据,如用户的帐号、地址:

  • 这些数据通常需要做结构化查询,比如join,这时候,关系型数据库就要胜出一筹。

  • 这些数据的规模、增长的速度通常是可以预期的。

  • 事务性、一致性。

NoSQL适合存储非结构化数据,如文章、评论:

  • 这些数据通常用于模糊处理,如全文搜索、机器学习。

  • 这些数据是海量的,而且增长的速度是难以预期的。

  • 根据数据的特点,NoSQL数据库通常具有无限(至少接近)伸缩性。

  • 按key获取数据效率很高,但是对join或其他结构化查询的支持就比较差。

5、Mysql索引相关问题。

1)什么是索引?

  • 索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据。

2)索引具体采用的哪种数据结构呢?

  • 常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,通常使用的是InnoDB引擎,默认的是B+树。

3)InnoDb内存使用机制?

Innodb体系结构如图所示:

03e1dc097cc205e55ad22712239ead1d.png

Innodb关于查询效率有影响的两个比较重要的参数分别是innodb_buffer_pool_size,innodb_read_ahead_threshold:

  • innodb_buffer_pool_size指的是Innodb缓冲池的大小,该参数的大小可通过命令指定innodb_buffer_pool_size 20G。缓冲池使用改进的LRU算法进行管理,维护一个LRU列表、一个FREE列表,FREE列表存放空闲页,数据库启动时LRU列表是空的,当需要从缓冲池分页时,首先从FREE列表查找空闲页,有则放入LRU列表,否则LRU执行淘汰,淘汰尾部的页分配给新页。

  • innodb_read_ahead_threshold相对应的是数据预加载机制,innodb_read_ahead_threshold 30表示的是如果一个extent中的被顺序读取的page超过或者等于该参数变量的,Innodb将会异步的将下一个extent读取到buffer pool中,比如该参数的值为30,那么当该extent中有30个pages被sequentially的读取,则会触发innodb linear预读,将下一个extent读到内存中;在没有该变量之前,当访问到extent的最后一个page的时候,Innodb会决定是否将下一个extent放入到buffer pool中;可以在Mysql服务端通过show innodb status中的Pages read ahead和evicted without access两个值来观察预读的情况:Innodb_buffer_pool_read_ahead:表示通过预读请求到buffer pool的pages;Innodb_buffer_pool_read_ahead_evicted:表示由于请求到buffer pool中没有被访问,而驱逐出内存的页数。

可以看出来,Mysql的缓冲池机制是能充分利用内存且有预加载机制,在某些条件下目标数据完全在内存中,也能够具备非常好的查询性能。

4)B+ Tree索引和Hash索引区别?

  • 哈希索引适合等值查询,但是无法进行范围查询。

  • 哈希索引没办法利用索引完成排序。

  • 哈希索引不支持多列联合索引的最左匹配规则。

  • 如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题。

5)B+ Tree的叶子节点都可以存哪些东西吗?

  • InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值。

6)这两者有什么区别吗?

  • 在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。

7)聚簇索引和非聚簇索引,在查询数据的时候有区别吗?

  • 聚簇索引查询会更快,因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询。

8)主键索引查询只会查一次,而非主键索引需要回表查询多次(这个过程叫做回表)。是所有情况都是这样的吗?非主键索引一定会查询多次吗?

覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。

如,表covering_index_sample中有一个普通索引 idx_key1_key2(key1,key2)。当我们通过SQL语句:select key2 from covering_index_sample where key1 = 'keytest';的时候,就可以通过覆盖索引查询,无需回表。

9)在创建索引的时候都会考虑哪些因素呢?

一般对于查询概率比较高,经常作为where条件的字段设置索引。

10)在创建联合索引的时候,需要做联合索引多个字段之间顺序,这是如何选择的呢?

在创建多列索引时,我们根据业务需求,where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。

11)你知道在MySQL 5.6中,对索引做了哪些优化吗?

  • 索引条件下推:“索引条件下推”,称为 Index Condition Pushdown (ICP),这是MySQL提供的用某一个索引对一个特定的表从表中获取元组”,注意我们这里特意强调了“一个”,这是因为这样的索引优化不是用于多表连接而是用于单表扫描,确切地说,是单表利用索引进行扫描以获取数据的一种方式。

  • 例如有索引(key1,key2),SQL语句中where key1 = 'XXX' and key2 like '%XXX%':

  • 如果没有使用索引下推技术,MySQL会通过key1 = 'XXX'从存储引擎返回对应的数据至MySQL服务端,服务端再基于key2 like 判断是否符合条件。

  • 如果使用了索引下推技术,MySQL首先返回key1='XXX'的索引,再根据key2 like 判断索引是否符合条件,如果符合则通过索引定位数据,如果不符合则直接reject掉。有了索引下推优化,可以在有like条件查询的情况下,减少回表次数。

12)如何知道索引是否生效?

explain显示了MySQL如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。使用方法,在select语句前加上explain就可以了。

13)那什么情况下会发生明明创建了索引,但是执行的时候并没有通过索引呢?

在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。这个成本最低的方案就是所谓的执行计划。优化过程大致如下:

  • 根据搜索条件,找出所有可能使用的索引。

  • 计算全表扫描的代价。

  • 计算使用不同索引执行查询的代价。

  • 对比各种执行方案的代价,找出成本最低的那一个。

14)为什么索引结构默认使用B+Tree,而不是Hash,二叉树,红黑树?

  • B+tree是一种多路平衡查询树,节点是天然有序的,非叶子节点包含多个元素,不保存数据,只用来索引,叶子节点包含完整数据和带有指向下一个节点的指针,形成一个有序链表,有助于范围和顺序查找。因为非叶子节点不保存数据,所以同样大小的磁盘页可以容纳更多的元素,同样能数据量的情况下,B+tree相比B-tree高度更低,因此查询时IO会更少。

  • B-tree不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低;

  • Hash索引底层是基于哈希表,就是以key-value存储数据的结构,多个数据在存储关系上是没有任何顺序关系的。只适合等值查询,不适合范围查询,而且也无法利用索引完成排序,不支持联合索引的最左匹配原则,如果有大量重复键值的情况下,哈希索引效率会很低,因为存在哈希碰撞。

  • 二叉树:树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),并且IO代价高。

  • 红黑树:树的高度随着数据量增加而增加,IO代价高。

6、如何优化MySQL?

MySQL优化大致可以分为三部分:索引的优化、SQL语句优化和表的优化

索引优化可以遵循以下几个原则:

  • 联合索引最左前缀匹配原则

  • 尽量把字段长度小的列放在联合索引的最左侧(因为字段越小,一页存储的数据量越大,IO性能也就越好)

  • order by 有多个列排序的,应该建立联合索引

  • 对于频繁的查询优先考虑使用覆盖索引

  • 前导模糊查询不会使用索引,比如说Like '%aaa%'这种

  • 负向条件不会使用索引,如!=,<>,not like,not in,not exists

  • 索引应该建立在区分度比较高的字段上 一般区分度在80%以上的时候就可以建立索引,区分度可以使用 count(distinct(列名))/count(*)

  • 对于where子句中经常使用的列,最好设置索引

SQL语句优化,可以通过explain查看SQL的执行计划,优化语句原则可以有:

  • 在where和order by涉及的列上建立合适的索引,避免全表扫描

  • 任何查询都不要使用select * ,而是用具体的字段列表代替

  • 多表连接时,尽量小表驱动大表,即小表join大表

  • 用exists代替in

  • 尽量避免在where字句中对字段进行函数操作

数据库表优化

  • 表字段尽可能用not null

  • 字段长度固定表查询会更快

  • 数据库大表按照时间或者一些标志拆分成小表

  • 水平拆分:将记录散列到不同的表中,每次从分表查询

  • 垂直拆分:将表中的大字段单独拆分到另一张表,形成一对一的关系

7、为什么任何查询都不要使用SELECT *?

  • 多出一些不用的列,这些列可能正好不在索引的范围之内(索引的好处不多说)select * 杜绝了索引覆盖的可能性,而索引覆盖又是速度极快,效率极高,业界极为推荐的查询方式。(索引覆盖)

  • 数据库需要知道 * 等于什么 = 查数据字典会增大开销(记录数据库和应用程序元数据的目录)。

  • 不需要的字段会增加数据传输的时间,即使 mysql 服务器和客户端是在同一台机器上,使用的协议还是 tcp,通信也是需要额外的时间。

  • 大字段,例如很长的 varchar,blob,text。准确来说,长度超过 728 字节的时候,会把超出的数据放到另外一个地方,因此读取这条记录会增加一次 io 操作。(mysql innodb)

  • 影响数据库自动重写优化SQL(类似 Java 中编译 class 时的编译器自动优化) 。(Oracle)

  • select * 数据库需要解析更多的 对象,字段,权限,属性相关,在 SQL 语句复杂,硬解析较多的情况下,会对数据库造成沉重的负担。

  • 额外的 io,内存和 cpu 的消耗,因为多取了不必要的列。

  • 用 SELECT * 需谨慎,因为一旦列的个数或顺序更改,就有可能程序执行失败。

多线程

Java实现多线程有几种方式?

有三种方式:

  • 继承Thread类,并重写run方法。

  • 实现Runnable接口,并重写run方法。

  • 实现Callable接口,并重写run方法,并使用FutureTask包装器。

线程的生命周期

0da7ce70b3a866e8089e6d29a0125278.png

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

  • 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

  • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

  • 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

start()方法和run()方法的区别?

  • start()方法会使得该线程开始执行,java虚拟机会去调用该线程的run()方法。

  • 通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。

  • run()方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到多线程的目的。

Runnable接口和Callable接口的区别?

  • Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已。

  • Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

  • 这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable + Future/FutureTask却可以获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非常有用。

volatile关键字

volatile基本介绍:volatile可以看成是synchronized的一种轻量级的实现,但volatile并不能完全代替synchronized,volatile有synchronized可见性的特性,但没有synchronized原子性的特性。可见性即用volatile关键字修饰的成员变量表明该变量不存在工作线程的副本,线程每次直接都从主内存中读取,每次读取的都是最新的值,这也就保证了变量对其他线程的可见性。另外,使用volatile还能确保变量不能被重排序,保证了有序性。

当一个变量定义为volatile之后,它将具备两种特性:

  • ①保证此变量对所有线程的可见性:当一条线程修改了这个变量的值,新值对于其他线程可以说是可以立即得知的。Java内存模型规定了所有的变量都存储在主内存,每条线程还有自己的工作内存,线程的工作内存保存了该线程使用到的变量在主内存的副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读取主内存中的变量。

  • ②禁止指令重排序优化:

volatile boolean isOK = false;
//假设以下代码在线程A执行
A.init();
isOK=true;
//假设以下代码在线程B执行
while(!isOK){
sleep();
}
B.init();
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐