JAVA 面试题 合辑(二)

JAVA 面试题 合辑(一)https://blog.csdn.net/haponchang/article/details/92741553

JAVA 面试题 合辑(二)https://blog.csdn.net/haponchang/article/details/92829739

JAVA 面试题 合辑(三)https://blog.csdn.net/haponchang/article/details/92833016

目录

MYSQL数据库

MySQL 的逻辑架构图

MYSQL 有哪些存储引擎,各自优缺点

MySQL 的索引原理,索引的类型有哪些,

索引建立的原则,索引如何优化

聚集索引和非聚集索引的区别

扫描二维码关注公众号,回复: 6549216 查看本文章

画出索引树,并描述B+树叶子结点

Btree 怎么分裂的,什么时候分裂,为什么是平衡的

自增索引的优点、可能的问题

sql 能够有效的使用到复合索引

非主键索引(二级索引)和主键索引的区别

什么是页分裂

MySQL的乐观锁和悲观锁

INNODB 的行级锁,解释其含义?(共享锁和排他锁)

数据库事务的特性(ACID)和隔离级别?

数据库事务的用途,如何开启?

MySQL分布式事务?

缓存穿透和缓存雪崩?

数据库查询缓慢是什么原因,如何优化?

SQL 优化的一般步骤是什么,怎么看执行计划,如何理解其中各个字段的含义?

Mysql怎么保证数据库不丢失?什么是两阶段提交?

binlog与redolog

分库分表的原则

如何保持redis数据与mysql的一致性

高并发下,如何做到安全的修改同一行数据

mysql 中 in 和 exists 区别。

NOSQL数据库

MongoDB数据库

HBase 数据库

Elasticsearch 数据库

redis数据库分析

Redis 的数据结构都有哪些

Redis 的数据结构都有哪些redis 的 list 结构相关的操作

Redis 的持久化的机制,aof 和 rdb 的区别

Redis 的使用要注意什么,讲讲持久化方式,内存设置,集群的应用和优劣势,淘汰策略等

redis2 和 redis3 的区别,redis3 内部通讯机制。

redis 和 memcached 的内存管理的区别。

Redis 的并发竞争问题如何解决,了解 Redis 事务的 CAS 操作吗。

Redis 的选举算法和流程是怎样的

Sentinel的选举流程

redis 的集群怎么同步的数据的。

elasticsearch 了解多少,说说你们公司 es 的集群架构,索引数据大小,分片有多少,以及一些调优手段。elasticsearch 的倒排索引是什么。

elasticsearch 索引数据多了怎么办,如何调优,部署。

lucence 内部结构是什么

MQ

用过哪些 MQ,和其他 mq 比较有什么优缺点,MQ 的连接是线程安全的吗,你们公司的MQ 服务架构怎样的

MQ 系统的数据如何保证不丢失

rabbitmq 如何实现集群高可用

BIO/NIO/AIO

序列化和反序列化?

BIO/NIO/AIO的区别?

Netty,零拷贝?

Reactor线程模型

Netty使用的编解码技术

如何解决TCP拆包粘包问题?

linux


MYSQL数据库

MySQL 的逻辑架构图


大体来说,MySQL 可以分为 Server 层和存储引擎层两部分。

Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。

连接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。文本中这个图是 show processlist 的结果,其中的 Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接。

MYSQL 有哪些存储引擎,各自优缺点

MyISAM: 拥有较高的插入,查询速度,但不支持事务

InnoDB :5.5版本后Mysql的默认数据库,事务型数据库的首选引擎,支持ACID事务,支持行级锁定

BDB: 源自Berkeley DB,事务型数据库的另一种选择,支持COMMIT和ROLLBACK等其他事务特性

Memory :所有数据置于内存的存储引擎,拥有极高的插入,更新和查询效率。但是会占用和数据量成正比的内存空间。并且其内容会在Mysql重新启动时丢失

Merge :将一定数量的MyISAM表联合而成一个整体,在超大规模数据存储时很有用

Archive :非常适合存储大量的独立的,作为历史记录的数据。因为它们不经常被读取。Archive拥有高效的插入速度,但其对查询的支持相对较差

Federated: 将不同的Mysql服务器联合起来,逻辑上组成一个完整的数据库。非常适合分布式应用

Cluster/NDB :高冗余的存储引擎,用多台数据机器联合提供服务以提高整体性能和安全性。适合数据量大,安全和性能要求高的应用

CSV: 逻辑上由逗号分割数据的存储引擎。它会在数据库子目录里为每个数据表创建一个.CSV文件。这是一种普通文本文件,每个数据行占用一个文本行。CSV存储引擎不支持索引。

BlackHole :黑洞引擎,写入的任何数据都会消失,一般用于记录binlog做复制的中继

MySQL 的索引原理,索引的类型有哪些,

JDK索引是通过复杂的算法,提高数据查询性能的手段。从磁盘io到内存io的转变

索引类型:普通索引,主键,唯一,单列/多列索引

索引建立的原则,索引如何优化

1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);

5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可

聚集索引和非聚集索引的区别

“聚簇”就是索引和记录紧密在一起。

非聚簇索引 索引文件和数据文件分开存放,索引文件的叶子页只保存了主键值,要定位记录还要去查找相应的数据块。

