Java基础后端面试点大全(六)

公众号

  • 全网唯一一个从0开始帮助Java开发者转做大数据领域的公众号~

  • 大数据技术与架构或者搜索import_bigdata关注~

  • 海量【java和大数据的面试题+视频资料】整理在公众号,关注后可以下载~

数据库
1.mysql索引都有哪些原则? 索引的数据结构?B+ tree 和 B tree 有什么区别?
https://www.cnblogs.com/tgycoder/p/5410057.html
建索引的几大原则
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)的索引,那么只需要修改原来的索引即可。
6、如果确定有多少条数据,使用 limit 限制一下,MySQL在查找到对应条数的数据的时候,会停止继续查找

7、利用查询缓存,很多时候MySQL会对查询结果进行cache,但是对应“动态”的数据会不cache,例如:

1 SELECT username FROM user WHERE signup_date >= CURDATE() 无法使用cache
2 SELECT username FROM user WHERE signup_date >= '2017-05-06' 可以cache 

当使用了MySQL的一写函数之后,MySQL无法确定结果是易变的,所以不会cache,还有now(),rand()
也一样不开启cache

8、join 语法,尽量将小的表放在前面,在需要on的字段上,数据类型保持一致,并设置对应的索引,否则MySQL无法使用索引来join查询

9、在大表上做大量更新时,如果会锁全表,则需要拆分执行,避免长时间锁住表,导致其他请求积累太多(InnoDB 支持行锁,但前提是Where子句需要建立索引,没有索引也一样是锁全表)


为什么要B+树

由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引,而B树则常用于文件索引。

这都是由于B+树和B具有这不同的存储结构所造成的区别,以一个m阶树为例。
关键字的数量不同;B+树中分支结点有m个关键字,其叶子结点也有m个,其关键字只是起到了一个索引的作用,但是B树虽然也有m个子结点,但是其只拥有m-1个关键字。
存储的位置不同;B+树中的数据都存储在叶子结点上,也就是其所有叶子结点的数据组合起来就是完整的数据,但是B树的数据存储在每一个结点中,并不仅仅存储在叶子结点上。
分支结点的构造不同;B+树的分支结点仅仅存储着关键字信息和儿子的指针(这里的指针指的是磁盘块的偏移量),也就是说内部结点仅仅包含着索引信息。
查询不同;B树在找到具体的数值以后,则结束,而B+树则需要通过索引找到叶子结点中的数据才结束,也就是说B+树的搜索过程中走了一条从根结点到叶子结点的路径。
http://blog.csdn.net/bigtree_3721/article/details/73626663

1.选择唯一性索引

唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。如果使用姓名的话,可能存在同名现象,从而降低查询速度。

2.为经常需要排序、分组和联合操作的字段建立索引

经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。

3.为常作为查询条件的字段建立索引

如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。

4.限制索引的数目

索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。

5.尽量使用数据量少的索引

如果索引的值很长,那么查询的速度会受到影响。例如,对一个CHAR(100)类型的字段进行全文检索需要的时间肯定要比对CHAR(10)类型的字段需要的时间要多。

6.尽量使用前缀来索引

如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。

7.删除不再使用或者很少使用的索引

表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。

8 . 最左前缀匹配原则,非常重要的原则。

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

9 .=和in可以乱序。

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

10 . 尽量选择区分度高的列作为索引。

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

11 .索引列不能参与计算,保持列“干净”。

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

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

注意:选择索引的最终目的是为了使查询的速度变快。上面给出的原则是最基本的准则,但不能拘泥于上面的准则。读者要在以后的学习和工作中进行不断的实践。根据应用的实际情况进行分析和判断,选择最合适的索引方式。
2.mysql有哪些存储引擎?有啥区别?要详细!
MyISAM存储引擎:不支持事务、也不支持外键,优势是访问速度快,对事务完整性没有 要求或者以select,insert为主
InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM引擎,写的处理效率会差一些,并且会占用更多的磁盘空间以保留数据和索引。 InnoDB存储引擎的特点:支持自动增长列,支持外键约束
Memory存储引擎使用存在于内存中的内容来创建表。每个memory表只实际对应一个磁盘文件,格式是.frm。memory类型的表访问非常的快,因为它的数据是放在内存中的,并且默认使用HASH索引,但是一旦服务关闭,表中的数据就会丢失掉
Merge存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,merge表本身并没有数据,对merge类型的表可以进行查询,更新,删除操作,这些操作实际上是对内部的MyISAM表进行的

3.设计高并发系统数据库层面如何设计?数据库锁有哪些类型?如何实现?
https://www.cnblogs.com/zhangj391/p/6715410.html
http://blog.csdn.net/tangkund3218/article/details/47704527
https://www.cnblogs.com/bcphp/p/7682866.html
分库分表 读写分离 数据库高可用 数据分级 粗细管道 (表层面 不用外键关联 慢sql优化 索引等)

