天天看点

Java面试题 第五部分 数据库相关

Redis

1 Redis有哪些数据结构?底层的编码有哪些?有序链表采用了哪些不同的编码?

数据结构

  1. String(Key,Value) function:get,set,del
  2. List(Key,List) function:rpush,lrange,lindex,lpop
  3. Set(Key,Set) function:sadd,smembers,sismember,srem
  4. Hash(Key,Map) function:hset,hget(key,mapkey),hgetall,hdel
  5. Zset(Key,Set) function:zadd,zrange,zrangebyscore,zrem (Zset和Set的区别在于每个Set每个元素都有一个分值,实现有序set的目的)

底层编码

Redis数据编码:

defineOBJ_ENCODING_RAW 0

有序链表采用的编码:ziplist和skiplist

2 redis的hash数据结构最多能存储多少个元素

Every hash can store up to 2^32 - 1 field-value pairs (more than 4 billion).

3 Redis的高可用方案(todo)

4 什么是一致性hash算法

key个数为2的32次方的圆环
  1. 求出每个服务器的IP获得hash值,将其配置到一个 0~2^n 的圆环上(n通常取32)称为Node。
  2. 用同样的方法求出待存储对象的主键 hash值,也将其配置到这个圆环上,然后从数据映射到的位置开始顺时针查找,找到第一个Node进行存储。
    Java面试题 第五部分 数据库相关
    节点增删,需要对当前操作节点和逆时针找到的第一个节点之间的数据进行数据迁移 节点雪崩,对热点Node添加虚拟节点,减少节点的压力

5 Redis为什么使用单线程模型,为什么不用多线程达到高并发的目的

区别并发和并行的关系

单线程减少了线程上下文的切换&采用IO多路复用的策略,提升了读写效率

6 Redis数据热点问题

如何发现数据热点

(1)数据访问量日志记录进行推算

(2)利用redis4.x自身特性,LFU机制发现热点数据(LFU:最近最少使用算法,每个对象包含24bit空间记录访问信息,高16位访问时间,低8位访问频次)

数据热点问题的解决

(1)请求进入队列,等待热点redis节点重建完成 (2)添加分布式锁,只允许一个线程访问db资源 (3)主从分离,主节点负责读写,从节点负责读操作

7 redis实现的消息队列

两种消息队列的实现方式

  1. redis支持key-List存储,通过lpush对list添加,lpop从list读取,完成消息队列的功能,blpop实现阻塞读取的作用,队列为空的时候阻塞等待
  2. redis支持发布订阅模型,通过主题发布数据,通过订阅获取发布的数据

两者的区别

lpush-lpop:1对1(pop一次数据就没了) 发布订阅:1对多

lpush-lpop:拉模式 发布订阅:推模式

Mysql

1 mysql存储引擎

默认存储引擎

v5.1之前是MyISAM,V5.1之后是MyISAN

存储引擎的分类

MyISAM存储引擎:静态型、动态型、压缩型存储结构,并发插入的表锁(针对select量大的情况)

InnoDB存储引擎:事务,自增列,外键,mvcc,行锁

MEMORY存储引擎:数据存储在内存,表结构存储磁盘frm文件,hash索引,数据安全性不高

2 数据库垂直和水平拆分依据(单表数据量多大拆)

通俗理解:水平拆分行,行数据拆分到不同表中 (表记录条数太多)垂直拆分列,表数据拆分到不同表中,单表数据达到千万级(表记录复杂,每条记录内容太多)

3 Mysql的事务操作问题和事务隔离级别

事务操作读写不一致问题问题

脏读

一个事务对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。(一读一修改)

不可重复读

一个事务多次读同一数据,(前后读取结果不一致),针对单行数据变化产生的错误。(多读一修改)

幻读

例如事务在插入已经检查过不存在的记录时,发生数据插入的冲突。针对的数据行数发生的变化。(多读一增删)

事务隔离级别

READ UNCOMMITTED(未提交读):事务修改使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(DirtyRead)。

READ COMMITTED(提交读):一个事务开始时,只能“看见”已经提交的事务所做的修改。不可重复读。

REPEATABLEREAD(可重复读):同一个事务中多次读取相同的数据返回的结果是一样的。其避免了脏读和不可重复读问题,但幻读依然存在。

SERIALIZABLE(可串行化):通过强制事务串行执行,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题

隔离级别原理 (todo)