画出索引树,并描述B+树叶子结点

B 树的特点是:他会将数据也保存在非页子节点。

而这个特点会导致非页子节点不能存储大量的索引。

而 B+ Tree 就是针对这个对 B tree 做了优化。如下图所示:

我们看到,B+ Tree 将所有的 data 数据都保存到了叶子节点中,非也子节点只保存索引和指针。

我们假设一个非页子节点是 16kb,每个索引,即主键是 bigint,即 8b,指针为 8b。那么每页能存储大约 1000 个索引(16kb/ 8b + 8b).

而一颗 3 层的 B+树能够存储多少索引呢?如下图:

大约能够存储 10 亿个索引。通常 B+ 树的高度在 2-4 层,由于 MySql 在运行时,根节点是常驻内存的,因此每次查找只需要大约 2 -3 次 IO。可以说,B+ 树的设计,就是根据机械磁盘的特性来进行设计的。

知道了索引的设计,我们能够知道另外一些信息:

MySql 的主键不能太大,如果使用 UUID 这种,将会浪费 B+ 树的非叶子节点。

MySql 的主键最好是自增的,如果使用 UUID 这种,每次插入都会调整 B+树,从而导致页分裂,严重影响性能。

Btree 怎么分裂的,什么时候分裂,为什么是平衡的

Key 超过1024才分裂B树为甚会分裂? 因为随着数据的增多,一个结点的key满了,为了保持B树的特性,就会产生分裂,就向红黑树和AVL树为了保持树的性质需要进行旋转一样!

自增索引的优点、可能的问题

从性能和存储空间方面考量,自增主键往往是更合理的选择。主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。因此自增主键比较合适。

在分库分表时可能会生成重复主键 利用自增比例达到唯一 自增1 2,3 等

sql 能够有效的使用到复合索引

由于复合索引的组合索引,类似多个木板拼接在一起,如果中间断了就无法用了,所以要能用到复合索引,首先开头(第一列)要用上,比如index(a,b) 这种,我们可以select table tname where a=XX 用到第一列索引 如果想用第二列 可以 and b=XX 或者and b like‘TTT%’

非主键索引(二级索引)和主键索引的区别

B+树所有的关键字都出现在叶子节点的链表(稠密索引)中,且链表中的关键字是有序的。非叶子节点只起索引作用(稀疏索引)。
如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+树。
如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,ID 的值为 500,再到 ID 索引树搜索一次。这个过程称回表
也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,在应用中应该尽量使用主键查询。

什么是页分裂

一个数据页满了,按照B+Tree算法,新增加一个数据页,叫做页分裂,会导致性能下降。空间利用率降低大概50%。
当相邻的两个数据页利用率很低的时候,系统会做数据页合并,合并的过程是分裂过程的逆过程。

MySQL的乐观锁和悲观锁

乐观锁是设定每次修改都不会冲突,只在提交的时候去检查,悲观锁设定每次修改都会冲突,持有排他锁。

乐观锁:乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

实现:乐观锁不是数据库自带的,需要我们自己去实现。比如更新记录时检查当前记录version字段值是否与数据库中记录当前version一致。

悲观锁:悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作。

INNODB 的行级锁,解释其含义?(共享锁和排他锁)

行级锁分为共享锁和排他锁两种 共享锁又称读锁 排他锁又称写锁

共享锁:共享锁又称读锁,是读取操作创建的锁。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。

使用:SELECT ... LOCK IN SHARE MODE;

排他锁:排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

使用:SELECT ... FOR UPDATE;

数据库事务的特性(ACID)和隔离级别?

ACID:

1)原子性: 原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。

2)一致性:数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。

3)隔离性:隔离性是指并发的事务是相互隔离的。

4)持久性:持久性是指在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。即使出现了任何事故比如断电等,事务一旦提交,则持久化保存在数据库中。

隔离级别:

a. Read uncommitted:读未提交,一个事务可以读取另一个未提交事务的数据。引发脏读;

b. Read committed:读提交,一个事务要等另一个事务提交后才能读取数据。引发不可重复读(UPDATE操作:A准备用卡买单时,B正使用该卡全额付款并提交后,A再买单时发现余额不足);

c. Repeatable read:重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。引发幻读(INSERT操作:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读);

d. Serializable:事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用;

// Oracel、Sql Server默认Read committed,Mysql默认Repeatable read;

数据库事务的用途,如何开启?

try {//设置禁止自动提交事务

    connection.setAutoCommit( false);

    通过conn得到stmt进行数据库操作...

    connection.commit(); //统一提交

} catch(Exception ex) {

    connection.rollback();

} finally {

    connection和statement的关闭

}

MySQL分布式事务?

InnoDB存储引擎提供了对XA事务的支持,并通过XA事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。在使用分布式事务时,InnoDB存储引擎的事务隔离级别必须设置为Serializable。

缓存穿透和缓存雪崩?

缓存穿透含义:一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就去DB查找。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对DB造成很大的压力。这就叫做缓存穿透。

缓存穿透解决方案:

1. 对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存;

2. 对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的set中,查询时通过该set过滤(用的较少);