共享锁S LOCK 允许事务读一行数据
排他锁 X LOCK 允许事务删除或更新一行数据
page-level locking(页级锁)
锁定表中某些行集合(称做页),被锁定的行只对锁定最初的线程是可行。如果另外一个线程想要向这些行写数据,它必须等到锁被释放。
———————
InnoDB的锁大致分为:
3.1 行锁
支持并发高,带来最大的锁开销. 在存储引擎层实现,服务器感知不到
3.2 表锁
服务器会为诸如: ALTER Table 之类的语句使用表锁,忽略存储引擎的锁机制
但锁的类型又分为:
(1). 共享锁(S Lock) , 允许事务读取一行数据
(2). 排他锁(X Lock),允许事务删除或更新一行数据.

4.数据库事务

分库分表
1.如何设计动态扩容的分库分表方案?
http://blog.csdn.net/ht99582/article/details/37890599
2.用过哪些数据库分表的中间件,有啥有点和缺点?分库分表中间件的设计原理
https://www.cnblogs.com/wangzhongqiu/p/7100332.html
http://blog.csdn.net/kingice1014/article/details/52344297
无论使用哪种架构,核心逻辑均极为相似,除了协议实现层不同(JDBC或数据库协议),都会分为分片规则配置、SQL解析、SQL改写、SQL路由、SQL执行以及结果归并等模块。
3.我现在有一个未分库分表的系统,以后系统需分库分表,如何设计?
https://www.cnblogs.com/fjwuyongzhi/archive/2012/01/14/2322611.html
让未分库分表的系统切换到分表的系统上

4.分布式事务如何解决?TCC?如果出现网络问题怎么办?
https://www.cnblogs.com/taiyonghai/p/6094350.html
一、结合MQ消息中间件实现的可靠消息最终一致性
二、TCC补偿性事务解决方案(二阶段提交,设置好回滚)
三、最大努力通知型方案

第一种方案:可靠消息最终一致性,需要业务系统结合MQ消息中间件实现,在实现过程中需要保证消息的成功发送及成功消费。即需要通过业务系统控制MQ的消息状态
第二种方案:TCC补偿性,分为三个阶段TRYING-CONFIRMING-CANCELING。每个阶段做不同的处理。(2阶段提交,设置好回滚)
TRYING阶段主要是对业务系统进行检测及资源预留
CONFIRMING阶段是做业务提交,通过TRYING阶段执行成功后,再执行该阶段。默认如果TRYING阶段执行成功,CONFIRMING就一定能成功。
CANCELING阶段是回对业务做回滚,在TRYING阶段中,如果存在分支事务TRYING失败,则需要调用CANCELING将已预留的资源进行释放。
第三种方案:最大努力通知xing型,这种方案主要用在与第三方系统通讯时,比如:调用微信或支付宝支付后的支付结果通知。这种方案也是结合MQ进行实现,例如:通过MQ发送http请求,设置最大通知次数。达到通知次数后即不再通知。
具体的案例你也可以参考下这篇博客,它上面的这个案例就是结合电商支付做的系统分布式事务实现案例:http://www.roncoo.com/article/detail/124243

基于事务消息的MQ方案是目前公认的较为理想的分布式事务解决方案,各大电商都在应用这一方案。种方式适合的业务场景广泛,而且比较可靠。不过这种方式技术实现的难度比较大。目前主流的开源MQ(ActiveMQ、RabbitMQ、Kafka)均未实现对事务消息的支持,所以需二次开发或者新造轮子。
https://www.cnblogs.com/taiyonghai/p/6094350.html
5.为什么要分库分表?
单表容量有限 系统分离,不能以挂全挂
6.分布式寻址方式有哪些算法?一致性hash知道吗?

7.如何解决分库分表主键问题?有什么实现方案?
分布式主键生成策略
https://www.jianshu.com/p/a0a3aa888a49

分布式缓存

  1. redis和memchache有什么区别?为什么单线程的redis比多线程的memchache效率要高?
    http://blog.csdn.net/zhangweiguangsunjiao/article/details/46659707?ref=myread
    1、Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等。
    2、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。
    3、虚拟内存–Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘
    4、过期策略–memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10
    5、分布式–设定memcache集群,利用magent做一主多从;redis可以做一主多从。都可以一主一从
    6、存储数据安全–memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化)
    7、灾难恢复–memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复
    8、Redis支持数据的备份,即master-slave模式的数据备份。

代码更清晰,处理逻辑更简单
不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
不存在多进程或者多线程导致的切换而消耗CPU

2.redis有什么数据类型?在哪些场景下使用?
String
应用场景:String是最常用的一种数据类型,普通的key/ value 存储都可以归为此类.即可以完全实现目前 Memcached 的功能,并且效率更高。还可以享受Redis的定时持久化,操作日志及 Replication等功能。除了提供与 Memcached 一样的get、set、incr、decr 等操作外,Redis还提供了下面一些操作:
获取字符串长度
往字符串append内容
设置和获取字符串的某一段内容
设置及获取字符串的某一位(bit)
批量设置一系列字符串的内容
Hash
常用命令:hget,hset,hgetall 等。
应用场景:在Memcached中,我们经常将一些结构化的信息打包成HashMap,在客户端序列化后存储为一个字符串的值,比如用户的昵称、年龄、性别、积分等,这时候在需要修改其中某一项时,通常需要将所有值取出反序列化后,修改某一项的值,再序列化存储回去。这样不仅增大了开销,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。
List
比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。
Set
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
Sets 集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构,可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。
实现方式:
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