4 数据库主从同步数据一致性如何解决

  1. 半同步复制办法就是等主从同步完成之后,等主库上的写请求再返回,这就是常说的“半同步复制”。

    优点:利用数据库原生功能,比较简单 缺点:主库的写请求时延会增长,吞吐量会降低

  2. 数据库中间件(读写直接请求中间件,中间件承担请求路由的作用)

    1)所有的读写都走数据库中间件,通常情况下,写请求路由到主库,读请求路由到从库

    2)记录路由到写库的key,主从同步时间窗口内(假设是500ms),如果有读请求访问就把这个key上的读请求路由到主库。

    3)在主从同步时间过完后,对应key的读请求继续路由到从库。

半同步复制的原理&同步方法

  1. 基于SQL语句的复制(statement-based replication, SBR):sql语句写入binllog,binlog小,从服务器版本可能大于主服务器,行锁更多
  2. 基于行的复制(row-based replication, RBR): 每一条行数据写入binlog,数据安全可靠,可以采用多线程复制,binlog文件大 混合模式复制(mixed-based replication, MBR);
  3. 混合模式 12

主从延时长的优化方案

mysql通过主从架构实现读写分离,数据量大高并发场景下主从同步延时较长,原因在于mysql使用单线程重放Relaylog,通过多线程重放可以提升效率,但是多线程操作不能保证请求的顺序性,所以多线程的操作的前提是请求的隔离性,请求在不同的库上执 行,互相之间不存在顺序影响,相同库上的写操作使用相同线程,不同库上的写操作使用不同的线程(针对多库情况) 针对单库多表的情况,mysql5.7开始,将可以同步并行执行的事务分组,同组事务可以并行执行,mysqlbinlog中可以发现在原始的binlog中新增了两个字段,分别为lastcommited(上次提交事务编号)和sequence_number

5 什么叫做当前读&快照读?

在可重复读级别下,快照读是通过MVVC(多版本控制)和undo log来实现的,当前读是通过加record lock(记录锁)和gap lock(间隙锁)来实现的。
  1. 快照读(snapshot read) 简单的select操作(不包括 select … lock in share mode, select … for update)
  2. 当前读读(current read) select … lock in share mode&select … for update&insert&update&delete

6 写sql

有学生成绩表(存在重复数据)

问题1:删除重复数据,保留ID最小的记录

问题2:计算每个学生的全学科平均分

找出重复数据的最小id

(A) select min(peopleid) from people group by peopleName having count(peopleName)>1

找出全部存在重复数据的所有记录的Name

(B) select peopleName from people group by peopleName having count(peopleName)>1

删除除去id最小记录的全部重复数据

© delete from people where peoName in B and peopleId not in (A)

算平均数

select name,avg(score) from(slect * from A left join C where A.Name =C.Name) group by Name

事务

事务

1 事务特性以及实现原理

ACID

Atomic:原子性:成功或者失败 实现原理:回滚日志(undo log) Duration:持久性:一旦事务被提交,那么数据一定会被写入到数据库中并持久存储起来(redo log)

Isolation:隔离性:事务的隔离级别,并发控制机制(锁/时间戳/多版本/快照) Consistency:一致性:更改便持久的保存在数据库之中,并不会被回滚,前三个都是为了保证最终的一致性

CAP

C:Consistency一致性

A:Availability可用性

P:Partition tolerance分区容错性

一致性解决方案:串行化,单个数据集(单个数据集意味着一旦数据丢失就是导致可用性降低的问题)

可用性解决方案:冗余副本,故障自动迁移(冗余就会产生一致性问题)

分区容错性解决方案:可扩展,机器扩容增减不影响数据一致性

CA: 一致性和可用性,为什么不可能同时成立

因为可能通信失败(即出现分区容错)。如果保证 G2 的一致性,那么 G1 必须在写操作时,锁定 G2 的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2 不能读写,没有可用性不。如果保证 G2 的可用性,那么势必不能锁定 G2,所以一致性不成立。 综上所述,G2 无法同时做到一致性和可用性。系统设计时只能选择一个目标。如果追求一致性,那么无法保证所有节点的可用性;如果追求所有节点的可用性,那就没法做到一致性。

因为AC只能满足一个(C[单库水平切分] A[双主库同步高可用],一般采用的方案还是说AP+最终一致性

分布式事务(多数据库情况)

在同一个数据库上的事务操作叫做单机事务,在不同数据库上的事务操作叫做分布式事务

举例:余额、订单、流水可能分布在不同的数据库上,甚至不同的数据库实例上,此时就不能用数据库原生事务来保证数据的一致性了。

分布式事务解决方案

  1. 添加补偿事务,即事务本身的逆向操作,在一系列事务的组合操作存在某一事务操作失败的情况下,对已经执行成功事务执行逆操作补偿事务

    问题:

    补偿事务需要针对每一个事务单独编写,不具有通用性;补偿事务本身也可能会失败;业务流程复杂时会有很多if/else逻辑判断

  2. 后置提交优化:事务先执行,最后一起提交,curd执行阶段耗时较长,commit阶段时间短,curd阶段有问题则直接终止事务,全部不再提交。产生不一致的问题转移到提交阶段,因为提交阶段时间短,也就是说,不一致情况发生概率减小。

    问题:

    因为提交 commit阶段延时到最慢事务的执行时间截止,所以整体的数据库连接时间变长,减少了系统吞吐量。

  3. 两阶段提交(分布式RPC调用环境下的分布式事务) 它通过引入一个协调者(Coordinator)来统一掌控所有参与者(Participant)的操作结果,并指示它们是否要把操作结果进行真正的提交(commit)或者回滚(rollback)

    问题:

    (1)某一个参与者回复消息之前,所有参与者以及协调者都处于阻塞状态;

    (2)在协调者发出消息之前,所有参与者都处于阻塞状态;]

  4. 在6的基础上添加超时机制