缓存雪崩含义:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给DB带来很大压力;

缓存雪崩解决方案:

1. 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待;

2. 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀;

3. 做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期;

数据库查询缓慢是什么原因,如何优化?

执行复杂的逻辑或统计、全表扫描、索引失效、join的表过多等;

索引失效优化:

a. where 子句中使用!=或<>操作符、函数操作、表达式(如:num/2)以及null值判断;

b. in 、 not in 、or 也要慎用,否则条件超过一定数量会导致索引失效;

c. 模糊查询(LIKE)时避免在关键词前使用”%”(如:LIKE '%小分期');

其他优化:

a. JOIN时使用小结果集驱动大结果集;

b. JOIN时条件判断应放在on中,避免在后面加where;

SQL 优化的一般步骤是什么,怎么看执行计划,如何理解其中各个字段的含义?

查看慢日志(show [session|gobal] status ),定位慢查询,查看慢查询执行计划 根据执行计划确认优化方案

Explain sql

select_type:表示select类型。常见的取值有SIMPLE(简单表,即不使用连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(union中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个SELECT)等。

talbe:输出结果集的表。

type:表的连接类型。性能由高到底:system(表中仅有一行)、const(表中最多有一个匹配行)、eq_ref、ref、ref_null、index_merge、unique_subquery、index_subquery、range、idnex等

possible_keys:查询时,可能使用的索引

key:实际使用的索引

key_len:索引字段的长度

rows:扫描行的数量

Extra:执行情况的说明和描述

Mysql怎么保证数据库不丢失?什么是两阶段提交?

首先redolog是物理日志,binlog是逻辑日志,也就是说,redolog中以物理方式存储数据(包括数据文件、数据页等等),而binlog中主要存的是当初操作的SQL语句

其次redolog作用是保持事务的持久性,也就是说,事务开始的时候,就已经开始把每一步的数据改动写redolog日志了,所以当出现故障的时候,可以依据redolog恢复数据,而binlog是在事务commit之后一次性写入的,会造成IO抖动,比较危险

另外binlog用于复制实现主从同步,还有基于某一个时间点,进行数据库还原,还原原理就是重新跑一遍binlog中的SQL。

执行的具体过程
1、执行器请求存储引擎获取ID=2的行数据,存储引擎直接用树搜索该行数据,如果ID=2所在的数据页在内存中就直接返回执行器,否则就从磁盘中读取写入内存并返回;
2、执行器获取到该行数据之后执行+1操作,并将+1后的结果调用存储引擎的接口写入新数据;
3、存储引擎将该行数据更新到内存中,并将更新操作记录到redolog中,此时redolog会处于prepare状态,之后存储引擎会告知执行器执行完毕,可以随时提交事务;
4、执行器生成该操作的binlog日志,写入磁盘;
5、执行器调用存储引擎的提交事务的接口,存储引擎将刚写入的redolog更新为commit状态,更新完成;
具体流程图如下:【其中深色部分表示存储引擎的业务】

在这里插入图片描述
基于上述流程分析,redolog 两次,binlog一次,且redolog和binlog相互独立,而binlog写入刚好在这两次的redolog之间,这两次的写入状态不同,这也就保证了一致性;

binlog与redolog

1、binlog,server层公用的日志模块,主要做的是mysql功能层面上的事情;
2、redolog,引擎层独有的日志模块,主要处理引擎相关的事情;
两者的区别如下:
1、binlog是server层公共的日志模块,主要作用是归档;而redolog是InnoDb引擎独有的,用于实现crash-safe的能力;
2、binlog是逻辑日志,记录的是执行的sql语句或者更新前后的数据行;而redolog是物理日志,记录了在某个数据页中做了哪些修改;
3、binlog是追加写日志,当日志文件写到一定大小之后会切换到下一个,而redolog是基于固定空间大小的日志文件循环写日志;

分库分表的原则

常见的分散存储的方法“分库分表”,其中包括“分库”和“分表”两大类。

业务分库

业务分库指的是按照业务模块将数据分散到不同的数据库服务器。例如,一个简单的电商网站,包括用户、商品、订单三个业务模块,我们可以将用户数据、商品数据、订单数据分开放到三台不同的数据库服务器上,而不是将所有数据都放在一台数据库服务器上。

虽然业务分库能够分散存储和访问压力,但同时也带来了新的问题,接下来我进行详细分析。

1.join 操作问题

业务分库后,原本在同一个数据库中的表分散到不同数据库中,导致无法使用 SQL 的 join 查询。

例如:“查询购买了化妆品的用户中女性用户的列表”这个功能,虽然订单数据中有用户的 ID 信息,但是用户的性别数据在用户数据库中,如果在同一个库中,简单的 join 查询就能完成;但现在数据分散在两个不同的数据库中,无法做 join 查询,只能采取先从订单数据库中查询购买了化妆品的用户 ID 列表,然后再到用户数据库中查询这批用户 ID 中的女性用户列表,这样实现就比简单的 join 查询要复杂一些。

2. 事务问题

原本在同一个数据库中不同的表可以在同一个事务中修改,业务分库后,表分散到不同的数据库中,无法通过事务统一修改。虽然数据库厂商提供了一些分布式事务的解决方案(例如,MySQL 的 XA),但性能实在太低,与高性能存储的目标是相违背的。

例如,用户下订单的时候需要扣商品库存,如果订单数据和商品数据在同一个数据库中,我们可以使用事务来保证扣减商品库存和生成订单的操作要么都成功要么都失败,但分库后就无法使用数据库事务了,需要业务程序自己来模拟实现事务的功能。例如,先扣商品库存,扣成功后生成订单,如果因为订单数据库异常导致生成订单失败,业务程序又需要将商品库存加上;而如果因为业务程序自己异常导致生成订单失败,则商品库存就无法恢复了,需要人工通过日志等方式来手工修复库存异常。

3. 成本问题

业务分库同时也带来了成本的代价,本来 1 台服务器搞定的事情,现在要 3 台,如果考虑备份,那就是 2 台变成了 6 台。

基于上述原因,对于小公司初创业务,并不建议一开始就这样拆分。

分表

单表数据拆分有两种方式:垂直分表和水平分表。

从上往下切就是垂直切分,就是表记录数相同但包含不同的列。

从左往右切就是水平切分,就是表的列相同但包含不同的行数据。

实际架构设计过程中并不局限切分的次数,可以切两次,也可以切很多次。

单表进行切分后,是否要将切分后的多个表分散在不同的数据库服务器中,可以根据实际的切分效果来确定,并不强制要求单表切分为多表后一定要分散到不同数据库中。原因在于单表切分为多表后,新的表即使在同一个数据库服务器中,也可能带来可观的性能提升,如果性能能够满足业务要求,是可以不拆分到多台数据库服务器的,毕竟我们在上面业务分库的内容看到业务分库也会引入很多复杂性的问题;如果单表拆分为多表后,单台服务器依然无法满足性能要求,那就不得不再次进行业务分库的设计了。

分表能够有效地分散存储压力和带来性能提升,但和分库一样,也会引入各种复杂性。

1. 垂直分表

垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。可以将某些字段独立到另外一张表中,带来一定的性能提升。

垂直分表引入的复杂性主要体现在表操作的数量要增加。例如,原来只要一次查询就可以获取,现在需要两次或多次查询。

2. 水平分表

水平分表适合表行数特别大的表,水平分表相比垂直分表,会引入更多的复杂性,主要表现在下面几个方面:

路由

水平分表后,某条数据具体属于哪个切分后的子表,需要增加路由算法进行计算,这个算法会引入一定的复杂性。

常见的路由算法有:范围路由、Hash 路由、配置路由

join 操作

水平分表后,数据分散在多个表中,如果需要与其他表进行 join 查询,需要在业务代码或者数据库中间件中进行多次 join 查询,然后将结果合并。

count() 操作

水平分表后,虽然物理上数据分散到多个表中,但某些业务逻辑上还是会将这些表当作一个表来处理。

这时通过count() 相加可以简单实现,缺点就是性能比较低。

可以使用记录数表:具体做法是新建一张表,假如表名为“记录数表”,包含 table_name、row_count 两个字段,每次插入或者删除子表数据成功后,都更新“记录数表”。

order by 操作

水平分表后,数据分散到多个子表中,排序操作无法在数据库中完成,只能由业务代码或者数据库中间件分别查询每个子表中的数据,然后汇总进行排序。


如何保持redis数据与mysql的一致性

  • 更新的时候,先删除缓存,然后再更新数据库
  • 读的时候,先读缓存;如果没有的话,就读数据库,同时将数据放入缓存,并返回响应。

高并发下,如何做到安全的修改同一行数据

使用悲观锁 悲观锁本质是当前只有一个线程执行操作,结束了唤醒其他线程进行处理。
也可以缓存队列中锁定主键。

mysql in exists 区别。

由于复合mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。

如果查询的两个表大小相当,那么用in和exists差别不大。

如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:

not in 和not exists如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。

1.EXISTS只返回TRUE或FALSE,不会返回UNKNOWN。

2.IN当遇到包含NULL的情况,那么就会返回UNKNOWN。

NOSQL数据库

MongoDB数据库

MongoDB 文档数据库 解决关系数据库强 schema 约束的问题

解决关系数据库 schema 带来的问题,文档数据库应运而生。目前绝大部分文档数据库存储的数据格式是 JSON(或者 BSON)。

1. 新增字段简单

业务上增加新的字段,无须再像关系数据库一样要先执行 DDL 语句修改表结构,程序代码直接读写即可。

2. 历史数据不会出错

对于历史数据,即使没有新增的字段,也不会导致错误,只会返回空值,此时代码进行兼容处理即可。

3. 可以很容易存储复杂数据

JSON 是一种强大的描述语言,能够描述复杂的数据结构。这种业务场景如果使用关系数据库来存储数据,就会很麻烦,而使用文档数据库,会简单、方便许多,扩展新的属性也更加容易。

文档数据库 no-schema 的特性带来的这些优势也是有代价的,最主要的代价就是不支持事务。例如,使用 MongoDB 来存储商品库存,系统创建订单的时候首先需要减扣库存,然后再创建订单。这是一个事务操作,用关系数据库来实现就很简单,但如果用 MongoDB 来实现,就无法做到事务性。异常情况下可能出现库存被扣减了,但订单没有创建的情况。因此某些对事务要求严格的业务场景是不能使用文档数据库的。

文档数据库另外一个缺点就是无法实现关系数据库的 join 操作。例如,我们有一个用户信息表和一个订单表,订单表中有买家用户 id。如果要查询“购买了苹果笔记本用户中的女性用户”,用关系数据库来实现,一个简单的 join 操作就搞定了;而用文档数据库是无法进行 join 查询的,需要查两次:一次查询订单表中购买了苹果笔记本的用户,然后再查询这些用户哪些是女性用户。

HBase 数据库

HBase 列式数据库 解决关系数据库大数据场景下的 I/O 问题

传统关系数据库被称为“行式数据库”,因为关系数据库是按照行来存储数据的。优势在于:

业务同时读取多个列时效率高,因为这些列都是按行存储在一起的,一次磁盘操作就能够把一行数据中的各个列都读取到内存中。

能够一次性完成对一行中的多个列的写操作,保证了针对行数据写操作的原子性和一致性;否则如果采用列存储,可能会出现某次写操作,有的列成功了,有的列失败了,导致数据不一致。

列式数据库就是按照列来存储数据的数据库。优势:海量数据进行统计。例如,计算某个城市体重超重的人员数据,实际上只需要读取每个人的体重这一列并进行统计即可,而行式存储即使最终只使用一列,也会将所有行数据都读取出来。如果单行用户信息有 1KB,其中体重只有 4 个字节,行式存储还是会将整行 1KB 数据全部读取到内存中,这是明显的浪费。而如果采用列式存储,每个用户只需要读取 4 字节的体重数据即可,I/O 将大大减少。

除了节省 I/O,列式存储还具备更高的存储压缩比,能够节省更多的存储空间。普通的行式数据库一般压缩率在 3:1 到 5:1 左右,而列式数据库的压缩率一般在 8:1 到 30:1 左右,因为单个列的数据相似度相比行来说更高,能够达到更高的压缩率。

列式存储应用在离线的大数据分析和统计场景中,因为这种场景主要是针对部分列单列进行操作,且数据写入后就无须再更新删除。

Elasticsearch 数据库

Elasticsearch 倒排索引 解决关系数据库的全文搜索性能问题

使用关系数据库进行全文搜索的问题:

全文搜索的条件可以随意排列组合,如果通过索引来满足,则索引的数量会非常多。

全文搜索的模糊匹配方式,索引无法满足,只能用 like 查询,而 like 查询是整表扫描,效率非常低。

全文搜索引擎的技术原理被称为“倒排索引”(Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,其基本原理是建立单词到文档的索引。之所以被称为“倒排”索引,是和“正排“索引相对的,“正排索引”的基本原理是建立文档到单词的索引。比如:正排索引适用于根据文档名称来查询文档内容。,倒排索引适用于根据关键词来查询文档内容。

全文搜索的使用方式

全文搜索引擎的索引对象是单词和文档,而关系数据库的索引对象是键和行,两者的术语差异很大,不能简单地等同起来。因此,为了让全文搜索引擎支持关系型数据的全文搜索,需要做一些转换操作,即将关系型数据转换为文档数据。将关系型数据按照对象的形式转换为 JSON 文档,然后将 JSON 文档输入全文搜索引擎进行索引。

redis数据库分析

redis K-V 存储 解决关系数据库无法存储数据结构的问题,

Redis 是 K-V 存储的典型代表,它是一款开源(基于 BSD 许可)的高性能 K-V 缓存和存储系统。Redis 的 Value 是具体的数据结构,包括 string、hash、list、set、sorted set、bitmap 和 hyperloglog,所以常常被称为数据结构服务器。

Redis 的缺点主要体现在并不支持完整的 ACID 事务,Redis 虽然提供事务功能,但 Redis 的事务和关系数据库的事务不可同日而语,Redis 的事务只能保证隔离性和一致性(I 和 C),无法保证原子性和持久性(A 和 D)。

虽然 Redis 并没有严格遵循 ACID 原则,但实际上大部分业务也不需要严格遵循 ACID 原则。以上面的微博关注操作为例,即使系统没有将 A 加入 B 的粉丝列表,其实业务影响也非常小,因此我们在设计方案时,需要根据业务特性和要求来确定是否可以用 Redis,而不能因为 Redis 不遵循 ACID 原则就直接放弃。

Redis 的数据结构都有哪些

字符串(strings):存储整数(比如计数器)和字符串(废话。。),有些公司也用来存储json/pb等序列化数据,并不推荐,浪费内存

哈希表(hashes):存储配置,对象(比如用户、商品),优点是可以存取部分key,对于经常变化的或者部分key要求atom操作的适合

列表(lists):可以用来存最新用户动态,时间轴,优点是有序,确定是元素可重复,不去重

集合(sets):无序,唯一,对于要求严格唯一性的可以使用

有序集合(sorted sets):集合的有序版,很好用,对于排名之类的复杂场景可以考虑

Redis 的数据结构都有哪些redis list 结构相关的操作

LPUSH LPUSHX RPUSH RPUSHX LPOP RPOP BLPOP BRPOP LLEN LRANGE

Redis 的持久化的机制,aof rdb 的区别

RDB 定时快照方式(snapshot): 定时备份,可能会丢失数据

AOF 基于语句追加方式 只追加写操作

AOF 持久化和 RDB 持久化的最主要区别在于,前者记录了数据的变更,而后者是保存了数据本身

Redis 的使用要注意什么,讲讲持久化方式,内存设置,集群的应用和优劣势,淘汰策略等

持久化方式:RDB时间点快照 AOF记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。

内存设置 maxmemory used_memory

虚拟内存: vm-enabled yes

3.0采用Cluster方式,

Redis集群相对单机在功能上存在一些限制, 需要开发人员提前了解,

在使用时做好规避。 限制如下:

1) key批量操作支持有限。 如mset、 mget, 目前只支持具有相同slot值的key执行批量操作。 对于映射为不同slot值的key由于执行mget、 mget等操作可能存在于多个节点上因此不被支持。

2) key事务操作支持有限。 同理只支持多key在同一节点上的事务操作, 当多个key分布在不同的节点上时无法使用事务功能。