Sorted set
Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
另外还可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
pub/sub
Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。

Transactions
谁说NoSQL都不支持事务,虽然Redis的Transactions提供的并不是严格的ACID的事务(比如一串用EXEC提交执行的命令,在执行中服务器宕机,那么会有一部分命令执行了,剩下的没执行),但是这个Transactions还是提供了基本的命令打包执行的功能(在服务器不出问题的情况下,可以保证一连串的命令是顺序在一起执行的,中间有会有其它客户端命令插进来执行)。Redis还提供了一个Watch功能,你可以对一个key进行Watch,然后再执行Transactions,在这过程中,如果这个Watched的值进行了修改,那么这个Transactions会发现并拒绝执行。

https://www.cnblogs.com/mrhgw/p/6278619.html

3.redis的复制如何实现?redis集群模式如何实现的?redis的key如何寻址?

Redis 使用异步复制。 从 Redis 2.8 开始, 从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
一个主服务器可以有多个从服务器。
不仅主服务器可以有从服务器, 从服务器也可以有自己的从服务器, 多个从服务器之间可以构成一个图状结构。
复制功能不会阻塞主服务器: 即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。
复制功能也不会阻塞从服务器: 只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步, 服务器也可以使用旧版本的数据集来处理命令查询。
不过, 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内, 连接请求会被阻塞。
你还可以配置从服务器, 让它在与主服务器之间的连接断开时, 向客户端发送一个错误。
复制功能可以单纯地用于数据冗余(data redundancy), 也可以通过让多个从服务器处理只读命令请求来提升扩展性(scalability): 比如说, 繁重的 SORT 命令可以交给附属节点去运行。
可以通过复制功能来让主服务器免于执行持久化操作: 只要关闭主服务器的持久化功能, 然后由从服务器去执行持久化操作即可。

http://doc.redisfans.com/topic/persistence.html
Redis 提供了多种不同级别的持久化方式:
RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。
你甚至可以关闭持久化功能,让数据只在服务器运行时存在。
了解 RDB 持久化和 AOF 持久化之间的异同是非常重要的, 以下几个小节将详细地介绍这这两种持久化功能, 并对它们的相同和不同之处进行说明。

4.redis如何设计分布式锁?zk可以吗?如何实现?哪个效率更高

获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

查看目标Node是否已经创建,已经创建,那么等待锁。
如果未创建,创建一个瞬时Node,表示已经占有锁。
如果创建失败,那么证明锁已经被其他线程占有了,那么同样等待锁。
当释放锁,或者当前Session超时的时候,节点被删除,唤醒之前等待锁的线程去争抢锁。
https://www.jianshu.com/p/5d12a01018e1

5.redis持久化?有什么优缺点?具体底层实现?

6.redis的过期测略有哪些?LRU?写代码?
定时删除
含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除-
优点:保证内存被尽快释放
缺点:
若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key
定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重
没人用
惰性删除
含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)
缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
定期删除
含义:每隔一段时间执行一次删除过期key操作
优点:
通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理"定时删除"的缺点
定期删除过期key–处理"惰性删除"的缺点
缺点
在内存友好方面,不如"定时删除"
在CPU时间友好方面,不如"惰性删除"
难点
合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)(这个要根据服务器运行情况来定了)
import java.util.LinkedHashMap;
import java.util.Map;

public LRUCache<K, V> extends LinkedHashMap<K, V> {
private int cacheSize;

public LRUCache(int cacheSize) {
super(16, 0.75, true);
this.cacheSize = cacheSize;
}

protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() >= cacheSize;
}
}

分布式服务框架
1.dubbo的实现过程?注册中心挂了可以继续通信吗?
服务容器负责启动,加载,运行服务提供者。

  1. 服务提供者在启动时,向注册中心注册自己提供的服务。
  2. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
  6. 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
  7. 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
  8. 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
    启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用

2.zk原理?zk都可以做什么?paxos算法知道吗?说一下原理和实现?
ZooKeeper数据模型的结构与Unix文件系统很类似,整体上可以看作是一棵树,每个节点称做一个ZNode。每个ZNode都可以通过其路径唯一标识,比如上图中第三层的第一个ZNode, 它的路径是/app1/c1。在每个ZNode上可存储少量数据

总体说来,paxos就是通过两个阶段确定一个决议:Phase1:确定谁的编号最高,只有编号最高者才有权利提交proposal;Phase2:编号最高者提交proposal,如果没有其他节点提出更高编号的proposal,则该提案会被顺利通过;否则,整个过程就会重来。你编号高,我比你更高,反复如此,算法永远无法结束,这叫活锁。FLP Impossibility已经证明,在异步通信中不存在任何一致性算法,活锁便是Paxos无法解决的硬伤。

3.dubbo支持哪些序列化协议?hessian?他的数据结构呢?PB知道吗?为啥PB是效率最高的?
dubbo共支持如下几种通信协议:

dubbo://
rmi://
hessian://
http://
webservice://
thrift://
memcached://
redis://

4.netty?netty可以干啥?NIO BIO AIO都是什么?有什么区别?

5.dubbo的复制均衡和高可用测略有那些?动态代理测略呢?

Dubbo提供了多种均衡策略,缺省为random随机调用
随机,按权重设置随机概率。
轮循,按公约后的权重设置轮循比率。
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
一致性Hash,相同参数的请求总是发到同一提供者。

6.为什么要进行系统拆分?拆分不用dubbo可以么?dubbo和thrift有什么区别?
thrift其实只是一个跨平台的序列化协议,跟dubbo中使用的hessian2或json等价。

分布式消息队列
1.为什么使用消息队列?消息队列有什么有点和缺点?

2.如何保证消息队列的高可用?如何保证消息不被重复消费?
参照rabbitmq ack机制,对消费的数据预存在某个位置,消费端一定要提供消费回馈,不然数据持久保存在那

在数据生产时避免数据丢失的方法:
只要能避免上述两种情况,那么就可以保证消息不会被丢失。
1)就是说在同步模式的时候,确认机制设置为-1,也就是让消息写入leader和所有的副本。
2)还有,在异步模式下,如果消息发出去了,但还没有收到确认的时候,缓冲池满了,在配置文件中设置成不限制阻塞超时的时间,也就说让生产端一直阻塞,这样也能保证数据不会丢失。
在数据消费时,避免数据丢失的方法:如果使用了storm,要开启storm的ackfail机制;如果没有使用storm,确认数据被完成处理之后,再更新offset值。低级API中需要手动控制offset值。

针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。

3.kafka,activemq,rocketmq,rabbitmq有什么优缺点?
http://blog.csdn.net/kaixuanfeng2012/article/details/72821874
rocketmq:顺序消息、消息重复rocketmq不保证(业务端去重)、事务消息
4.如何让你实现一个消息队列,如何进行架构设计?说一下思路
mq 幂等 顺序 延迟

顺序性:保证生产者 - MQServer - 消费者是一对一对一的关系
分布式搜索引擎
1,es的工作过程是如何实现的?如何实现分布式的?
https://www.cnblogs.com/tgzhu/p/6098339.html
https://www.cnblogs.com/licongyu/p/5466992.html
路由和分片
分片
文档在索引的时候,需要确定文档存放到哪个分片上去。(通过把 _id 作为 routing 来计算 shard)
文档在检索的时候,需要确定文档处在具体哪个分片上。(通过把 _id 作为 routing 来计算 shard)
2.es在数据量很大的情况下如何提高查询效率?(10亿)
亿级规模的ES查询优化实战
能用filter就不用query
filter拿到相应的doc后不计算score不用排序
query会对符合条件的doc计算score并进行排序
filter的查询速度比query快很多

增加相关cache的配置
indices.cache.filter.size: 30%
indices.fielddata.cache.size: 60%
index.cache.field.type: soft
indices.breaker.fielddata.limit: 70%

优化方案——总结
能用filter就不用query
增加冗余字段将部分range aggregation查询变成terms aggregation
为常用字段增加配置,将fielddata的loading设成eager,尽量多加载到内存
增加集群的缓存资源,把内存尽量多的用起来
Global ordinals
Index warmer
调整aggregation的collect_mode
上SSD
使用bulk请求
并且每个请求不超过几十M,因为太大会导致内存使用过大
如何提高ES的性能
不要返回较大的结果集

ES是设计成一个搜索引擎的,只擅长返回匹配查询较少文档,如果需要返回非常多的文档需要使用Scroll。

避免稀疏

因为ES是基于Lucene来索引和存储数据的,所以对稠密的数据更有效。Lucene能够有效的确定文档是通过一个整数的文档id,无论有没有数据都会话费一个字节存储id。稀疏主要影响norms和doc_values,一些可以避免稀疏的推荐:

避免将不相关的数据放到相同的索引中

规范的文档结构

使用相同的字段名来保存同样的数据。

避免类型

不用norms和doc_values在稀疏字段

调整索引速度
使用bulk请求

并且每个请求不超过几十M,因为太大会导致内存使用过大

使用 multiple workers/threads发送数据到ES

多进程或者线程,如果看到TOO_MANY_REQUESTS (429)和EsRejectedExecutionException则说明ES跟不上索引的速度,当集群的I/O或者CPU饱和就得到了工作者的数量。

增加刷新间隔

index.refresh_interval默认是1s,可以改成30s以减少合并压力。

在加载大量数据时候可以暂时不用refresh和repliccas

index.refresh_interval to -1 and index.number_of_replicas to 0

禁用swapping

禁用swapping

给文件缓存分配内存

缓存是用来缓存I/O操作的,至少用一般的内存来运行ES文件缓存。

使用更快的硬件

使用SSD作为存储设备。
使用本地存储,避免使用NFS或者SMB
注意使用虚拟存储,比如亚马逊的EBS
索引缓冲大小

indices.memory.index_buffer_size通常是JVM的0.1,确保他足够处理至多512MB的索引。

