1. 两次点击,如何防止重复下订单
token机制解决
1.客户端申请token
2.服务器端生成token,并存放在session中,同时将token发送到客户端
3.客户端存储token,在请求提交时,同时发送token信息
4.服务器端统一拦截同一个用户的所有请求,验证当前请求是否需要被验证 (不是所有请求都验证重复提交)
5.验证session中token是否和用户请求中的token一致,如果一致则放行
6.session清除会话中的token,为下一次的token生成作准备
7.并发重复请求到来,验证token和请求token不一致,请求被拒绝
2. 数据库表设计,索引设计
①数据库表设计:
在数据冗余和处理速度之间找到合适的平衡点。原则是相对的,不是绝对的。
做表设计,读懂需求就对了。先不要管性能,先实现需求。表设计好了,写SQL的时候再考虑该合并,合并,该拆分,拆分。另外最关键的就是搞清楚一对一还是一对多。
保证每列的原子性,不可分解,意思表达要清楚,不能含糊,高度概括字段的含义,能用一个字段表达清楚的绝不使用第二个字段,可以用两个字段表达清楚的绝不使用一个字段
表及其字段之间的关系, 应尽量满足第三范式。但是,满足第三范式的数据库设计,往往不是最好的设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的。
最好做好静态表和动态表的分离。这里解释一下静态表和动态表的含义,静态表:存储着一些固定不变的资源,比如城市/地区名/国家。动态表:一些频繁修改的表
不要有null值,有null值的话,数据库在进行索引的时候查询的时间更久,从而浪费更多的时间!建议可以为null的值转换成not null default
2张表的多对多的表关系,最好设计成3张表,即增加一张中间表,之前的两张表和中间表的关系是一对多的关系。
建表的时候,字段长度尽量要比实际业务的字段大3-5个字段左右(考虑到合理性和伸缩性),最好是2的n次方幂值。不能建比实际业务太大的字段长度,这是因为如果字段长度过大,在进行查询的时候索引在B-Tree树上遍历会越耗费时间,从而查询的时间会越久;但是绝对不能建小,否则mysql数据会报错,程序会抛出异常;
对于频繁修改的字段(一般是指状态类字段)最好用独立的数字或者单个字母去表示,不用使用汉字或者英文
数据库不要存储任何资源文件,比如照片/视频/网站等,可以用文件路径/外链用来代替,这样可以在程序中通过路径,链接等来进行索引
关系映射:多对一或者一对多的关系,关联一张表最好通过id去建立关系,而不是去做重复数据,这样做最大的好处就是中间的关系表比较清楚明白。
通过单一字段表示该行记录是否可用,通过一个单一字段去控制表是否可用,比如通常起名为isVaild,预制的含义为0为有效,1为无效,这样便于以后我们去剔除数据或者重整数据,使其成为boolean性质的数据 更加便于我们去操控。
预留备用字段:在设计一张表的时候应该预制2到3个空白字段,用于以后的扩展,因为你也不是确定这张表以后不会扩展。
②索引设计
最适合索引的列是出现在 WHERE子句中的列,或连接子句中指定的列,而不是出现在 SELECT 关键字后的选择列表中的列。
使用惟一索引。考虑某列中值的分布。索引的列的基数越大,索引的效果越好。
如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。
利用最左前缀。
不要过度索引。
对于 InnoDB 存储引擎的表,记录默认会按照一定的顺序保存,主键要尽可能选择较短的数据类型,可以有效地减少索引的磁盘占用,提高索引的缓存效果。
3. Redis的缓存淘汰策略,更新策略
1)LRU/LFU/FIFO算法剔除:剔除算法通常用于缓存使用量超过了预设的最大值时候,如何对现有的数据进行剔除。例如Redis使用maxmemory-policy这个配置作为内存最大值后对于数据的剔除策略。
2)超时剔除:通过给缓存数据设置过期时间,让其在过期时间后自动删除,例如Redis提供的expire命令。如果业务可以容忍一段时间内,缓存层数据和存储层数据不一致,那么可以为其设置过期时间。在数据过期后,再从真实数据源获取数据,重新放到缓存并设置过期时间。例如一个视频的描述信息,可以容忍几分钟内数据不一致,但是涉及交易
方面的业务,后果可想而知。
3)主动更新:应用方对于数据的一致性要求高,需要在真实数据更新后,立即更新缓存数据。例如可以利用消息系统或者其他方式通知缓存更新。
4. Dubbo、netty、RPC介绍原理
①Dubbo:
主要组件:
Provider:暴露服务方称之为“服务提供者”。
Consumer:调用远程服务方称之为“服务消费者”。
Registry:服务注册与发现的中心目录服务称之为“服务注册中心”。
Monitor:统计服务的调用次数和调用时间的日志服务称之为“服务监控中心”。
注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
监控中心宕掉不影响使用,只是丢失部分采样数据
数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
注册中心对等集群,任意一台宕掉后,将自动切换到另一台
注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
服务提供者无状态,任意一台宕掉后,不影响使用
服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者
②netty
主要组件:
1)Channel
Channel 是java NIO的一个基本构造,可以把channel看作是传入或者传出的数据载体,可以被打开或者关闭,连接或者断开连接。简单来说其实就是我们平常网络编程中经常使用的socket套接字对象。
2)EventLoop
EventLoop定义了Netty的核心对象,用于处理IO事件,多线程模型、并发。EventLoop及其相关的设计实现,需要了解以下几点:
1、一个EventLoopGroup包含一个或者多个EventLoop;
2、一个EventLoop在它的生命周期内只和一个Thread绑定;
3、所有有EventLoop处理的I/O事件都将在它专有的Thread上被处理;
4、一个Channel在它的生命周期内只注册于一个EventLoop;
5、一个EventLoop可能会被分配给一个货多个Channel;
其实我们可以简单的把EventLoop及其相关的实现NioEventLoop、NioEventLoopGroup等理解为netty针对我们网络编程时创建的多线程进行了封装和优化,构建了自己的线程模型。
ChannelHandler和ChannelPipeline
ChannelHandler其实就是用于负责处理接收和发送数据的的业务逻辑,Netty中可以注册多个handler,以链式的方式进行处理,根据继承接口的不同,实现的顺序也不同。
1)ChannelInboundHandler:对接收的信息进行处理。一般用来执行解码、读取客户端数据、进行业务处理等。如ByteToMessageDecoder;
2)ChannelOutboundHandler:对发送的信息进行处理,一般用来进行编码、发送报文到客户端。如MessageToByteEncoder;
3)而ChannelPipeline为ChannelHandler链提供了容器。
ByteBuf
网络数据的操作归根到底是字节的操作,Netty的ByteBuf做为一个强大高效的字节容器,提供了更加丰富的API用于字节的操作,同时保持了卓越的功能性和灵活性;
③ RPC
通过IPC和RPC,程序能利用其它程序或计算机处理的进程。客户机/服务器模式计算把远程过程调用与其它技术(如消息传递)一道,作为系统间通信的一种机制。客户机执行自己的任务,但靠服务器提供后端文件服务。RPC为客户机提供向后端服务器申请服务的通信机制,如图R-4所示。如果你把客户机/服务器应用程序想作是一个分离的程序,服务器能运行数据访问部分,因为它离数据最近,客户机能运行数据表示和与用户交互的前端部分。这样,远程过程调用可看作是把分割的程序通过网络重组的部件。LPC有时也称耦合(Coupling)机制。
用这种方式分割程序,当用户要访问数据时就无需每次拷贝整个数据库或它的大部分程序到用户系统。其实,服务器只处理请求,甚至只执行一些数据计算,把得出的结果再发送给用户。因为当数据存放在一个地方时,数据库同步很容易实现,所以多个用户可同时访问相同的数据。
5. 限流算法
有以下算法:
①计算器算法
计数器算法是使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数。
②滑动窗口算法
滑动窗口算法是将时间周期分为N个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。
③漏桶算法
漏桶算法是访问请求到达时直接放入漏桶,如当前容量已达到上限(限流值),则进行丢弃(触发限流策略)。漏桶以固定的速率进行释放访问请求(即请求通过),直到漏桶为空。
④令牌桶算法
令牌桶算法是程序以r(r=时间周期/限流值)的速度向令牌桶中增加令牌,直到令牌桶满,请求到达时向令牌桶请求令牌,如获取到令牌则通过请求,否则触发限流策略
6. Zk挂了怎么办
监控中心宕掉不影响使用,只是丢失部分采样数据
数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
注册中心对等集群,任意一台宕掉后,将自动切换到另一台
注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
服务提供者无状态,任意一台宕掉后,不影响使用
服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
服务消费者在注册中心宕机后让然可以调用服务提供者提供的服务。但是注册中心宕机后我们不能再注册新的服务。
7. 分布式锁的实现方式,zk实现和Redis实现的比较
①基于数据库实现排他锁
用数据库实现分布式锁的方式和redis分布式锁的实现方式类似,这里采用数据库表的唯一键的形式。如果同一个时刻,多个线程同时向一个表中插入同样的记录,由于唯一键的原因,只能有一个线程插入成功。
②基于redis的实现
获取锁时,使用redis的命令setnx、pexpire(提供基于毫秒的过期时间,expire提供基于秒的过期时间)+ lua脚本(保证脚本中的命令被一起执行,不间断)来实现分布式锁。删除锁时,先执行get,如果获取的值是自己设置的,则执行del操作,同时,这两个操作也放在lua脚本中执行,来保证原子性。
③基于zookeeper实现
zookeeper实现分布式锁的原理就是多个节点同时在一个指定的节点下面创建临时会话顺序节点,谁创建的节点序号最小,谁就获得了锁,并且其他节点就会监听序号比自己小的节点,一旦序号比自己小的节点被删除了,其他节点就会得到相应的事件,然后查看自己是否为序号最小的节点,如果是,则获取锁。
8. 秒杀场景的设计,应付突然的爆发流量
①设计理念:
限流:鉴于只有少部分用户能够秒杀成功,所以要限制大部分流量,只允许少部分流量进入服务后端。
削峰:对于秒杀系统瞬时会有大量用户涌入,所以在抢购一开始会有很高的瞬间峰值。高峰值流量是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计秒杀系统很重要的思路。实现削峰的常用的方法有利用缓存和消息中间件等技术。
异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。
内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。
可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了。像淘宝、京东等双十一活动时会增加大量机器应对交易高峰。
②思路:
将请求拦截在系统上游,降低下游压力:秒杀系统特点是并发量极大,但实际秒杀成功的请求数量却很少,所以如果不在前端拦截很可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时。
充分利用缓存:利用缓存可极大提高系统读写速度。
消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。
9. 分布式数据一致性
大部份使用传统关系型数据库的DBA在看到“数据一致性”时,第一反应可能都是数据在跨表事务中的数据一致性场景。但是本文介绍的“数据一致性”,指的是“数据在多份副本中存储时,如何保障数据的一致性”场景。
由于在大数据领域,数据的安全不再由硬件来保证,而是通过软件手段,通过同时将数据写入到多个副本中,来确保数据的安全。数据库在同时向多个副本写入记录时,如何确保每个副本数据一致,称为“数据一致性”。
10. 一致性哈希
一致性哈希算法是当前较主流的分布式哈希表协议之一,它对简单哈希算法进行了修正,解决了热点(hotPot)问题,它的原理分为两步:
首先,对存储节点的哈希值进行计算,其将存储空间抽象为一个环,将存储节点配置到环上。环上所有的节点都有一个值。其次,对数据进行哈希计算,按顺时针方向将其映射到离其最近的节点上去。当有节点出现故障离线时,按照算法的映射方法,受影响的仅仅为环上故障节点开始逆时针方向至下一个节点之间区间的数据对象,而这些对象本身就是映射到故障节点之上的。当有节点增加时,比如,在节点A和B之间重新添加一个节点H,受影响的也仅仅是节点H逆时针遍历直到B之间的数据对象,将这些重新映射到H上即可,因此,当有节点出现变动时,不会使得整个存储空间上的数据都进行重新映射,解决了简单哈希算法增删节点,重新映射所有数据带来的效率低下的问题。
一致性哈希算法作为分布式存储领域的一个重要算法,它基本解决了以P2P为代表的存储环境中一个关键的问题——如何在动态的网络拓扑中对数据进行分发和选择路由。在算法所构成的存储拓扑中,每个存储节点仅需维护少量相邻节点的信息,并且在节点加入/退出系统时,仅有相关的少量节点参与到拓扑的维护中,这使得一致性哈希算法成为一个具有实用意义的DHT(DistributedHashTable,分布式哈希表)算法。但是一致性哈希算法尚有不足之处。第一,在查询过程中,查询消息要经过O(n)步(n代表系统内的节点总数)才能到达被查询的节点。不难想象,当系统规模非常大时,节点数量可能超过百万,这样的查询效率显然难以满足使用的需要。第二,当应用一致性哈希算法的分布式存储系统中添加或者删除新的物理节点时,要将下一个节点与之相关的数据迁移过来,查询命中率和存储效率下降,影响系统的整体性能。
11. 消息队列原理
本地通讯的情况,应用程序A和应用程序B运行于同一系统A,它们之间可以借助消息队列技术进行彼此的通讯:应用程序A向队列1发送一条信息,而当应用程序B需要时就可以得到该信息。
远程通讯的情况,如果信息传输的目标改为在系统B上的应用程序C,这种变化不会对应用程序A产生影响,应用程序A向队列2发送一条信息,系统A的MQ发现Q2所指向的目的队列实际上位于系统B,它将信息放到本地的一个特殊队列-传输队列(Transmission Queue)。我们建立一条从系统A到系统B的消息通道,消息通道代理将从传输队列中读取消息,并传递这条信息到系统B,然后等待确认。只有MQ接到系统B成功收到信息的确认之后,它才从传输队列中真正将该信息删除。如果通讯线路不通,或系统B不在运行,信息会留在传输队列中,直到被成功地传送到目的地。这是MQ最基本而最重要的技术–确保信息传输,并且是一次且仅一次(once-and-only-once)的传递。
MQ提供了用于应用集成的松耦合的连接方法,因为共享信息的应用不需要知道彼此物理位置(网络地址);不需要知道彼此间怎样建立通信;不需要同时处于运行状态;不需要在同样的操作系统或网络环境下运行。
12. 注解原理
注解的本质就是一个继承了 Annotation 接口的接口。有关这一点,你可以去反编译任意一个注解类,你会得到结果的。
一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。
而解析一个类或者方法的注解往往有两种形式,一种是编译期直接的扫描,一种是运行期反射。反射的事情我们待会说,而编译器的扫描指的是编译器在对 java 代码编译字节码的过程中会检测到某个类或者方法被一些注解修饰,这时它就会对于这些注解进行某些处理。
典型的就是注解 @Override,一旦编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的方法签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。
这一种情况只适用于那些编译器已经熟知的注解类,比如 JDK 内置的几个注解,而你自定义的注解,编译器是不知道你这个注解的作用的,当然也不知道该如何处理,往往只是会根据该注解的作用范围来选择是否编译进字节码文件,仅此而已。
13. 数据库原理,数据库中间件,索引优化
①数据库中间件:
1)Cobar
Cobar 是提供关系型数据库(MySQL)分布式服务的中间件,它可以让传统的数据库得到良好的线性扩展,并看上去还是一个数据库,对应用保持透明。
Cobar以Proxy的形式位于前台应用和实际数据库之间,对前台的开放的接口是MySQL通信协议。将前台SQL语句变更并按照数据分布规则发到合适的后台数据分库,再合并返回结果,模拟单库下的数据库行为。
2)MyCat
从定义和分类看,它是一个开源的分布式数据库系统,是一个实现了MySQL协议的Server,前端用户可以把它看做是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL Native Protocol与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。
MyCAT发展到目前的版本,已经不是一个单纯的MySQL代理了,它的后端可以支持MySQL, SQL Server, Oracle, DB2, PostgreSQL等主流数据库,也支持MongoDB这种新型NoSQL方式的存储,未来还会支持更多类型的存储。
MyCAT是一个强大的数据库中间件,不仅仅可以用作读写分离,以及分表分库、容灾管理,而且可以用于多租户应用开发、云平台基础设施,让你的架构具备很强的适应性和灵活性,借助于即将发布的MyCAT只能优化模块,系统的数据访问瓶颈和热点一目了然,根据这些统计分析数据,你可以自动或手工调整后端存储,将不同的表隐射到不同存储引擎上,而整个应用的代码一行也不用改变。
②索引优化
1)尽量选择惟一性索引;
2)为经常需要排序、分组和联合操作的字段建立索引;
3)为常作为查询条件的字段建立索引;
4)限制索引的数目,索引越多,更新和插入的效率越低;
5)尽量使用数据量少的索引;
6)组合索引中的尽量把能过滤掉更多数据的字段放在前面;
7)尽量使用前缀来索引,针对长文本,TEXT等类型;
8)删除不再使用或者很少使用的索引;
9)数据是动态变化中的,索引的使用也需要根据数据的变化而变化。
14. Ioc原理、aop原理和应用
①Ioc原理
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;是IoC 容器控制了对象;主要控制了外部资源获取(不只是对象包括比如文件等)。
有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;依赖对象的获取被反转了。
②aop原理
AOP的思想是,不去动原来的代码,而是基于原来代码产生代理对象,通过代理的方法,去包装原来的方法,就完成了对以前方法的增强。换句话说,AOP的底层原理就是动态代理的实现。
Aop的作用:可以进行日志记录,可以进行事务管理,可以进行安全控制,可以进行异常处理,可以进行性能统计
15. 大数据相关,MapReduce
MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)"和"Reduce(归约)",是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。 当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归约)函数,用来保证所有映射的键值对中的每一个共享相同的键组。
16. Docker原理
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上。
Dokcer可以封装任何有效的应用程序,几乎可以达在任何一台服务器之间运行相同的服务,换句话说开发者,只需构建一次应用,就可以在多平台上运行,运维人员只需要配置他们的服务,即可运行所有的应用。
同时有了容器还可以保证整个开发、测试和生产环境可以保持高度一致。
docker使用c/s架构,docker daemon 作为server端接受client的请求,并处理(创建、运行、分发容器),他们可以运行在一个机器上,也通过socket或者RESTful API通信
Docker底层的2个核心技术分别是Namespace和Control groups
Namespace 用来隔离各个容器
1)pid Namespace
不同用户的进程就是通过pid Namespace 隔离开的,且不同 Namespace 中可以有相同pid。
所有的LXC进程在Docker中的父进程为Docker进程,每个lxc进程具有不同的Namespace。
2)net Namespace
有了pid Namespace ,每个Namespace中的pid能够相互隔离,但是网络端口还是共享Host的端口。网络隔离是通过net Namespace 实现的,每个net Namespace 有独立的 network devices,IP address,IP routing tables,/proc/net 目录。这样每个container的网络就能隔离开来。Docker 默认采用veth的方式将container中的虚拟网卡同host上的一个Docker bridge:docker0 连接在一起。
3)ipc Namespace
container中进程交互还是采用Linux常见的进程间交互方法包括常见的信号量、消息队列和共享内存。container的进程间交互实际上还是Host上具有相同pid的Namespace中的进程间交互。
4)mnt Namespace
类似chroot,将一个进程放到一个特定的目录执行。mnt Namespace 允许不同Namespace的进程看到的文件结构不同,这样每个 Namespace 中的进程所看到的文件目录就被隔离开了。在container里头,看到的文件系统,就是一个完整的Linux系统,有/etc、/lib等,通过chroot实现。
5)uts Namespace
UTS(“UNIX Time-sharing System“)Namespace允许每个container拥有独立的hostname和domain name,使其在网络上可以被视作一个独立的节点而非Host上的一个进程。
6)user Namespace
每个container可以有不同的user 和 group id,也就是说可以在container内部用container内部的用户执行程序而非Host上的用户。
关注并私信我,发送文字“资料”即可获取精品Java学习课程。