10 数据库不同维度的查询请求如何满足,采用冗余数据后如何保证正反数据的一致性

互联网数据量大的业务场景,常常: 使用水平切分来降低单库数据量 使用数据冗余的反范式设计来满足不同维度的查询需求

冗余数据三种方案:

(1)服务同步双写法能够很容易的实现数据冗余

(2)为了降低时延,可以优化为服务异步双写法

(3)为了屏蔽“冗余数据”对服务带来的复杂性,可以优化为线下异步双写法

保证数据一致性的方案:

(1)最简单的方式,线下脚本扫全量数据比对

(2)提高效率的方式,线下脚本扫增量数据比对

(3)最实时的方式,线上检测“消息对”

索引

1 数据库索引

什么是索引

索引是对数据库表中一列或多列的值进行排序的一种结构

_使用索引的原因)

加快对表中记录的查查找找或排排序序

索引的字段选择

表的主键,外键,非外键的连接字段(用于关联查询的字段),枚举类型的数据(有限的变化区间),数值类型的数据

2 聚集索引和非聚集索引

区别

物理内存连续性&表能创建个数 读取聚集快,增删聚集慢,大数据集合查询,范围查询优

1、聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个,这个跟没问题没差别,一般人都知道。

2、聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续,这个大家也都知道。

聚集索引像拼音查字典,拼音的顺序和汉字在字典中存在的位置顺序保持一致,A找到啊,啊在第一页第一个,B找到比,比在第10页,a在b前,a在比前非聚集索引像是笔画查字典,笔画靠前不一定笔画对应的汉字也靠前 聚集索引对应的叶子节点存放最终数据 非聚集索引的叶子节点存放的对应数据行的内存地址和索引的字段,不包含数据行的全部数据

使用场景

使用聚集索引的查询效率要比非聚集索引的效率要高,但是如果需要频繁去改变聚集索引的值,写入性能并不高,因为需要移动对应数据的物理位置。 非聚集索引在查询的时候可以的话就避免二次查询,这样性能会大幅提升。

如何解决非聚集索引的二次查询问题(非聚查主键,聚集主键查内容)

复合索引(覆盖索引)多列索引 PS:在非聚集索引中可以直接查到非聚集索引的索引列内容

非聚集索引分类

普通索引:最基本的索引,无限制索引。

唯一索引:索引列的值唯一允许有空值。

主键索引:不允许有空值的唯一索引。

全文索引:仅可用于 MyISAM表,针对较大的数据,生成全文索引很耗时好空间。

组合索引:多列索引的组合,遵循”最左前缀“原则。

复合索引的最左前缀原则(判断使用index&ref(扫全索引)查询)

mysql创建复合索引的规则是首先会对复合索引的最左边的,也就是第一个name字段的数据进行排序,在第一个字段的排序基础上,然后再对后面第二个的cid字段进行排序。其实就相当于实现了类似 order by name cid这样一种排序规则。

1.索引左前缀性的第一层意思:必须用到索引的第一个字段。

2. 索引前缀性的第二层意思:对于索引的第一个字段,用like时左边必须是固定值,通配符只能出现在右边。

3.索引前缀性的第三层意思:如果在字段前加了函数,则索引会被抑制 (a,b,c) 查询(a,c)只有a走索引,c需要走index扫描

3 数据库索引,底层是怎样实现的(todo)

4 索引有什么用?如何建索引?(加快查询效率)

  1. 选择区分度高的字段作为 索引 字段
  2. 范围 ,条件不明确的 有索引 速度也会很慢
  3. 索引字段 不能 通过 *10 avg() 等公式计算
  4. 最左前缀 --联合索引 (a,b,c,d)

5 存在慢查询日志但是线上查询explain发现走索引的原因?

1)数据后置处理groupby

2)返回的数据集太大

3)使用多列索引的查询语句

6 什么叫做倒排索引