3) key作为数据分区的最小粒度, 因此不能将一个大的键值对象如hash、 list等映射到不同的节点。

4) 不支持多数据库空间。 单机下的Redis可以支持16个数据库, 集群模式下只能使用一个数据库空间, 即db0。

5) 复制结构只支持一层, 从节点只能复制主节点, 不支持嵌套树状复制结构。

Redis Cluster是Redis的分布式解决方案, 在3.0版本正式推出, 有效地解决了Redis分布式方面的需求。 当遇到单机内存、 并发、 流量等瓶颈时, 可以采用Cluster架构方案达到负载均衡的目的。 之前, Redis分布式方案一般有两种:

·客户端分区方案, 优点是分区逻辑可控, 缺点是需要自己处理数据路由、 高可用、 故障转移等问题。

·代理方案, 优点是简化客户端分布式逻辑和升级维护便利, 缺点是加重架构部署复杂度和性能损耗。

现在官方为我们提供了专有的集群方案: Redis Cluster, 它非常优雅地解决了Redis集群方面的问题, 因此理解应用好Redis Cluster将极大地解放我们使用分布式Redis的工作量, 同时它也是学习分布式存储的绝佳案例。

redis2 和 redis3 的区别,redis3 内部通讯机制。

集群方式的区别,3采用Cluster,2采用客户端分区方案和代理方案