调整搜索速度
给文件系统缓存大内存

至少给可用内存的一半到文件系统缓存。

使用更快的硬件

使用SSD作为存储设备。
使用性能更好的CPU,高并发
使用本地存储,避免使用NFS或者SMB
注意使用虚拟存储,比如亚马逊的EBS
文档建模

避免链接,嵌套会使查询慢几倍,而亲自关系能使查询慢几百倍,所以如果同样的问题可以通过没有链接的非规范回答就可以提升速度。

预索引数据

不明觉厉

映射

数值型数据不一定要映射成整形或者长整型

避免scripts

如果实在要使用,就用painless和expressions

强势合并只读索引

https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-forcemerge.html
不要强势合并正在写的索引

准备全局顺序

准备文件系统缓存

index.store.preload,如果内存不是很大会使搜索变得缓慢。

调整磁盘使用
禁用不需要的功能

不需要过滤时可以禁用索引“index”:false
如果你不需要text字段的score,可以禁用”norms”:false
如果不需要短语查询可以不索引positions"indexe_options":“freqs”
不用默认的动态字符串匹配

不要使用_all

使用best_compression

使用最小的足够用的数值类型

byte,short,integer,long
half_float,float,double

https://www.cnblogs.com/cutd/p/5800795.html

3.es的查询是一个怎么样的过程?底层的lucence介绍一下?到排索引知道吗?
es和mongo有什么区别?什么场景下使用?
深分页场景ES性能极差,ES应该主要还是搜索、以及目前正在加强的用于数据分析。做存储有些深分页查询场景因性能无法使用。
Es更新后即时查询较差,延迟高。mongo在并发条件下也强于es

高并发高可用架构设计

1,如何设计一个高并发高可用系统?
高并发原则
无状态
拆分
服务化
消息队列
数据异构
缓存银弹
高可用原则:
降级
限流
切流量
可回滚
3业务设计原则
防重设计
幂等设计
流程定义
状态与状态机
后台系统操作可反馈
后台系统审批化
文档注释
备份
2,如何限流?工作中怎么做的?说一下具体实现?

限制总并发数(比如数据库连接池、线程池)
限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)
限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)
限制远程接口调用速率
限制MQ的消费速率。
可以根据网络连接数、网络流量、CPU或内存负载等来限流

https://www.jianshu.com/p/d6fb865b970b

3.缓存如何使用的?缓存使用不当会造成什么后果?
http://blog.csdn.net/u011277123/article/details/53579168

4.如何熔断?熔断框架有哪些?具体实现原理知道吗?
类似现实世界中的“保险丝“,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时。
熔断的触发条件可以依据不同的场景有所不同,比如统计一个时间窗口内失败的调用次数。
NetFlix Hydrix
Hystrix为每一个依赖服务维护一个线程池(或者信号量),当线程池占满,该依赖服务将会立即拒绝服务而不是排队等待
每个依赖服务都被隔离开来,Hystrix 会严格控制其对资源的占用,并在任何失效发生时,执行失败回退逻辑。

hystrix语义为“豪猪”,具有自我保护的能力。hystrix的出现即为解决雪崩效应,它通过四个方面的机制来解决这个问题

隔离(线程池隔离和信号量隔离):限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。
优雅的降级机制:超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
融断:当失败率达到阀值自动触发降级(如因网络故障/超时造成的失败率高),熔断器触发的快速失败会进行快速恢复。
缓存:提供了请求缓存、请求合并实现。
支持实时监控、报警、控制(修改配置)
(1)线程池隔离模式:使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
(2)信号量隔离模式:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃改类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)

5.如何降级?如何进行系统拆分?如何进行数据库拆分?
降级需要对下层依赖的业务分级,把产生故障的丢了,换一个轻量级的方案,是一种退而求其次的方法。
根据业务场景的不同,一般采用以下两种模式:
第一种(最常用)如果服务失败,则我们通过fallback进行降级,返回静态值。

第二种采用服务级联的模式,如果第一个服务失败,则调用备用服务,例如失败重试或者访问缓存失败再去取数据库。服务级联的目的则是尽最大努力保证返回数据的成功性,但如果考虑不充分,则有可能导致级联的服务崩溃(比如,缓存失败了,把全部流量打到数据库,瞬间导致数据库挂掉)。因此级联模式,也要慎用,增加了管理的难度。

通信协议
1,说一下TCP/IP四层?
应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
互连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial
Line等)来传送数据。