由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。全文检索就是倒排索引。

7 一条SQL语句执行得很慢的原因

(1)大多数情况是正常的,只是偶尔会出现很慢的情况。
  1. 数据库在刷新脏页 redo log打满,请求等待redo log同步磁盘完成,任何一条sql都有可能
  2. 语句涉及到的表加锁了(mysql可以使用showprocesslist)
(2)在数据量不变的情况下,这条SQL语句一直以来都执行的很慢。
  1. 没用到索引(字段没有索引||字段有索引,但却没有用索引)

缓存

1 缓存失效如何解决?

  1. 大面积的缓存key失效(缓存的时间分散分布)
  2. 热点key失效(加锁重构缓存,备份缓存,缓存降级提示等待重试)

2 缓存问题(击穿和雪崩都是缓存失效问题)

缓存穿透

缓存和数据库都查询不到这条数据

缓存中存储这些空数据的key&布隆过滤器

缓存击穿(实质上是热点数据的缓存失效问题)

大量的请求同时查询一个key时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面,第一个查询数据的请求上使用一个互斥锁来锁住它

缓存雪崩(缓缓存存大大规规模模失失效效问问题题)

某一时刻发生大规模的缓存失效的情况

使用集群缓存,保证缓存服务的高可用,缓存失效时间分散

热点数据集中失效(就就是是缓缓存存击击穿穿问问题题)

热点的数据来说,当缓存失效以后会存在大量的请求 备份缓存&缓存降级&重构加锁

数据库和Redis数据一致性问题

数据库和redis数据一致性问题(分布式多节点环境 & 单机环境)

单机场景下:写库成功写缓存失败导致数据不一致

解决方案:读缓存再读库,删缓存写库再写缓存

分布式环境:写读并发,
  1. 目标更新数据A为B
  2. 目标读取数据A 1删缓缓存A——2读取缓存(null)——2读取数据库(A)——1修改数据库(B)——1更新缓存(B)——2更新缓存(A) 导致缓存数据库不一致最终库中存B,缓存存A

    解决方案:采用队列同步并发操作,根据ID配置队列,保证单个数据的查询修改行为是同步的,

    优化点,缓存未命中多个查询时,后续的查询不进入队列,自旋等待队列其他查询任务刷缓存

小结(数据库之间的对比)

1 Nosql和关系型数据库的区别 (存储方式表&文档-图-键值对,存储是否结构化,存储扩展方式,事务规范,性能)

1.目标:应付超大规模,超大流量以及高并发的场景

2.存储方式(关系型-表格型,Nosql-数据集中,就像文档、键值对或者图结构)

3.存储结构(关系型-结构化数据,Nosql-动态结构,非结构化数据)

4.存储规范(关系型-数据分割到最小的关系表,Nosql-平面数据集,存在重复)

5.存储扩展(关系型-提升节点机器性能,Nosql-分布式集群规模扩张)

6.查询方式(关系型-结构化sql查询,Nosql-非结构化查询语言unql)

7.事务(关系型-ACID原子一致隔离持久原则,Nosql-Base原则基本可用柔性一致)

8.性能 (关系型-海量数据读写性能差,Nosql-内存级key-value效率高)

ps:图数据库:Neo4J ·节点与边线可以被赋予属性(键-值对); ·只有边线能够与类别相关联,例如“KNOWS”; ·边线可以指定为有指向或无指向。

2 solr和mongodb的区别,存数据为什么不用solr?

都可以做非结构化文档存储。mongodb是典型的nosql,相对于mysql等传统的关系型数据库出现的,侧重于存储非结构化数据,基于这些非结构化数据提供一些简单的查询。 solr是典型的搜索系统,支持结构化数据的搜索,也支持非结构化数据的搜索,侧重点在于搜索。
我的理解:solr是以luence为基础的搜索引擎,solr中存储的数据Field会作为索引项存在,但是作为存储项,很多并不需要作为索引项,仅作存储展示使用,所以存数据不适合使用solr

3 Redis和memcache有什么区别?Redis为什么比memcache有优势?(存储方式,存储格式,集群管理方式)

  1. 存储方式:(memcache纯内存,redis是内存+硬盘,快照或者AOF文件的方式持久化)
  2. 数据支持类型:(redis比memcache支持的存储类型更多,memcache仅仅支持String)
  3. 集群管理方式不同:(redis支持分布式,memcache需要客户端自定义一致性hash算法来实现分布式的memcache集群)

PS:mongodb支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富。mongoDB支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制。 MongoDB从1.8版本开始采用binlog方式支持持久化的可靠性。mongoDB不支持事务。mongoDB内置了数据分析的功能(mapreduce),其他两者不支持。MongoDB:主要解决海量数据的访问效率问题。