通信过程说明:

1) 集群中的每个节点都会单独开辟一个TCP通道, 用于节点之间彼此

通信, 通信端口号在基础端口上加10000。

2) 每个节点在固定周期内通过特定规则选择几个节点发送ping消息。

3) 接收到ping消息的节点用pong消息作为响应。

redis 和 memcached 的内存管理的区别。

Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完全解决内存碎片问题。

Redis的内存管理主要通过源码中zmalloc.h和zmalloc.c两个文件来实现的。

在Redis中,并不是所有的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。

Redis 的并发竞争问题如何解决,了解 Redis 事务的 CAS 操作吗。

Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有2种解决方法:

1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。

2.服务器角度,利用setnx实现锁。

MULTI,EXEC,DISCARD,WATCH 四个命令是 Redis 事务的四个基础命令。其中:

MULTI,告诉 Redis 服务器开启一个事务。注意,只是开启,而不是执行

EXEC,告诉 Redis 开始执行事务

DISCARD,告诉 Redis 取消事务

WATCH,监视某一个键值对,它的作用是在事务执行之前如果监视的键值被修改,事务会被取消。

可以利用watch实现cas乐观锁

http://wiki.jikexueyuan.com/project/redis/transaction-mechanism.html

http://www.jianshu.com/p/d777eb9f27df