2.http的工作流程?http1.0 1.1 2.0有哪些区别?
地址解析
如用客户端浏览器请求这个页面:http://localhost.com:8080/index.htm
从中分解出协议名、主机名、端口、对象路径等部分,对于我们的这个地址,解析得到的结果如下: 协议名:http 主机名:localhost.com( 在这一步,需要域名系统DNS解析域名localhost.com,得主机的IP地址。) 端口:8080 对象路径:/index.htm
封装http请求数据包
封装成tcp包,建立tcp连接(三次握手)
在HTTP工作开始之前,客户机(Web浏览器)首先要通过网络与服务器建立连接,该连接是通过TCP来完成的
客户端发送请求
服务器响应
服务器关闭tcp连接
————————————
长连接
HTTP 1.0需要使用keep-alive参数来告知服务器端要建立一个长连接,而HTTP1.1默认支持长连接。
HTTP是基于TCP/IP协议的,创建一个TCP连接是需要经过三次握手的,有一定的开销,如果每次通讯都要重新建立连接的话,对性能有影响。因此最好能维持一个长连接,可以用个长连接来发多个请求。
节约带宽
HTTP 1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,否则返回401。客户端如果接受到100,才开始把请求body发送到服务器。
这样当服务器返回401的时候,客户端就可以不用发送请求body了,节约了带宽。
另外HTTP还支持传送内容的一部分。这样当客户端已经有一部分的资源后,只需要跟服务器请求另外的部分资源即可。这是支持文件断点续传的基础。
HOST域
现在可以web server例如tomat,设置虚拟站点是非常常见的,也即是说,web server上的多个虚拟站点可以共享同一个ip和端口。
HTTP1.0是没有host域的,HTTP1.1才支持这个参数。
HTTP1.1 HTTP 2.0主要区别
多路复用
HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。
当然HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。
TCP连接有一个预热和保护的过程,先检查数据是否传送成功,一旦成功过,则慢慢加大传输速度。因此对应瞬时并发的连接,服务器的响应就会变慢。所以最好能使用一个建立好的连接,并且这个连接可以支持瞬时并发的请求。
关于多路复用,可以参看学习NIO 。
数据压缩
HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。
服务器推送
意思是说,当我们对支持HTTP2.0的web server请求数据的时候,服务器会顺便把一些客户端需要的资源一起推送到客户端,免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源。
服务器端推送的这些资源其实存在客户端的某处地方,客户端直接从本地加载这些资源就可以了,不用走网络,速度自然是快很多的。
3.TCP三次握手,4次分手工作流程?画图?为什么不是5次或者2次?
4.画一下https的工作流程?如何实现?如何防止被抓包?
客户端发送请求到服务器端
服务器端返回证书和公开密钥,公开密钥作为证书的一部分而存在
客户端验证证书和公开密钥的有效性,如果有效,则生成共享密钥并使用公开密钥加密发送到服务器端
服务器端使用私有密钥解密数据,并使用收到的共享密钥加密数据,发送到客户端
客户端使用共享密钥解密数据
SSL加密建立………
—————
验证消息的确来自微信服务器
    通过 timestamp, nonce 以及双方约定好的 token 构成一个签名,能够验证信息是否来自微信。

在这里并不适用,因为api就是由用户调用的。

算法
1,有一个文件,45亿个阿拉伯数字,,如何去重?如何找出最大的那个?
方案1:申请512M的内存(2^32/8=512MB),一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。
mapreduce
数据结构
2.二叉树,红黑树

基础

1.class.forname和classloader有什么区别
java中class.forName()和classLoader都可用来对类进行加载。
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象

2.arraylist,hashmap
ArrayList底层以数组实现,允许重复,默认第一次插入元素时创建数组的大小为10,超出限制时会增加50%的容量,每次扩容都底层采用System.arrayCopy()复制到新的数组,初始化时最好能给出数组大小的预估值。

和LinkedList的区别

1、ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2、对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3、对于新增和删除操作add和remove(不是在尾部添加删除),LinkedList比较占优势,因为ArrayList要移动数据。

和Vector的区别
1、Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
2、Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。
3、Vector还有一个子类Stack.

LinkedList以双向链表实现,允许重复。(如下Node的实现)并保留头指针和尾指针。
链表无容量限制,但双向链表本身使用了更多空间,也需要额外的链表指针操作。   
按下标访问元素—get(i)/set(i,e) 要悲剧的遍历链表将指针移动到位(如果i>数组大小的一半,会从末尾移起)。
插入、删除元素时修改前后节点的指针即可,但还是要遍历部分链表的指针才能移动到下标所指的位置,只有在链表两头的操作—add(), addFirst(),removeLast()或用iterator()上的remove()能省掉指针的移动

HashMap
工作原理:通过hash算法,通过put和get存储和获取对象。
存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Factor则resize为原来的2倍)。如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来。如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。
获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。

HashMap 包含如下几个构造器:
HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
ashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。
HashMap的基础构造器HashMap(int initialCapacity, float loadFactor)带有两个参数,它们是初始容量initialCapacity和负载因子loadFactor。
负载因子loadFactor衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

从Java 8开始,HashMap,ConcurrentHashMap和LinkedHashMap在处理频繁冲突时将使用平衡树来代替链表,当同一hash桶中的元素数量超过特定的值便会由链表切换到平衡树,这会将get()方法的性能从O(n)提高到O(logn)。


1.redis线程模型,底层实现,如何实现高并发高可用持久化,redis集群的扩容同步,redis序列化的方式,如何防止穿透
Redis 基于 Reactor 模式开发了自己的网络事件处理器: 这个处理器被称为文件事件处理器(file event handler):
文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
虽然文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性。

