目录
4、不同的隔离级别,隔离性是如何实现的,为什么不同事物间能够互不干扰?
11、redolog和binlog可以相互替代或者只保留其一吗?
11.1 可以使用binlog替代redolog进行数据恢复吗?
15、Mysql的索引是怎么实现的,联合索引里存放的是什么?
16、mysql数据库中的乐观锁知道吗?在设计表的时候应该注意什么?
17、HTTP协议常用字段, HTTP响应码有哪些?作用是什么? 504知道么?
18、Java里面 System.out.println("Hello World!") 到屏幕显示,发生了什么?
1、一亿个数据找出前K个大的数
维护容量为k的小顶堆,从源数据中取出前k个填充实例化堆,然后对剩下的n-k个数据迭代,对于每个遍历到的数字x,如果x >minValue,用x把minValue替换掉,然后调整堆最小值的位置。时间复杂度 O(K + (n-k)*lgk), 空间复杂度 O(k)。
2、求数组中第k大的数字
使用类似快排的方法,但是只要找到对应位置的值即可,时间复杂度可以达到o(n),最坏时间复杂度o(n^2).
3、排序算法稳定与不稳定是什么意思?
如果数组中的2个数字相等,排序前后他们的前后顺序不改变,则称该排序算法稳定,否则称其为不稳定。
4、不同的隔离级别,隔离性是如何实现的,为什么不同事物间能够互不干扰?
锁 和 MVCC。行锁的种类 在 InnoDB 事务中,行锁通过给索引上的索引项加锁来实现。 这意味着只有通过索引条件检索数据,InnoDB才使用行级锁,否则将使用表锁。行级锁定同样分为两种类型: 共享锁 和 排他锁,以及加锁前需要先获得的意向共享锁和意向排他锁。行锁是在需要的时候才加上 的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
当某事务正在写数据时,其他事务获取不到写锁,就无法写数据,一定程度上保证了事务间的隔离。但前面说, 加了写锁,为什么其他事务也能读数据呢?
前面说到,有了锁,当前事务没有写锁就不能修改数据,但还是能读的,而且读的时候,即使该行数据其他事务已修改且提交,还是可以重复读到同样的值。这就是 MVCC,多版本的并发控制,Multi-Version Concurrency Control。
Innodb 中行记录的存储格式,有一些额外的字段: DATA_TRX_ID和DATA_ROLL_PTR。
- DATA_TRX_ID:数据行版本号。用来标识最近对本行记录做修改的事务 id。
- DATA_ROLL_PTR:指向该行回滚段的指针。该行记录上所有旧版本,在 undo log中都通过链表的形式组织。
5、什么是当前读和快照读?
- 当前读:像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
- 快照读:像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。
6、MVCC的实现原理
MVCC的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的。
每行记录除了我们自定义的字段外,还有数据库隐式定义的等字段:
- DB_TRX_ID: 6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
- DB_ROLL_PTR:7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
- DB_ROW_ID:6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
undo日志:undo log主要分为两种:
- insert undo log:代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃。
- update undo log:事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。
Read View(读视图)
说白了Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)
所以我们知道 Read View主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个Read View读视图,把它比作条件用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。
Read View遵循一个可见性算法,主要是将要被修改的数据的最新记录中的DB_TRX_ID(即当前事务ID)取出来,与系统当前其他活跃事务的ID去对比(由Read View维护),如果DB_TRX_ID跟Read View的属性做了某些比较,不符合可见性,那就通过DB_ROLL_PTR回滚指针去取出Undo Log中的DB_TRX_ID再比较,即遍历链表的DB_TRX_ID(从链首到链尾,即从最近的一次修改查起),直到找到满足特定条件的DB_TRX_ID, 那么这个DB_TRX_ID所在的旧记录就是当前事务能看见的最新老版本
ReadView有几个重要属性:
- **trx_ids: **当前系统活跃(未提交)事务版本号集合。
- low_limit_id: 创建当前 read view 时“当前系统最大事务版本号 +1”。
- up_limit_id: 创建当前read view 时“系统正处于活跃事务 最小版本号”
- **creator_trx_id: **创建当前read view的事务版本号;
首先比较DB_TRX_ID < up_limit_id, 如果小于,则当前事务能看到DB_TRX_ID 所在的记录,如果大于等于进入下一个判断。接下来判断 DB_TRX_ID 大于等于 low_limit_id , 如果大于等于则代表DB_TRX_ID 所在的记录在Read View生成后才出现的,那对当前事务肯定不可见,如果小于则进入下一个判断。
判断DB_TRX_ID 是否在活跃事务之中,trx_ids.contains(DB_TRX_ID),如果在,则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据,我当前事务也是看不见的;如果不在,则说明,你这个事务在Read View生成之前就已经Commit了,你修改的结果,我当前事务是能看见的。
7、RC,RR级别下的InnoDB快照读有什么不同?
正是Read View生成时机的不同,从而造成RC,RR级别下快照读的结果的不同
- 在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照及Read View, 将当前系统活跃的其他事务记录起来,此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见;
- 即RR级别下,快照读生成Read View时,Read View会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的。而早于Read View创建的事务所做的修改均是可见
- 而在RC级别下的,事务中,每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因
总之在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
8、mysql原子性的实现
接着说说原子性。前文有提到 undo log ,回滚日志。隔离性的MVCC其实就是依靠它来实现的,原子性也是。 实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。 当事务对数据库进行修改时,InnoDB会生成对应的 undo log;如果事务执行失败或调用了 rollback,导致事务需要回滚,便可以利用 undo log 中的信息将数据回滚到修改之前的样子。 undo log 属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB 会根据 undo log 的内容做与之前相反的工作。以update操作为例:当事务执行update时,其生成的undo log中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到update之前的状态。
9、mysql持久性的实现
Innnodb有很多 log,持久性靠的是 redo log。持久性肯定和写有关,MySQL 里经常说到的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。
当有一条记录要更新时,InnoDB 引擎就会先把记录写到 redo log(并更新内存),这个时候更新就算完成了。在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。redo log 有两个特点,大小固定,循环写,crash-safe。
InnoDB还提供了缓存,Buffer Pool 中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当读取数据时,会先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中。Buffer Pool 的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时 Buffer Pool 中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
所以加入了 redo log。当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。
redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。 而且这样做还有两个优点:
- 刷脏页是随机 IO,redo log 顺序 IO
- 刷脏页以Page为单位,一个Page上的修改整页都要写;而redo log 只包含真正需要写入的,无效 IO 减少。
10、什么是redolog和binglog?
redo log | Binglog | |
日志类型 | 物理日志:物理日志即数据页中的真实二级制数据,恢复速度快 | 逻辑日志:即sql语句,因需要逐条执行,恢复速度慢 |
存储格式 | innodb存储引擎数据的单位是页,redo log也是基于页进行存储,一个默认16K大小的页中存了很多512Byte的log block,log block的存储格式如下 [log block header 12Byte,log block body 492 Bytes,log block tailer 8 Bytes] | statement:SQL语句的形式 row:记录相关行的每一列的值(官方推荐) |
用途 | 重做数据页 | 数据复制 |
所处层级 | innodb存储引擎中 | 存储引擎的上层,因此不管用什么存储引擎,都可以开启binlog |
11、redolog和binlog可以相互替代或者只保留其一吗?
11.1 可以使用binlog替代redolog进行数据恢复吗?
不可以,innodb利用wal技术进行数据恢复,write ahead logging技术依赖于物理日志进行数据恢复,binlog不是物理日志是逻辑日志,因此无法使用。
11.2 可以只使用redolog而不使用binlog吗?
不可以,redolog是循环写,写到末尾要回到开头继续写,这样的日志无法保留历史记录,无法进行数据复制。
12、为什么redolog和binlog要进行二阶段提交?
首先区分一个概念,commit步骤是属于begin…commit语句中的一个步骤,且是最后一个步骤,两个commit是包含的关系。
如果redo log持久化并进行了提交,而binlog未持久化数据库就crash了,则从库从binlog拉取数据会少于主库,造成不一致。因此需要内部事务来保证两种日志的一致性。
13、阶段提交步骤
prepare:redolog写入log buffer,并fsync持久化到磁盘,在redolog事务中记录2PC的XID,在redolog事务打上prepare标识
commit:binlog写入log buffer,并fsync持久化到磁盘,在binlog事务中记录2PC的XID,同时在redolog事务打上commit标识
其中,prepare和commit阶段所提到的“事务”,都是指内部XA事务,即2PC
14、恢复步骤?
redolog中的事务如果经历了二阶段提交中的prepare阶段,则会打上prepare标识,如果经历commit阶段,则会打上commit标识(此时redolog和binlog均已落盘)。
Step1. 按顺序扫描redolog,如果redolog中的事务既有prepare标识,又有commit标识,就直接提交(复制redolog disk中的数据页到磁盘数据页)
Step2 .如果redolog事务只有prepare标识,没有commit标识,则说明当前事务在commit阶段crash了,binlog中当前事务是否完整未可知,此时拿着redolog中当前事务的XID(redolog和binlog中事务落盘的标识),去查看binlog中是否存在此XID
a. 如果binlog中有当前事务的XID,则提交事务(复制redolog disk中的数据页到磁盘数据页)
b. 如果binlog中没有当前事务的XID,则回滚事务(使用undolog来删除redolog中的对应事务)
15、Mysql的索引是怎么实现的,联合索引里存放的是什么?
通过B+树或者hash表实现的,InnoDB和myisam使用的是b+树,memory支持b+树和hash。在联合索引中存放了联合索引中各个字段的值和主键的值。
16、mysql数据库中的乐观锁知道吗?在设计表的时候应该注意什么?
mysql的乐观锁,和java中的乐观锁相同,默认程序在运行过程中不会发生冲突,如果发生冲突,则放弃这次操作,重新运行,适合于冲突较少的场合。乐观锁在数据库上的实现完全是逻辑的,数据库本身不提供支持,而是需要开发者自己来实现。
设计表的注意事项:
- 慎重选择表名:与表的内容相关
- 关于编码的设定:根据表的内容包含中文或者英文,选择编码的类型
- 关于表引擎的选择
- 关于属性数据类型的选择
- 关于默认值
- 关于多数据库建立:应该把对应的业务放在各自不同的数据库里,而不是所有业务放到一个库里面。
- 关于索引
17、HTTP协议常用字段, HTTP响应码有哪些?作用是什么? 504知道么?
请求消息数据格式
- 请求行:
- 请求方式:get,post
- 请求url
- 请求协议/版本
- 请求头:
- 请求头的名称:请求值
- host:请求的主机
- User-agent:浏览器告诉服务器自己的版本信息,解决浏览器的兼容性问题
- accept:接受响应信息的格式
- accept-language:接受信息的语言
- referer:告诉服务器请求从哪里来,防盗链,数据统计
- connection:连接状况,keep-alive
- 请求空行:空行,分隔请求头和请求体的
- 请求体:封装post请求消息的请求参数的
响应消息的数据格式
- 响应行:
- 协议/版本
- 响应状态码
- 1xx:服务器接受客户端消息,但没有接受完全,等待一段时间后发送1xx
- 2xx:成功
- 3xx:重定向,302(重定向),304(访问缓存)
- 4xx:客户端错误,404(路径没有对应的资源),405:请求方式没有对应的doxxx方法,401(没有权限访问),
- 5xx:服务器错误,500(服务器内部出现异常)
- 响应头
- content-type:服务器告诉客户端本次响应体数据格式以及编码格式
- content-disposition:服务器告诉客户端以什么格式打开响应体数据
- in-line(默认):当前页面
- attachment;filename=xxx:以附件形式打开响应体。文件下载
- Content-Length:响应体的长度
- Server:服务器的信息
- 响应空行
- 响应体
常用状态码:
1xx(临时响应):
- 100: 请求者应当继续提出请求。
- 101(切换协议) 请求者已要求服务器切换协议,服务器已确认并准备进行切换。
2xx(成功):
- 200:正确的请求返回正确的结果,如果不想细分正确的请求结果都可以直接返回200。
- 201:表示资源被正确的创建。比如说,我们 POST 用户名、密码正确创建了一个用户就可以返回 201。
- 202:请求是正确的,但是结果正在处理中,这时候客户端可以通过轮询等机制继续请求
- 203:请求的代理服务器修改了源服务器返回的 200 中的内容,我们通过代理服务器向服务器 A 请求用户信息,服务器 A 正常响应,但代理服务器命中了缓存并返回了自己的缓存内容,这时候它返回 203 告诉我们这部分信息不一定是最新的,我们可以自行判断并处理。
3xx(已重定向)
- 300:请求成功,但结果有多种选择。
- 301:请求成功,但是资源被永久转移。比如说,我们下载的东西不在这个地址需要去到新的地址。
- 303:使用 GET 来访问新的地址来获取资源。
- 304:请求的资源并没有被修改过。
- 308:使用原有的地址请求方式来通过新地址获取资源。
4xx(请求错误)
- 400:请求出现错误,比如请求头不对等。
- 401:没有提供认证信息。请求的时候没有带上 Token 等。
- 403:请求的资源不允许访问。就是说没有权限。
- 404:请求的内容不存在。
- 406:请求的资源并不符合要求。
- 408:客户端请求超时。
- 413:请求体过大。
5xx(服务器错误)
- 500:服务器错误。
- 501:请求还没有被实现。
- 502:网关错误。
- 503:服务暂时不可用。服务器正好在更新代码重启。
- 504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
- 505:请求的 HTTP 版本不支持。
18、Java里面 System.out.println("Hello World!") 到屏幕显示,发生了什么?
将“hello world” 封装入PrintStream,再通过setOut0(PrintStream out)调用本地方法将hello world输出到屏幕上。
19、IO模式和IO多路复用
19.1 直接I/O和缓存I/O
缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,以write为例,数据会先被拷贝进程缓冲区,在拷贝到操作系统内核的缓冲区中,然后才会写到存储设备中。
缓存I/O的write:
直接I/O的write:(少了拷贝到进程缓冲区这一步)
19.2 I/O模式
对于一次IO访问(这回以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的缓冲区,最后交给进程。所以说,当一个read操作发生时,它会经历两个阶段:
- 等待数据准备 (Waiting for the data to be ready)
- 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)
正式因为这两个阶段,linux系统产生了下面五种网络模式的方案:
- 阻塞 I/O(blocking IO):也就是说,内核准备数据和数据从内核拷贝到进程内存地址这两个过程都是阻塞的。
- 非阻塞 I/O(nonblocking IO):特点是用户进程在内核准备数据的阶段需要不断的主动询问数据好了没有,non-blocking IO在kernel还准备数据的情况下会立刻返回。
- I/O 多路复用( IO multiplexing):I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。
- 信号驱动 I/O( signal driven IO)
- 异步 I/O(asynchronous IO):用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
20、面向对象的三大特征?
继承、封装、多态。
- 继承性是指子类拥有父类的全部特征和行为,这是类之间的一种关系。Java 只支持单继承
- 封装是将代码及其处理的数据绑定在一起的一种编程机制,该机制保证了程序和数据都不受外部干扰且不被误用。封装的目的在于保护信息。
- 保护类中的信息,它可以阻止在外部定义的代码随意访问内部代码和数据。
- 隐藏细节信息,一些不需要程序员修改和使用的信息。
- Java 语言的基本封装单位是类
- 面向对象的多态性,即“一个接口,多个方法”。多态性体现在父类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方式。
多态的前提:
- 要有继承关系或实现关系(接口);
- 要有方法重写;
- 要有父类或者父接口引用指向子类`Fu f= new Zi();
多态的优点:
提高了代码的维护性(继承保证);提高了代码的扩展性。多态简化了应用软件的代码编写和修改过程。
多态的两种应用场景
- 使用父类类型作为方法返回值,实现多态,使方法可以返回不同的子类对象
- 父类类型引用作为方法的形参,实现多态,使方法参数的类型更为宽泛 (该父类的任何一个子类均可作为实参传入)