Redis 的选举算法和流程是怎样的

Raft采用心跳机制触发Leader选举。系统启动后,全部节点初始化为Follower,term为0.节点如果收到了RequestVote或者AppendEntries,就会保持自己的Follower身份。如果一段时间内没收到AppendEntries消息直到选举超时,说明在该节点的超时时间内还没发现Leader,Follower就会转换成Candidate,自己开始竞选Leader。一旦转化为Candidate,该节点立即开始下面几件事情:

1、增加自己的term。

2、启动一个新的定时器。

3、给自己投一票。

4、向所有其他节点发送RequestVote,并等待其他节点的回复。

如果在这过程中收到了其他节点发送的AppendEntries,就说明已经有Leader产生,自己就转换成Follower,选举结束。

如果在计时器超时前,节点收到多数节点的同意投票,就转换成Leader。同时向所有其他节点发送AppendEntries,告知自己成为了Leader。

每个节点在一个term内只能投一票,采取先到先得的策略,Candidate前面说到已经投给了自己,Follower会投给第一个收到RequestVote的节点。每个Follower有一个计时器,在计时器超时时仍然没有接受到来自Leader的心跳RPC, 则自己转换为Candidate, 开始请求投票,就是上面的的竞选Leader步骤。

如果多个Candidate发起投票,每个Candidate都没拿到多数的投票(Split Vote),那么就会等到计时器超时后重新成为Candidate,重复前面竞选Leader步骤。