Redis sentinel(哨兵)模块已经被集成在redis2.4+的版本中,主要为 Redis 主从架构(M-S)的系统提供:
监控:系统中master/slave 可用性监控
通知:当被监控的redis实例出问题时,通知应用程序
自动故障转移,M-S角色转换等能力。从一个方面说是提高了redis集群的可用性.
Redis Sentinel 是一个分布式系统,你可以在一个系统中运行多个 Sentinel 进程(progress),这些进程使用流言协议(gossip protocols)来接收关于master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。
sentinel的一些设计思路和zookeeper非常类似,事实上,你可以不使用sentinel,而是自己开发一个监控redis的zk客户端也能够完成相应的设计要求。

序列化的目的是将一个实现了Serializable接口的对象转换成一个字节序列,可以。 把该字节序列保存起来(例如:保存在一个文件里),以后可以随时将该字节序列恢复为原来的对象.

增量同步
Redis增量同步主要指Slave完成初始化后开始正常工作时,Master发生的写操作同步到Slave的过程。通常情况下,Master每执行一个写命令就会向Slave发送相同的写命令,然后Slave接收并执行。
全量同步
  Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
  1)从服务器连接主服务器,发送SYNC命令;
  2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
  3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
  4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
  5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
  6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

缓存穿透:

认识

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决办法:

对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。还有最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
也可以采用一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存雪崩

认识

如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。
解决方法

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存
不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀
做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

2.mq的原理 如何保证消息的可靠性传输 处理消息丢失和解决消息幂等。 mq消息的延迟和过期失效 自己设计一个消息中间件
可靠性:消息的落地,超时重传和确认
幂等
上半场生成全局唯一ID,业务无关,保证同一条消息只投递一次,由mq保证; 下半场幂等要求有全局唯一的businessid 保证只消费一次,业务唯一,对MQ透明。
mq的延迟
最常见方案启动一个cron定时任务,将48小时未拼分的滴滴订单取出评价为5星。效率低下。
可以设计一个环形队列,1-3600,每个slot是一个Set,里面有属性代表第几圈扫描到此开始执行任务。
//redis zset 实现
用Map来存储元数据。id作为key,整个消息结构序列化(json/…)之后作为value,放入元消息池中。
将id放入其中(有N个)一个zset有序列表中,以createTime+delay+priority作为score。修改状态为正在延迟中
使用timer实时监控zset有序列表中top 10的数据 。 如果数据score<=当前时间毫秒就取出来,根据topic重新放入一个新的可消费列表(list)中,在zset中删除已经取出来的数据,并修改状态为待消费
客户端获取数据只需要从可消费队列中获取就可以了。并且状态必须为待消费 运行时间需要<=当前时间的 如果不满足 重新放入zset列表中,修改状态为正在延迟。如果满足修改状态为已消费。或者直接删除元数据。

3.dubbo的原理 spi思想 如何进行服务治理 dubbo的服务降级 失败重试 设计自己的RPC框架
http://blog.csdn.net/chao_19/article/details/51764150
spi:
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
通过mock的配置,可以很好的实现dubbo服务降级

1.超时设置
DUBBO消费端设置超时时间需要根据业务实际情况来设定,
如果设置的时间太短,一些复杂业务需要很长时间完成,导致在设定的超时时间内无法完成正常的业务处理。
这样消费端达到超时时间,那么dubbo会进行重试机制,不合理的重试在一些特殊的业务场景下可能会引发很多问题,需要合理设置接口超时时间。
比如发送邮件,可能就会发出多份重复邮件,执行注册请求时,就会插入多条重复的注册数据。
(1)合理配置超时和重连的思路
对于核心的服务中心,去除dubbo超时重试机制,并重新评估设置超时时间。
业务处理代码必须放在服务端,客户端只做参数验证和服务调用,不涉及业务流程处理
2.重连机制
dubbo在调用服务不成功时,默认会重试2次。
Dubbo的路由机制,会把超时的请求路由到其他机器上,而不是本机尝试,所以 dubbo的重试机器也能一定程度的保证服务的质量。
但是如果不合理的配置重试次数,当失败时会进行重试多次,这样在某个时间点出现性能问题,调用方再连续重复调用,
系统请求变为正常值的retries倍,系统压力会大增,容易引起服务雪崩,需要根据业务情况规划好如何进行异常处理,何时进行重试。

4.AQS原理 哪些地方用到了AQS/CAS ConcurrentHashMap原理 java线程模型。并发下出现那些问题

5.synchronized如何实现,和lock的区别
synchronized:
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。

ReentrantLock:
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

Atomic:
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。
6.mysql主从同步延迟处理
2.MySQL数据库主从同步延迟是怎么产生的。

答:当主库的TPS并发较高时,产生的DDL数量超过slave一个sql线程所能承受的范围,那么延时就产生了,当然还有就是可能与slave的大型query语句产生了锁等待。

3.MySQL数据库主从同步延迟解决方案

答:最简单的减少slave同步延时的方案就是在架构上做优化,尽量让主库的DDL快速执行。还有就是主库是写,对数据安全性较高,比如sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之类的设置,而slave则不需要这么高的数据安全,完全可以讲sync_binlog设置为0或者关闭binlog,innodb_flushlog也可以设置为0来提高sql的执行效率。另外就是使用比主库更好的硬件设备作为slave。