Raft协议的定时器采取随机超时时间,这是选举Leader的关键。每个节点定时器的超时时间随机设置,随机选取配置时间的1倍到2倍之间。由于随机配置,所以各个Follower同时转成Candidate的时间一般不一样,在同一个term内,先转为Candidate的节点会先发起投票,从而获得多数票。多个节点同时转换为Candidate的可能性很小。即使几个Candidate同时发起投票,在该term内有几个节点获得一样高的票数,只是这个term无法选出Leader。由于各个节点定时器的超时时间随机生成,那么最先进入下一个term的节点,将更有机会成为Leader。连续多次发生在一个term内节点获得一样高票数在理论上几率很小,实际上可以认为完全不可能发生。一般1-2个term类,Leader就会被选出来。

Sentinel的选举流程

Sentinel集群正常运行的时候每个节点epoch相同,当需要故障转移的时候会在集群中选出Leader执行故障转移操作。Sentinel采用了Raft协议实现了Sentinel间选举Leader的算法,不过也不完全跟论文描述的步骤一致。Sentinel集群运行过程中故障转移完成,所有Sentinel又会恢复平等。Leader仅仅是故障转移操作出现的角色。

选举流程

1、某个Sentinel认定master客观下线的节点后,该Sentinel会先看看自己有没有投过票,如果自己已经投过票给其他Sentinel了,在2倍故障转移的超时时间自己就不会成为Leader。相当于它是一个Follower。

2、如果该Sentinel还没投过票,那么它就成为Candidate。

3、和Raft协议描述的一样,成为Candidate,Sentinel需要完成几件事情

1)更新故障转移状态为start

2)当前epoch加1,相当于进入一个新term,在Sentinel中epoch就是Raft协议中的term。

3)更新自己的超时时间为当前时间随机加上一段时间,随机时间为1s内的随机毫秒数。

4)向其他节点发送is-master-down-by-addr命令请求投票。命令会带上自己的epoch。

5)给自己投一票,在Sentinel中,投票的方式是把自己master结构体里的leader和leader_epoch改成投给的Sentinel和它的epoch。

4、其他Sentinel会收到Candidate的is-master-down-by-addr命令。如果Sentinel当前epoch和Candidate传给他的epoch一样,说明他已经把自己master结构体里的leader和leader_epoch改成其他Candidate,相当于把票投给了其他Candidate。投过票给别的Sentinel后,在当前epoch内自己就只能成为Follower。

5、Candidate会不断的统计自己的票数,直到他发现认同他成为Leader的票数超过一半而且超过它配置的quorum(quorum可以参考《redis sentinel设计与实现》)。Sentinel比Raft协议增加了quorum,这样一个Sentinel能否当选Leader还取决于它配置的quorum。

6、如果在一个选举时间内,Candidate没有获得超过一半且超过它配置的quorum的票数,自己的这次选举就失败了。

7、如果在一个epoch内,没有一个Candidate获得更多的票数。那么等待超过2倍故障转移的超时时间后,Candidate增加epoch重新投票。

8、如果某个Candidate获得超过一半且超过它配置的quorum的票数,那么它就成为了Leader。

9、与Raft协议不同,Leader并不会把自己成为Leader的消息发给其他Sentinel。其他Sentinel等待Leader从slave选出master后,检测到新的master正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程。

redis 的集群怎么同步的数据的。

redis replication redis-migrate-tool等方式

elasticsearch 了解多少,说说你们公司 es 的集群架构,索引数据大小,分片有多少,以及一些调优手段。elasticsearch 的倒排索引是什么。

ElasticSearch(简称ES)是一个分布式、Restful的搜索及分析服务器,设计用于分布式计算;能够达到实时搜索,稳定,可靠,快速。和Apache Solr一样,它也是基于Lucence的索引服务器,而ElasticSearch对比Solr的优点在于:

轻量级:安装启动方便,下载文件之后一条命令就可以启动。

Schema free:可以向服务器提交任意结构的JSON对象,Solr中使用schema.xml指定了索引结构。

多索引文件支持:使用不同的index参数就能创建另一个索引文件,Solr中需要另行配置。

分布式:Solr Cloud的配置比较复杂。

倒排索引是实现“单词-文档矩阵”的一种具体存储形式,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:“单词词典”和“倒排文件”。

elasticsearch 索引数据多了怎么办,如何调优,部署。

使用bulk API

初次索引的时候,把 replica 设置为 0

增大 threadpool.index.queue_size

增大 indices.memory.index_buffer_size

增大 index.translog.flush_threshold_ops

增大 index.translog.sync_interval

增大 index.engine.robin.refresh_interval

http://www.jianshu.com/p/5eeeeb4375d4

lucence 内部结构是什么

索引(Index):

在Lucene中一个索引是放在一个文件夹中的。

如上图,同一文件夹中的所有的文件构成一个Lucene索引。

段(Segment):

一个索引可以包含多个段,段与段之间是独立的,添加新文档可以生成新的段,不同的段可以合并。

如上图,具有相同前缀文件的属同一个段,图中共三个段 “_0” 和 "_1"和“_2”。

segments.gen和segments_X是段的元数据文件,也即它们保存了段的属性信息。

文档(Document):

文档是我们建索引的基本单位,不同的文档是保存在不同的段中的,一个段可以包含多篇文档。

新添加的文档是单独保存在一个新生成的段中,随着段的合并,不同的文档合并到同一个段中。

域(Field):

一篇文档包含不同类型的信息,可以分开索引,比如标题,时间,正文,作者等,都可以保存在不同的域里。

不同域的索引方式可以不同,在真正解析域的存储的时候,我们会详细解读。

词(Term):

词是索引的最小单位,是经过词法分析和语言处理后的字符串。

MQ

用过哪些 MQ,和其他 mq 比较有什么优缺点,MQ 的连接是线程安全的吗,你们公司的MQ 服务架构怎样的

MQ 系统的数据如何保证不丢失

对数据进行持久化,多盘存储

rabbitmq 如何实现集群高可用

集群是保证服务可靠性的一种方式,同时可以通过水平扩展以提升消息吞吐能力。RabbitMQ是用分布式程序设计语言erlang开发的,所以天生就支持集群。接下来,将介绍RabbitMQ分布式消息处理方式、集群模式、节点类型,并动手搭建一个高可用集群环境,最后通过java程序来验证集群的高可用性。

1. 三种分布式消息处理方式

RabbitMQ分布式的消息处理方式有以下三种:

1、Clustering:不支持跨网段,各节点需运行同版本的Erlang和RabbitMQ, 应用于同网段局域网。

2、Federation:允许单台服务器上的Exchange或Queue接收发布到另一台服务器上Exchange或Queue的消息, 应用于广域网,。

3、Shovel:与Federation类似,但工作在更低层次。

RabbitMQ对网络延迟很敏感,在LAN环境建议使用clustering方式;在WAN环境中,则使用Federation或Shovel。我们平时说的RabbitMQ集群,说的就是clustering方式,它是RabbitMQ内嵌的一种消息处理方式,而Federation或Shovel则是以plugin形式存在。

BIO/NIO/AIO

序列化和反序列化?

概念:

1. 把对象转换为字节序列的过程称为对象的序列化;

2. 把字节序列恢复为对象的过程称为对象的反序列化。

用途:1. 把对象的字节序列永久地保存到硬盘上,让它们离开内存空间,入住物理硬盘,等要用了,再把保存在硬盘中的对象还原到内存中;

2. 在网络上传送对象的字节序列;

实现:1. ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;

2. ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回;

3. 只有实现了Serializable接口的类的对象才能被序列化;

serialVersionUID的作用:

1. 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;

2. 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

BIO/NIO/AIO的区别?

BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理.AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

Netty,零拷贝?

Netty逻辑架构
第一层
Reactor 通信调度层,它由一系列辅助类组成,包括 Reactor 线程NioEventLoop 以及其父类、NioSocketChannel/NioServerSocketChannel 以及其父类、ByteBuffer 以及由其衍生出来的各种 Buffer、Unsafe 以及其衍生出的各种内部子类等
第二层
职责链 ChannelPipeLine,它负责调度事件在职责链中的传播,支持动态的编排职责链,职责链可以选择性的拦截自己关心的事件,对于其它IO操作和事件忽略,Handler同时支持inbound和outbound事件
第三层
业务逻辑编排层,业务逻辑编排层通常有两类:一类是纯粹的业务逻辑编排,还有一类是应用层协议插件,用于协议相关的编解码和链路管理,例如 CMPP 协议插件

“零拷贝”是指计算机操作的过程中,CPU不需要为数据在内存之间的拷贝消耗资源。而它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而直接在内核空间(Kernel Space)中传输到网络的方式。

Netty的“零拷贝”主要体现在如下三个方面:

1) Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。

2) Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。

3) Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。

Reactor线程模型

reactor线程模型分为单线程,多线程,主从多线程。 实际编程过程中,第二种用的是最多的。

Netty使用的编解码技术

Google 的 Protobuf
Protobuf 是谷歌的开源协议,详细说明见:http://www.jianshu.com/p/8025dbe0121a

特点:

结构化数据存储格式
高效的编解码性能
语言无关、平台无关、扩展性好
官方支持Java、C++、Python三种语言
Facebook 的 Thrift
Thrift可以作为高性能的通信中间件使用,支持数据(对象)序列化和多种语言的RPC服务。

MessagePack 编解码
MessagePack是一个高效的二进制序列化框架,它像JSON一样支持不同语言间的数据交换,但是它的性能更快,序列化之后的码流更小。

如何解决TCP拆包粘包问题?

1、包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分

ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
2、消息定长,报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分

sc.pipeline().addLast(new FixedLengthFrameDecoder(10));
3、将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段

linux

常用命令行

top:显示系统中各个进程的资源占用情况

sar:周期性地对内存和cpu使用情况进行采样。如统计CPU使用情况,每秒采样一次,一共三次sar -u 1 3

vmstat:

类似sar,统计CPU、内存使用情况、swap使用情况,也可以设置周期和采样次数。

例:vmstat

iostat:

iostat 1 2

pidstat工具

猜你喜欢

转载自blog.csdn.net/haponchang/article/details/92829739