4.MySQL数据库主从同步延迟产生的因素。

  1. 网络延迟
  2. master负载
  3. slave负载
    一般的做法是,使用多台slave来分摊读请求,再从这些slave中取一台专用的服务器,只作为备份用,不进行其他任何操作,就能相对最大限度地达到’实时’的要求了
    7.mybatis原理和mapper调用接口的原理
    XML不用说了,用来定义SQL语句
    Mapper的作用是用来绑定XML和程序之间的关系
    Mapper中必须提供与XML中id名称相同的接口方法,这个定义我想你已经知道了

根据MyBatis的日志显示,程序被加载时
MyBatis从XML中读取出各个SQL语句,然后根据XML指定的MAPPER位置绑定相应的接口
然后MyBatis会在自身内容进行动态代理,将各Mapper接口进行动态实现,所以虽然你没有写任何的具体JDBC代码,但实际上MyBatis已经为你做好了这些事情
然后你在Mapper.XXXXX具体调用的时候,就可以操作数据库了

简单的来说,XML保存了你的SQL逻辑
MAPPER则起到了沟通程序和SQL之间的相互访问
8.tomcat线程模型
一个或多个Acceptor线程,每个线程都有自己的Selector,Acceptor只负责accept新的连接,一旦连接建立之后就将连接注册到其他Worker线程中。
多个Worker线程,有时候也叫IO线程,就是专门负责IO读写的。方式实现方式就是有专门的线程负责IO事件监听,这些线程有自己的Selector,一旦监听到有IO读写事件,并不是像第一种实现方式那样(自己去执行IO操作),而是将IO操作封装成一个Runnable交给Worker线程池来执行,这种情况每个连接可能会被多个线程同时操作,相比第一种并发性提高了,但是也可能引来多线程问题,在处理上要更加谨慎些。tomcat的NIO模型就是第二种。
模块化:
Connector 组件是 Tomcat 中两个核心组件之一,它的主要任务是负责接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的 Request 和 Response 对象传给处理这个请求的线程,处理这个请求的线程就是 Container 组件要做的事了.
Container 是容器的父接口,所有子容器都必须实现这个接口,Container 容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine、Host、Context、Wrapper,这四个组件不是平行的,而是父子关系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper

9.分布式事务的场景和解决方案,redis和zk实现分布式锁
事务必须满足传统事务的特性,即原子性,一致性,分离性和持久性。但是分布式事务处理过程中,

  1. 当数据库单表一年产生的数据超过1000W,那么就要考虑分库分表,具体分库分表的原理在此不做解释,以后有空详细说,简单的说就是原来的一个数据库变成了多个数据库。这时候,如果一个操作既访问01库,又访问02库,而且要保证数据的一致性,那么就要用到分布式事务。
  2. 所谓的SOA化,就是业务的服务化。比如原来单机支撑了整个电商网站,现在对整个网站进行拆解,分离出了订单中心、用户中心、库存中心。对于订单中心,有 专门的数据库存储订单信息,用户中心也有专门的数据库存储用户信息,库存中心也会有专门的数据库存储库存信息。这时候如果要同时对订单和库存进行操作,那 么就会涉及到订单数据库和库存数据库,为了保证数据一致性,就需要用到分布式事务

常见解决方案:

  1. 基于XA协议的两阶段提交
    分布式事务通常采用2PC协议,全称Two Phase Commitment Protocol。该协议主要为了解决在分布式数据库场景下,所有节点间数据一致性的问题。分布式事务通过2PC协议将提交分成两个阶段:
  2. prepare;
  3. commit/rollback
    阶段一为准备(prepare)阶段。即所有的参与者准备执行事务并锁住需要的资源。参与者ready时,向transaction manager报告已准备就绪。
    阶段二为提交阶段(commit)。当transaction manager确认所有参与者都ready后,向所有参与者发送commit命令。
  4. 消息事务+最终一致性
    所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败

10.TCP三次握手和4次挥手为什么要这样做

11.项目中的挑战,怎么解决

12.设计一个高并发/高可用系统
https://www.cnblogs.com/wangzhongqiu/p/6557596.html
13.分布式环境下保持redis和mysql的数据一致性
Redis只用作cache,写请求只交给MySQL处理。否则你就要自己去解决一个分布式事务的问题
缓存只做失效,不做更新
高一致性情况下不要用缓存

14.dubbo redis mq里面踩过的坑
dubbo因为超时重试 产生雪崩现象,系统相应缓慢
redis批处理可以用pipeline模式提高效率
总结:
1、缓存穿透:查询一个必然不存在的数据。比如文章表,查询一个不存在的id,每次都会访问DB,如果有人恶意破坏,很可能直接对DB造成影响。
2、缓存失效:如果缓存集中在一段时间内失效,DB的压力凸显。这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。
当发生大量的缓存穿透,例如对某个失效的缓存的大并发访问就造成了缓存雪崩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王知无(import_bigdata)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值