1.final, finally, finalize 的区别 final—修饰符(关键字)若是一个类被声明为final,意味着它不能再派生出新的子类,不能做为父类被继承。所以一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,能够保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在之后的引用中只能读取,不可修改。被声明为final的方法也一样只能使用,不能重载。 finally—再异常处理时提供 finally 块来执行任何清除操做。若是抛出一个异常,那么相匹配的 catch 子句就会执行,而后控制就会进入 finally 块(若是有的话)。 finalize—方法名。Java 技术容许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去以前作必要的清理工做。这个方法是由垃圾收集器在肯定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,所以全部的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其余清理工做。finalize() 方法是在垃圾收集器删除对象以前对这个对象调用的。html
全网惟一一个从0开始帮助Java开发者转作大数据领域的公众号~java
大数据技术与架构或者搜索import_bigdata关注~web
海量【java和大数据的面试题+视频资料】整理在公众号,关注后能够下载~面试
2.int 和 Integer 有什么区别算法
1,不管如何,Integer与new Integer不会相等。不会经历拆箱过程,new出来的对象存放在堆,而非new的Integer常量则在常量池(在方法区),他们的内存地址不同,因此为false。 2,两个都是非new出来的Integer,若是数在-128到127之间,则是true,不然为false。由于java在编译Integer i2 = 128的时候,被翻译成:Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存。 3,两个都是new出来的,都为false。仍是内存地址不同。 4,int和Integer(不管new否)比,都为true,由于会把Integer自动拆箱为int再去比spring
class TestInteger {
public static void main(String[] args) {
int i = 128;
Integer i2 = 128;
Integer i3 = new Integer(128);
System.out.println(i == i2); //Integer会自动拆箱为int,因此为true
System.out.println(i == i3); //true,理由同上
Integer i4 = 127;//编译时被翻译成:Integer i4 = Integer.valueOf(127);
Integer i5 = 127;
System.out.println(i4 == i5);//true
Integer i6 = 128;
Integer i7 = 128;
System.out.println(i6 == i7);//false
Integer i8 = new Integer(127);
System.out.println(i5 == i8); //false
Integer i9 = new Integer(128);
Integer i10 = new Integer(123);
System.out.println(i9 == i10); //false
}
} 。
复制代码
3.重载和重写的区别数据库
一、覆盖的方法的标志必需要和被覆盖的方法的标志彻底匹配,才能达到覆盖的效果;编程
二、覆盖的方法的返回值必须和被覆盖的方法的返回一致;后端
三、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;设计模式
四、被覆盖的方法不能为private,不然在其子类中只是新定义了一个方法,并无对其进行覆盖。
2.Overload 特色
一、在使用重载时只能经过不一样的参数样式。例如,不一样的参数类型,不一样的参数个数,不一样的参数顺序(固然,同一方法内的几个参数类型必须不同,例如能够是fun(int, float), 可是不能为fun(int, int));
二、不能经过访问权限、返回类型、抛出的异常进行重载;
三、方法的异常类型和数目不会对重载形成影响;
四、对于继承来讲,若是某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,若是定义的话,也只是定义了一个新方法,而不会达到重载的效果。 4.抽象类和接口有什么区别
第一点. 接口是抽象类的变体,接口中全部的方法都是抽象的。而抽象类是声明方法的存在而不去实现它的类。 第二点. 接口能够多继承,抽象类不行 第三点. 接口定义方法,不能实现,而抽象类能够实现部分方法。 第四点. 接口中基本数据类型为static 而抽类象不是的。 当你关注一个事物的本质的时候,用抽象类;当你关注一个操做的时候,用接口。 抽象类的功能要远超过接口,可是,定义抽象类的代价高。由于高级语言来讲(从实际设计上来讲也是)每一个类只能继承一个类。在这个类中,你必须继承或编写出其全部子类的
说说反射的用途及实现 Java反射机制主要用于实现如下功能。 (1)在运行时判断任意一个对象所属的类型。 (2)在运行时构造任意一个类的对象。 (3)在运行时判断任意一个类所具备的成员变量和方法。 (4)在运行时调用任意一个对象的方法,甚至能够调用private方法。 注意:上述功能都是在运行时环境中,而不是在编译时环境中。
说说自定义注解的场景及实现 restful下方法上定义@LoggedIn判断是否须要登陆
HTTP 请求的 GET 与 POST 方式的区别
GET和POST是由HTTP协议定义的。在HTTP协议中,Method和Data(URL, Body, Header)是正交的两个概念,也就是说,使用哪一个Method与应用层的数据如何传输是没有相互关系的。
HTTP没有要求,若是Method是POST数据就要放在BODY中。也没有要求,若是Method是GET,数据(参数)就必定要放在URL中而不能放在BODY中。
那么,网上流传甚广的这个说法是从何而来的呢?我在HTML标准中,找到了类似的描述。这和网上流传的说法一致。可是这只是HTML标准对HTTP协议的用法的约定。怎么能当成GET和POST的区别呢?
并且,现代的Web Server都是支持GET中包含BODY这样的请求。虽然这种请求不可能从浏览器发出,可是如今的Web Server又不是只给浏览器用,已经彻底地超出了HTML服务器的范畴了。
HTTP协议明确地指出了,HTTP头和Body都没有长度的要求。而对于URL长度上的限制,有两方面的缘由形成:
session 与 cookie 区别
是一个在客户端一个在服务端。由于Cookie存在客户端因此用户能够看见,因此也能够编辑伪造,不是十分安全。
Session过多的时候会消耗服务器资源,因此大型网站会有专门的Session服务器,而Cookie存在客户端因此没什么问题。
域的支持范围不同,比方说a.com的Cookie在a.com下都能用,而www.a.com的Session在api.a.com下都不能用,解决这个问题的办法是JSONP或者跨域资源共享。
1.基于数据库的Session共享 2.基于NFS共享文件系统 3.基于memcached 的session,如何保证 memcached 自己的高可用性? 4. 基于resin/tomcat web容器自己的session复制机制 5. 基于TT/Redis 或 jbosscache 进行 session 共享。 6. 基于cookie 进行session共享
1)加载驱动程序。 2)创建链接。 3)建立语句。 4)执行语句。 5)处理ResultSet
MVC 设计思想
equals 与 == 的区别 == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,便是否是指相同一个对象。比较的是真正意义上的指针操做。 equals用来比较的是两个对象的内容是否相等,因为全部的类都是继承自java.lang.Object类的,因此适用于全部对象,若是没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的倒是==的判断。
List 和 Set 区别 List:1.能够容许重复的对象。 2.能够插入多个null元素。 3.是一个有序容器,保持了每一个元素的插入顺序,输出的顺序就是插入的顺序。 4.经常使用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于常常须要从 List 中添加或删除元素的场合更为合适。 Set:1.不容许重复对象 2. 无序容器,你没法保证每一个元素的存储顺序,TreeSet经过 Comparator 或者 Comparable 维护了一个排序顺序。 3. 只容许一个 null 元素 4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,所以 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。 1.Map不是collection的子接口或者实现类。Map是一个接口。 2.Map 的 每一个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是惟一的。 3. TreeMap 也经过 Comparator 或者 Comparable 维护了一个排序顺序。 4. Map 里你能够拥有随意个 null 值但最多只能有一个 null 键。 5.Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最经常使用)
Arraylist 与 LinkedList 区别
ArrayList 与 Vector 区别
HashMap 和 Hashtable 的区别 Hashtable和HashMap它们的性能方面的比较相似 Vector和ArrayList,好比Hashtable的方法是同步的,而HashMap的不是。
HashSet 和 HashMap 区别 (1)HashSet是set的一个实现类,hashMap是Map的一个实现类,同时hashMap是hashTable的替代品(为何后面会讲到). (2)HashSet以对象做为元素,而HashMap以(key-value)的一组对象做为元素,且HashSet拒绝接受重复的对象.HashMap能够看做三个视图:key的Set,value的Collection,Entry的Set。 这里HashSet就是其实就是HashMap的一个视图。 HashSet内部就是使用Hashmap实现的,和Hashmap不一样的是它不须要Key和Value两个值。 往hashset中插入对象其实只不过是内部作了 public boolean add(Object o) { return map.put(o, PRESENT)==null; }
HashMap 和 ConcurrentHashMap 的区别
HashMap 的工做原理及代码实现
ConcurrentHashMap 的工做原理及代码实现 jdk1.7中采用Segment + HashEntry的方式进行实现 1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现
1.建立线程的方式及实现
1)继承Thread类建立线程 2)实现Runnable接口建立线程 3)使用Callable和Future建立线程
2.sleep() 、join()、yield()有什么区别
sleep() sleep()方法须要指定等待的时间,它可让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可让其余同优先级或者高优先级的线程获得执行的机会,也可让低优先级的线程获得执行机会。可是sleep()方法不会释放“锁标志”,也就是说若是有synchronized同步块,其余线程仍然不能访问共享数据。 wait() wait()方法须要和notify()及notifyAll()两个方法一块儿介绍,这三个方法用于协调多个线程对共享数据的存取,因此必须在synchronized语句块内使用,也就是说,调用wait(),notify()和notifyAll()的任务在调用这些方法前必须拥有对象的锁。注意,它们都是Object类的方法,而不是Thread类的方法。 wait()方法与sleep()方法的不一样之处在于,wait()方法会释放对象的“锁标志”。当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程能够获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()方法,会将对象等待池中的全部线程都移动到该对象的锁标志等待池。 除了使用notify()和notifyAll()方法,还能够使用带毫秒参数的wait(long timeout)方法,效果是在延迟timeout毫秒后,被暂停的线程将被恢复到锁标志等待池。 此外,wait(),notify()及notifyAll()只能在synchronized语句中使用,可是若是使用的是ReenTrantLock实现同步,该如何达到这三个方法的效果呢?解决方法是使用ReenTrantLock.newCondition()获取一个Condition类对象,而后Condition的await(),signal()以及signalAll()分别对应上面的三个方法。
yield() yield()方法和sleep()方法相似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方法只是使当前线程从新回到可执行状态,因此执行yield()的线程有可能在进入到可执行状态后立刻又被执行,另外yield()方法只能使同优先级或者高优先级的线程获得执行机会,这也和sleep()方法不一样。
join() join()方法会使当前线程等待调用join()方法的线程结束后才能继续执行 3.说说 CountDownLatch 原理 www.jianshu.com/p/38c39e00e… 4.说说 CyclicBarrier 原理 www.jianshu.com/p/060761df1… 说说 Semaphore 原理
说说 Exchanger 原理 www.jianshu.com/p/1eab24ca3… 说说 CountDownLatch 与 CyclicBarrier 区别
ThreadLocal提供了set和get访问器用来访问与当前线程相关联的线程局部变量。 能够从ThreadLocal的get函数中看出来,其中getmap函数是用t做为参数,这里t就是当前执行的线程。
从而得知,get函数就是从当前线程的threadlocalmap中取出当前线程对应的变量的副本【注意,变量是保存在线程中的,而不是保存在ThreadLocal变量中】。当前线程中,有一个变量引用名字是threadLocals,这个引用是在ThreadLocal类中createmap函数内初始化的。每一个线程都有一个这样的threadLocals引用的ThreadLocalMap,以ThreadLocal和ThreadLocal对象声明的变量类型做为参数。这样,咱们所使用的ThreadLocal变量的实际数据,经过get函数取值的时候,就是经过取出Thread中threadLocals引用的map,而后从这个map中根据当前threadLocal做为参数,取出数据。如今,变量的副本从哪里取出来的(本文章提出的第一个问题)已经确认解决了。
ThreadLocal操做值的时候是取得当前线程的ThreadLocalMap对象,而后把值设置到了这个对象中,这样对于不一样的线程获得的就是不一样的ThreadLocalMap,那么向其中保存值或者修改值都只是会影响到当前线程,这样就保证了线程安全。
Java线程池的工厂类:Executors类, 初始化4种类型的线程池: newFixedThreadPool() 说明:初始化一个指定线程数的线程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene做为阻塞队列 特色:即便当线程池没有可执行任务时,也不会释放线程。 newCachedThreadPool() 说明:初始化一个能够缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue做为阻塞队列; 特色:在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源;当提交新任务时,若是没有空闲线程,则建立新线程执行任务,会致使必定的系统开销; 所以,使用时要注意控制并发的任务数,防止因建立大量的线程致使而下降性能。 newSingleThreadExecutor() 说明:初始化只有一个线程的线程池,内部使用LinkedBlockingQueue做为阻塞队列。 特色:若是该线程异常结束,会从新建立一个新的线程继续执行任务,惟一的线程能够保证所提交任务的顺序执行 newScheduledThreadPool() 特定:初始化的线程池能够在指定的时间内周期性的执行所提交的任务,在实际的业务场景中能够使用该线程池按期的同步数据。 总结:除了newScheduledThreadPool的内部实现特殊一点以外,其它线程池内部都是基于ThreadPoolExecutor类(Executor的子类)实现的。
ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,timeUnit,workQueue,threadFactory,handle);
方法参数: corePoolSize:核心线程数 maxPoolSize:最大线程数 keepAliveTime:线程存活时间(在corePore<*<maxPoolSize状况下有用) timeUnit:存活时间的时间单位 workQueue:阻塞队列(用来保存等待被执行的任务) 注:关于workQueue参数的取值,JDK提供了4种阻塞队列类型供选择: ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务; inkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量一般要高于
SynchronousQuene:一个不存储元素的阻塞队列,每一个插入操做必须等到另外一个线程调用移除操做,不然插入操做一直处于阻塞状态,吞吐量一般要高于ArrayBlockingQuene; PriorityBlockingQuene:具备优先级的无界阻塞队列; threadFactory:线程工厂,主要用来建立线程; handler:表示当拒绝处理任务时的策略,有如下四种取值 注:当线程池的饱和策略,当阻塞队列满了,且没有空闲的工做线程,若是继续提交任务,必须采起一种策略处理该任务,线程池提供了4种策略: ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,可是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,而后从新尝试执行任务(重复此过程) ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 固然也能够根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
线程的生命周期 当线程被建立并启动之后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要通过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤为是当线程启动之后,它不可能一直"霸占"着CPU独自运行,因此CPU须要在多条线程之间切换,因而线程状态也会屡次在运行、阻塞之间切换 锁机制 说说线程安全问题
volatile 实现原理 volatile 关键字,具备两个特性:1. 内存的可见性,2. 禁止指令重排序优化。 内存可见性是指:被 volatile 关键字修饰的变量,当线程要对这个变量执行的写操做,都不会写入本地缓存,而是直接刷入主内存中。当线程读取被 volatile 关键字修饰的变量时,也是直接从主内存中读取。(简单的说,一个线程修改的状态对另外一个线程是可见的)。注意:volatile 不能保证原子性。 禁止指令重排序优化:有volatile修饰的变量,赋值后多执行了一个 “load addl $0x0, (%esp)” 操做,这个操做至关于一个内存屏障,保证指令重排序时不会把后面的指令重排序到内存屏障以前的位置。
synchronize 实现原理 synchronized 代码块是经过 monitorenter 和 monitorexit 指令实现的。synchronized 方法虽然在 vm 字节码层面并无任何特别的指令来实现被 synchronized 修饰的方法,而是在 Class 文件的方法表中将该方法的 access_flags 字段中的 synchronized 标志位置1,表示该方法是同步方法。锁的实现有偏向锁、轻量级锁和重量级锁,其中偏向锁和轻量级锁是 JDK 针对锁的优化措施。在多线程的竞争下锁会升级,依次从偏向锁 -> 轻量级锁 -> 重量级锁,这里的锁只能升级但不能降级。在 Java 对象头中的 Mark Word 中存储了关于锁的标志位,其中:无锁和偏向锁为 00, 轻量级锁为 01,重量级锁为 10。 引入偏向锁主要目的是:为了在无多线程竞争的状况下尽可能减小没必要要的轻量级锁执行路径(在无竞争的状况下把整个同步都消除掉,连 CAS 操做都不作了)。 引入轻量级锁的主要目的是:在没有多线程竞争的前提下,减小传统的重量级锁使用操做系统互斥量产生的性能消耗(在无竞争的状况下使用 CAS 操做去消除同步使用的互斥量)。 重量级锁经过对象内部的监视器(monitor)实现,其中 monitor 的本质是依赖于底层操做系统的 Mutex Lock 实现,操做系统实现线程之间的切换须要从用户态到内核态的切换,切换成本很是高。
synchronized 与 lock 的区别 二者区别: 1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类; 2.synchronized没法判断是否获取锁的状态,Lock能够判断是否获取到锁; 3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程当中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),不然容易形成线程死锁; 4.用synchronized关键字的两个线程1和线程2,若是当前线程1得到锁,线程2线程等待。若是线程1阻塞,线程2则会一直等待下去,而Lock锁就不必定会等待下去,若是尝试获取不到锁,线程能够不用一直等待就结束了; 5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(二者皆可) 6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少许的同步问题。
CAS 乐观锁 乐观锁 乐观锁( Optimistic Locking)实际上是一种思想。相对悲观锁而言,乐观锁假设认为数据通常状况下不会形成冲突,因此在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,若是发现冲突了,则让返回用户错误的信息,让用户决定如何去作。 上面提到的乐观锁的概念中其实已经阐述了他的具体实现细节:主要就是两个步骤:冲突检测和数据更新。其实现方式有一种比较典型的就是Compare and Swap(CAS)。 CAS CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知此次竞争中失败,并能够再次尝试。 CAS 操做包含三个操做数 —— 内存位置(V)、预期原值(A)和新值(B)。若是内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。不然,处理器不作任何操做。不管哪一种状况,它都会在 CAS 指令以前返回该位置的值。(在 CAS 的一些特殊状况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;若是包含该值,则将 B 放到这个位置;不然,不要更改该位置,只告诉我这个位置如今的值便可。”这其实和乐观锁的冲突检查+数据更新的原理是同样的。
ABA 问题
乐观锁用到的机制就是CAS,Compare and Swap。 CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。 乐观锁的业务场景及实现方式 CAS看起来很爽,可是会致使“ABA问题”。 CAS算法实现一个重要前提须要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会致使数据的变化。 好比说一个线程one从内存位置V中取出A,这时候另外一个线程two也从内存中取出A,而且two进行了一些操做变成了B,而后two又将V位置的数据变成A,这时候线程one进行CAS操做发现内存中仍然是A,而后one操做成功。尽管线程one的CAS操做成功,可是不表明这个过程就是没有问题的。若是链表的头在变化了两次后恢复了原值,可是不表明链表就没有变化。所以前面提到的原子操做AtomicStampedReference/AtomicMarkableReference就颇有用了。这容许一对变化的元素进行原子操做。
MySQL 索引使用的注意事项
说说反模式设计
说说分库与分表设计
分库与分表带来的分布式困境与应对之策
说说 SQL 优化之道
一、应尽可能避免在 where 子句中使用!=或<>操做符,不然将引擎放弃使用索引而进行全表扫描。
二、对查询进行优化,应尽可能避免全表扫描,首先应考虑在 where 及 order by 涉及的列上创建索引。
三、应尽可能避免在 where 子句中对字段进行 null 值判断,不然将致使引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
能够在num上设置默认值0,确保表中num列没有null值,而后这样查询:
select id from t where num=0
四、尽可能避免在 where 子句中使用 or 来链接条件,不然将致使引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
能够这样查询:
select id from t where num=10
union all
select id from t where num=20
五、下面的查询也将致使全表扫描:(不能前置百分号)
select id from t where name like ‘�c%’
若要提升效率,能够考虑全文检索。
六、in 和 not in 也要慎用,不然会致使全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
七、若是在 where 子句中使用参数,也会致使全表扫。由于SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,若是在编译时创建访问计划,变量的值仍是未知的,于是没法做为索引选择的输入项。以下面语句将进行全表扫描:
select id from t where num=@num
能够改成强制查询使用索引:
select id from t with(index(索引名)) where num=@num
八、应尽可能避免在 where 子句中对字段进行表达式操做,这将致使引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改成:
select id from t where num=100*2
九、应尽可能避免在where子句中对字段进行函数操做,这将致使引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=’abc’–name以abc开头的id
select id from t where datediff(day,createdate,’2005-11-30′)=0–’2005-11-30′生成的id
应改成:
select id from t where name like ‘abc%’
select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′
十、不要在 where 子句中的“=”左边进行函数、算术运算或其余表达式运算,不然系统将可能没法正确使用索引。
十一、在使用索引字段做为条件时,若是该索引是复合索引,那么必须使用到该索引中的第一个字段做为条件时才能保证系统使用该索引,不然该索引将不会被使 用,而且应尽量的让字段顺序与索引顺序相一致。
十二、不要写一些没有意义的查询,如须要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,可是会消耗系统资源的,应改为这样:
create table #t(…)
1三、不少时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
1四、并非全部索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex,male、female几乎各一半,那么即便在sex上建了索引也对查询效率起不了做用。
1五、索引并非越多越好,索引当然能够提升相应的 select 的效率,但同时也下降了 insert 及 update 的效率,由于 insert 或 update 时有可能会重建索引,因此怎样建索引须要慎重考虑,视具体状况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。
16.应尽量的避免更新 clustered 索引数据列,由于 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将致使整个表记录的顺序的调整,会耗费至关大的资源。若应用系统须要频繁更新 clustered 索引数据列,那么须要考虑是否应将该索引建为 clustered 索引。
1七、尽可能使用数字型字段,若只含数值信息的字段尽可能不要设计为字符型,这会下降查询和链接的性能,并会增长存储开销。这是由于引擎在处理查询和链接时会 逐个比较字符串中每个字符,而对于数字型而言只须要比较一次就够了。
1八、尽量的使用 varchar/nvarchar 代替 char/nchar ,由于首先变长字段存储空间小,能够节省存储空间,其次对于查询来讲,在一个相对较小的字段内搜索效率显然要高些。
1九、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
20、尽可能使用表变量来代替临时表。若是表变量包含大量数据,请注意索引很是有限(只有主键索引)。
2一、避免频繁建立和删除临时表,以减小系统表资源的消耗。
2二、临时表并非不可以使用,适当地使用它们能够使某些例程更有效,例如,当须要重复引用大型表或经常使用表中的某个数据集时。可是,对于一次性事件,最好使 用导出表。
2三、在新建临时表时,若是一次性插入数据量很大,那么能够使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是数据量不大,为了缓和系统表的资源,应先create table,而后insert。
2四、若是使用到了临时表,在存储过程的最后务必将全部的临时表显式删除,先 truncate table ,而后 drop table ,这样能够避免系统表的较长时间锁定。
2五、尽可能避免使用游标,由于游标的效率较差,若是游标操做的数据超过1万行,那么就应该考虑改写。
2六、使用基于游标的方法或临时表方法以前,应先寻找基于集的解决方案来解决问题,基于集的方法一般更有效。
2七、与临时表同样,游标并非不可以使用。对小型数据集使用 FAST_FORWARD 游标一般要优于其余逐行处理方法,尤为是在必须引用几个表才能得到所需的数据时。在结果集中包括“合计”的例程一般要比使用游标执行的速度快。若是开发时 间容许,基于游标的方法和基于集的方法均可以尝试一下,看哪种方法的效果更好。
2八、在全部的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每一个语句后向客户端发送 DONE_IN_PROC 消息。
2九、尽可能避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
30、尽可能避免大事务操做,提升系统并发能力。
复制代码
MySQL 遇到的死锁问题
存储引擎的 InnoDB 与 MyISAM
数据库索引的原理
为何要用 B-tree
汇集索引与非汇集索引的区别
limit 20000 加载很慢怎么解决
1.子查询优化法
先找出第一条数据,而后大于等于这条数据的id就是要获取的数据
2.倒排表优化法
倒排表法相似创建索引,用一张表来维护页数,而后经过高效的链接获得数据
缺点:只适合数据数固定的状况,数据不能删除,维护页表困难
3.反向查找优化法
当偏移超过一半记录数的时候,先用排序,这样偏移就反转了
缺点:order by优化比较麻烦,要增长索引,索引影响数据的修改效率,而且要知道总记录数,偏移大于数据的一半
4.limit限制优化法
把limit偏移量限制低于某个数。。超过这个数等于没数据,我记得alibaba的dba说过他们是这样作的
5.只查索引法
复制代码
选择合适的分布式主键方案
最多见的方式。利用数据库,全数据库惟一。
优势:
1)简单,代码方便,性能能够接受。
2)数字ID自然排序,对分页或者须要排序的结果颇有帮助。
缺点: 1)不一样数据库语法和实现不一样,数据库迁移的时候或多数据库版本支持的时候须要处理。 2)在单个数据库或读写分离或一主多从的状况下,只有一个主库能够生成。有单点故障的风险。 3)在性能达不到要求的状况下,比较难于扩展。 4)若是碰见多个系统须要合并或者涉及到数据迁移会至关痛苦。 5)分表分库的时候会有麻烦。 优化方案: 1)针对主库单点,若是有多个Master库,则每一个Master库设置的起始数字不同,步长同样,能够是Master的个数。好比:Master1 生成的是 1,4,7,10,Master2生成的是2,5,8,11 Master3生成的是 3,6,9,12。这样就能够有效生成集群中的惟一ID,也能够大大下降ID生成数据库操做的负载。 2. UUID 常见的方式。能够利用数据库也能够利用程序生成,通常来讲全球惟一。 优势 1)简单,代码方便。 2)生成ID性能很是好,基本不会有性能问题。 3)全球惟一,在碰见数据迁移,系统数据合并,或者数据库变动等状况下,能够从容应对。 缺点: 1)没有排序,没法保证趋势递增。 2)UUID每每是使用字符串存储,查询的效率比较低。 3)存储空间比较大,若是是海量数据库,就须要考虑存储量的问题。 4)传输数据量大 5)不可读。 4. Redis生成ID 当使用数据库来生成ID性能不够要求的时候,咱们能够尝试使用Redis来生成ID。这主要依赖于Redis是单线程的,因此也能够用生成全局惟一的ID。能够用Redis的原子操做 INCR和INCRBY来实现。 5. Twitter的snowflake算法
ObjectId 规则
聊聊 MongoDB 使用场景
倒排索引
聊聊 ElasticSearch 使用场景
缓存使用
Redis 有哪些类型
Redis 内部结构
聊聊 Redis 使用场景
Redis 持久化机制
Redis 如何实现持久化
Redis 集群方案与实现
Redis 为何是单线程的
缓存奔溃
缓存降级
使用缓存的合理性问题
消息队列
消息队列的使用场景
消息的重发补偿解决思路
消息的幂等性解决思路
消息的堆积解决思路
本身如何实现消息队列
如何保证消息的有序性
复制代码
BeanFactory 和 ApplicationContext 有什么区别
BeanFacotry是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory没法支持spring的许多插件,如AOP功能、Web应用等。
ApplicationContext接口,它由BeanFactory接口派生而来,于是提供BeanFactory全部的功能。ApplicationContext以一种更向面向框架的方式工做以及对上下文进行分层和实现继承,ApplicationContext包还提供了如下的功能:
• MessageSource, 提供国际化的消息访问
• 资源访问,如URL和文件
• 事件传播
• 载入多个(有继承关系)上下文 ,使得每个上下文都专一于一个特定的层次,好比应用的web层
Spring Bean 的生命周期
Spring上下文中的Bean也相似,【Spring上下文的生命周期】
1. 实例化一个Bean,也就是咱们一般说的new
2. 按照Spring上下文对实例化的Bean进行配置,也就是IOC注入
3. 若是这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是Spring配置文件中Bean的ID
4. 若是这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂自己(能够用这个方法获取到其余Bean)
5. 若是这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,该方式一样能够实现步骤4,但比4更好,觉得ApplicationContext是BeanFactory的子接口,有更多的实现方法
6. 若是这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor常常被用做是Bean内容的更改,而且因为这个是在Bean初始化结束时调用After方法,也可用于内存或缓存技术
7. 若是这个Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法
8. 若是这个Bean关联了BeanPostProcessor接口,将会调用postAfterInitialization(Object obj, String s)方法
注意:以上工做完成之后就能够用这个Bean了,那这个Bean是一个single的,因此通常状况下咱们调用同一个ID的Bean会是在内容地址相同的实例
9. 当Bean再也不须要时,会通过清理阶段,若是Bean实现了DisposableBean接口,会调用其实现的destroy方法
10. 最后,若是这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
以上10步骤能够做为面试或者笔试的模板,另外咱们这里描述的是应用Spring上下文Bean的生命周期,若是应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了
Spring IOC 如何实现
IOC容器就是具备依赖注入功能的容器,IOC容器负责实例化、定位、配置应用程序中的对象及创建这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring中BeanFactory是IOC容器的实际表明者。
Spring IOC容器如何知道哪些是它管理的对象呢?这就须要配置文件,Spring IOC容器经过读取配置文件中的配置元数据,经过元数据对应用中的各个对象进行实例化及装配。通常使用基于xml配置文件进行配置元数据,并且Spring与配置文件彻底解耦的,能够使用其余任何可能的方式进行配置元数据,好比注解、基于java文件的、基于属性文件的配置均可以。
说说 Spring AOP
Spring AOP 实现原理
1.IOC
许多应用都是经过彼此间的相互合做来实现业务逻辑的,如类A要调用类B的方法,之前咱们都是在类A中,经过自身new一个类B,而后在调用类B的方法,如今咱们把new类B的事情交给spring来作,在咱们调用的时候,容器会为咱们实例化。
2. IOC容器的初始化过程
资源定位,即定义bean的xml-------》载入--------》IOC容器注册,注册beanDefinition
IOC容器的初始化过程,通常不包含bean的依赖注入的实现,在spring IOC设计中,bean的注册和依赖注入是两个过程,,依赖注入通常发生在应用第一次索取bean的时候,可是也能够在xm中配置,在容器初始化的时候,这个bean就完成了初始化。
3. 三种注入方式,构造器、接口、set注入,咱们经常使用的是set注入
4. bean是如何建立--- 工厂模式
5. 数据是如何注入-------反射
6.AOP
面向切面编程,在咱们的应用中,常常须要作一些事情,可是这些事情与核心业务无关,好比,要记录全部update*方法的执行时间时间,操做人等等信息,记录到日志,
经过spring的AOP技术,就能够在不修改update*的代码的状况下完成该需求。
7.AOP的实现原理------代理
动态代理(cglib 与 JDK)
静态代理是经过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户经过代理类调用被包装过的业务方法;
JDK动态代理是经过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGlib动态代理是经过继承业务类,生成的动态代理类是业务类的子类,经过重写业务方法进行代理;
Spring 事务实现方式
实现spring事务的四种方式分别为:
(1)编程式事务管理:须要手动编写代码,在实际开发中不多使用
(2)声明式事务管理:
(2.1)基于TransactionProxyFactoryBean的方式,须要为每一个进行事务管理的类作相应配置
(2.2)基于AspectJ的XML方式,不须要改动类,在XML文件中配置好便可
(2.3)基于注解的方式,配置简单,须要在业务层类中添加注解
Spring 事务底层原理
工做原理及实现
a、划分处理单元——IOC
因为spring解决的问题是对单个数据库进行局部事务处理的,具体的实现首相用spring中的IOC划分了事务处理单元。而且将对事务的各类配置放到了ioc容器中(设置事务管理器,设置事务的传播特性及隔离机制)。
b、AOP拦截须要进行事务处理的类
Spring事务处理模块是经过AOP功能来实现声明式事务处理的,具体操做(好比事务实行的配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,生成proxy代理对象,经过TransactionInterceptor完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。
读取ioc容器事务配置属性,转化为spring事务处理须要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。
c、对事物处理实现(事务的生成、提交、回滚、挂起)
spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是经过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的支持。
为经常使用数据源支持提供了一系列的TransactionManager。
d、结合
PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean结合起来,造成一个Spring声明式事务处理的设计体系。
如何自定义注解实现功能
Spring MVC 运行流程
Spring MVC 启动流程
Spring 的单例实现原理
你们真正要记住的是Spring对bean实例的建立是采用单例注册表的方式进行实现的,而这个注册表的缓存是HashMap对象,若是配置文件中的配置信息不要求使用单例,Spring会采用新建实例的方式返回对象实例
Spring 框架中用到了哪些设计模式
Spring框架中使用到了大量的设计模式,下面列举了比较有表明性的:
代理模式—在AOP和remoting中被用的比较多。
单例模式—在spring配置文件中定义的bean默认为单例模式。
模板方法—用来解决代码重复的问题。好比. RestTemplate, JmsTemplate, JpaTemplate。
工厂模式—BeanFactory用来建立对象的实例。
适配器--spring aop
装饰器--spring data hashmapper
观察者-- spring 时间驱动模型
回调--Spring ResourceLoaderAware回调接口
Spring 其余产品(Srping Boot、Spring Cloud、Spring Secuirity、Spring Data、Spring AMQP 等)
复制代码
为何选择 Netty
说说业务中,Netty 的使用场景
原生的 NIO 在 JDK 1.7 版本存在 epoll bug
https://www.cnblogs.com/JAYIT/p/8241634.html
Selector BUG出现的缘由
若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%,
Netty的解决办法
对Selector的select操做周期进行统计,每完成一次空的select操做进行一次计数,
若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug。
重建Selector,判断是不是其余线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,从新注册到新的Selector上,并将原来的Selector关闭。
什么是TCP 粘包/拆包、TCP粘包/拆包的解决办法
发生TCP粘包或拆包有不少缘由,现列出常见的几点,可能不全面,欢迎补充,
一、要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。
二、待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。
三、要发送的数据小于TCP发送缓冲区的大小,TCP将屡次写入缓冲区的数据一次发送出去,将会发生粘包。
四、接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。
粘包、拆包解决办法
经过以上分析,咱们清楚了粘包或拆包发生的缘由,那么如何解决这个问题呢?解决问题的关键在于如何给每一个数据包添加边界信息,经常使用的方法有以下几个:
一、发送端给每一个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,经过读取包首部的长度字段,便知道每个数据包的实际长度了。
二、发送端将每一个数据包封装为固定长度(不够的能够经过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就天然而然的把每一个数据包拆分开来。
三、能够在数据包之间设置边界,如添加特殊符号,这样,接收端经过这个边界就能够将不一样的数据包拆分开。
Netty 线程模型
Netty经过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件,由对应的Handler处理。
单线程模型:全部I/O操做都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor线程上完成的。既要接收客户端的链接请求,向服务端发起链接,又要发送/读取请求或应答/响应消息。一个NIO 线程同时处理成百上千的链路,性能上没法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载、大并发的应用场景不合适。
多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 链接请求;NIO 线程池负责网络IO 的操做,即消息的读取、解码、编码和发送;1 个NIO 线程能够同时处理N 条链路,可是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操做问题。但在并发百万客户端链接或须要安全认证时,一个Acceptor 线程可能会存在性能不足问题。
主从多线程模型:Acceptor 线程用于绑定监听端口,接收客户端链接,将SocketChannel 从主线程池的Reactor 线程的多路复用器上移除,从新注册到Sub 线程池的线程上,用于处理I/O 的读写等操做,从而保证mainReactor只负责接入认证、握手等操做;
说说 Netty 的零拷贝
Netty的零拷贝实现?
Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不须要进行字节缓冲区的二次拷贝。堆内存多了一次内存拷贝,JVM会将堆内存Buffer拷贝一份到直接内存中,而后才写入Socket中。ByteBuffer由ChannelConfig分配,而ChannelConfig建立ByteBufAllocator默认使用Direct Buffer
CompositeByteBuf 类能够将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了传统经过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。addComponents方法将 header 与 body 合并为一个逻辑上的 ByteBuf, 这两个 ByteBuf 在CompositeByteBuf 内部都是单独存在的, CompositeByteBuf 只是逻辑上是一个总体
经过 FileRegion 包装的FileChannel.tranferTo方法 实现文件传输, 能够直接将文件缓冲区的数据发送到目标 Channel,避免了传统经过循环write方式致使的内存拷贝问题。
经过 wrap方法, 咱们能够将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操做。
Selector BUG:若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%,
Netty的解决办法:对Selector的select操做周期进行统计,每完成一次空的select操做进行一次计数,若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug。重建Selector,判断是不是其余线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,从新注册到新的Selector上,并将原来的Selector关闭。
Netty 内部执行流程
服务端依次发生的步骤
创建服务端监听套接字ServerSocketChannel,以及对应的管道pipeline;
启动boss线程,将ServerSocketChannel注册到boss线程持有的selector中,并将注册返回的selectionKey赋值给ServerSocketChannel关联的selectionKey变量;
在ServerSocketChannel对应的管道中触发channelRegistered事件;
绑定IP和端口
触发channelActive事件,并将ServerSocketChannel关联的selectionKey的OP_ACCEPT位置为1。
客户端发起connect请求后,boss线程正在运行的select循环检测到了该ServerSocketChannel的ACCEPT事件就绪,则经过accept系统调用创建一个已链接套接字SocketChannel,并为其建立对应的管道;
在服务端监听套接字对应的管道中触发channelRead事件;
channelRead事件由ServerBootstrapAcceptor的channelRead方法响应:为已链接套接字对应的管道加入ChannelInitializer处理器;启动一个worker线程,并将已链接套接字的注册任务加入到worker线程的任务队列中;
worker线程执行已链接套接字的注册任务:将已链接套接字注册到worker线程持有的selector中,并将注册返回的selectionKey赋值给已链接套接字关联的selectionKey变量;在已链接套接字对应的管道中触发channelRegistered事件;channelRegistered事件由ChannelInitializer的channelRegistered方法响应:将自定义的处理器(譬如EchoServerHandler)加入到已链接套接字对应的管道中;在已链接套接字对应的管道中触发channelActive事件;channelActive事件由已链接套接字对应的管道中的inbound处理器的channelActive方法响应;将已链接套接字关联的selectionKey的OP_READ位置为1;至此,worker线程关联的selector就开始监听已链接套接字的READ事件了。
在worker线程运行的同时,Boss线程接着在服务端监听套接字对应的管道中触发channelReadComplete事件。
客户端向服务端发送消息后,worker线程正在运行的selector循环会检测到已链接套接字的READ事件就绪。则经过read系统调用将消息从套接字的接受缓冲区中读到AdaptiveRecvByteBufAllocator(能够自适应调整分配的缓存的大小)分配的缓存中;
在已链接套接字对应的管道中触发channelRead事件;
channelRead事件由EchoServerHandler处理器的channelRead方法响应:执行write操做将消息存储到ChannelOutboundBuffer中;
在已链接套接字对应的管道中触发ChannelReadComplete事件;
ChannelReadComplete事件由EchoServerHandler处理器的channelReadComplete方法响应:执行flush操做将消息从ChannelOutboundBuffer中flush到套接字的发送缓冲区中;
客户端依次发生的步骤
创建套接字SocketChannel,以及对应的管道pipeline;
启动客户端线程,将SocketChannel注册到客户端线程持有的selector中,并将注册返回的selectionKey赋值给SocketChannel关联的selectionKey变量;
触发channelRegistered事件;
channelRegistered事件由ChannelInitializer的channelRegistered方法响应:将客户端自定义的处理器(譬如EchoClientHandler)按顺序加入到管道中;
向服务端发起connect请求,并将SocketChannel关联的selectionKey的OP_CONNECT位置为1;
开始三次握手,客户端线程正在运行的select循环检测到了该SocketChannel的CONNECT事件就绪,则将关联的selectionKey的OP_CONNECT位置为0,再经过调用finishConnect完成链接的创建;
触发channelActive事件;
channelActive事件由EchoClientHandler的channelActive方法响应,经过调用ctx.writeAndFlush方法将消息发往服务端;
首先将消息存储到ChannelOutboundBuffer中;(若是ChannelOutboundBuffer存储的全部未flush的消息的大小超太高水位线writeBufferHighWaterMark(默认值为64 * 1024),则会触发ChannelWritabilityChanged事件)
而后将消息从ChannelOutboundBuffer中flush到套接字的发送缓冲区中;(若是ChannelOutboundBuffer存储的全部未flush的消息的大小小于低水位线,则会触发ChannelWritabilityChanged事件)
Netty 重连实现
使用 Netty 实现心跳机制的关键就是利用 IdleStateHandler 来产生对应的 idle 事件.
通常是客户端负责发送心跳的 PING 消息, 所以客户端注意关注 ALL_IDLE 事件, 在这个事件触发后, 客户端须要向服务器发送 PING 消息, 告诉服务器"我还存活着".
服务器是接收客户端的 PING 消息的, 所以服务器关注的是 READER_IDLE 事件, 而且服务器的 READER_IDLE 间隔须要比客户端的 ALL_IDLE 事件间隔大(例如客户端ALL_IDLE 是5s 没有读写时触发, 所以服务器的 READER_IDLE 能够设置为10s)
当服务器收到客户端的 PING 消息时, 会发送一个 PONG 消息做为回复. 一个 PING-PONG 消息对就是一个心跳交互.
在client在链接服务器的方法上加上一个监听,当发生断线的时候进行从新链接便可。unb
复制代码
微服务
先后端分离是如何作的
微服务哪些框架
你怎么理解 RPC 框架
说说 RPC 的实现原理
1)服务消费方(client)调用以本地调用方式调用服务;
2)client stub接收到调用后负责将方法、参数等组装成可以进行网络传输的消息体;
3)client stub找到服务地址,并将消息发送到服务端;
4)server stub收到消息后进行解码;
5)server stub根据解码结果调用本地的服务;
6)本地服务执行并将结果返回给server stub;
7)server stub将返回结果打包成消息并发送至消费方;
8)client stub接收到消息,并进行解码;
9)服务消费方获得最终结果。
使用到的技术
一、动态代理
生成 client stub和server stub须要用到 Java 动态代理技术 ,咱们能够使用JDK原生的动态代理机制,能够使用一些开源字节码工具框架 如:CgLib、Javassist等。
二、序列化
为了能在网络上传输和接收 Java对象,咱们须要对它进行 序列化和反序列化操做。
* 序列化:将Java对象转换成byte[]的过程,也就是编码的过程;
* 反序列化:将byte[]转换成Java对象的过程;
能够使用Java原生的序列化机制,可是效率很是低,推荐使用一些开源的、成熟的序列化技术,例如:protobuf、Thrift、hessian、Kryo、Msgpack
关于序列化工具性能比较能够参考:jvm-serializers
三、NIO
当前不少RPC框架都直接基于netty这一IO通讯框架,好比阿里巴巴的HSF、dubbo,Hadoop Avro,推荐使用Netty 做为底层通讯框架。
四、服务注册中心
可选技术:
* Redis
* Zookeeper
* Consul
* Etcd
说说 Dubbo 的实现原理
dubbo做为rpc框架,实现的效果就是调用远程的方法就像在本地调用同样。如何作到呢?就是本地有对远程方法的描述,包括方法名、参数、返回值,在dubbo中是远程和本地使用一样的接口;而后呢,要有对网络通讯的封装,要对调用方来讲通讯细节是彻底不可见的,网络通讯要作的就是将调用方法的属性经过必定的协议(简单来讲就是消息格式)传递到服务端;服务端按照协议解析出调用的信息;执行相应的方法;在将方法的返回值经过协议传递给客户端;客户端再解析;在调用方式上又能够分为同步调用和异步调用;简单来讲基本就这个过程
你怎么理解 RESTful
说说如何设计一个良好的 API
如何理解 RESTful API 的幂等性
如何保证接口的幂等性
全局惟一ID
若是使用全局惟一ID,就是根据业务的操做和内容生成一个全局ID,在执行操做前先根据这个全局惟一ID是否存在,来判断这个操做是否已经执行。若是不存在则把全局ID,存储到存储系统中,好比数据库、Redis等。若是存在则表示该方法已经执行。
从工程的角度来讲,使用全局ID作幂等能够做为一个业务的基础的微服务存在,在不少的微服务中都会用到这样的服务,在每一个微服务中都完成这样的功能,会存在工做量重复。另外打造一个高可靠的幂等服务还须要考虑不少问题,好比一台机器虽然把全局ID先写入了存储,可是在写入以后挂了,这就须要引入全局ID的超时机制。
使用全局惟一ID是一个通用方案,能够支持插入、更新、删除业务操做。可是这个方案看起来很美可是实现起来比较麻烦,下面的方案适用于特定的场景,可是实现起来比较简单。
去重表
这种方法适用于在业务中有惟一标的插入场景中,好比在以上的支付场景中,若是一个订单只会支付一次,因此订单ID能够做为惟一标识。这时,咱们就能够建一张去重表,而且把惟一标识做为惟一索引,在咱们实现时,把建立支付单据和写入去去重表,放在一个事务中,若是重复建立,数据库会抛出惟一约束异常,操做就会回滚。
插入或更新
这种方法插入而且有惟一索引的状况,好比咱们要关联商品品类,其中商品的ID和品类的ID能够构成惟一索引,而且在数据表中也增长了惟一索引。这时就能够使用InsertOrUpdate操做。在MySQL数据库中以下:
insert into goods_category (goods_id,category_id,create_time,update_time)
values(#{goodsId},#{categoryId},now(),now())
on DUPLICATE KEY UPDATE
update_time=now()
多版本控制 这种方法适合在更新的场景中,好比咱们要更新商品的名字,这时咱们就能够在更新的接口中增长一个版本号,来作幂等
说说 CAP 定理、 BASE 理论
CAP理论
一个经典的分布式系统理论。CAP理论告诉咱们:一个分布式系统不可能同时知足一致性(C:Consistency)、可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时知足其中两项。
一、一致性
在分布式环境下,一致性是指数据在多个副本之间可否保持一致的特性。在一致性的需求下,当一个系统在数据一致的状态下执行更新操做后,应该保证系统的数据仍然处于一直的状态。
对于一个将数据副本分布在不一样分布式节点上的系统来讲,若是对第一个节点的数据进 行了更新操做而且更新成功后,却没有使得第二个节点上的数据获得相应的更新,因而在对第二个节点的数据进行读取操做时,获取的依然是老数据(或称为脏数 据),这就是典型的分布式数据不一致的状况。在分布式系统中,若是可以作到针对一个数据项的更新操做执行成功后,全部的用户均可以读取到其最新的值,那么 这样的系统就被认为具备强一致性
二、可用性
可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每个操做请求老是可以在有限的时间内返回结果。这里的重点是"有限时间内"和"返回结果"。
"有限时间内"是指,对于用户的一个操做请求,系统必须可以在指定的时间内返回对 应的处理结果,若是超过了这个时间范围,那么系统就被认为是不可用的。另外,"有限的时间内"是指系统设计之初就设计好的运行指标,一般不一样系统之间有很 大的不一样,不管如何,对于用户请求,系统必须存在一个合理的响应时间,不然用户便会对系统感到失望。
"返回结果"是可用性的另外一个很是重要的指标,它要求系统在完成对用户请求的处理后,返回一个正常的响应结果。正常的响应结果一般可以明确地反映出队请求的处理结果,即成功或失败,而不是一个让用户感到困惑的返回结果。
三、分区容错性
分区容错性约束了一个分布式系统具备以下特性:分布式系统在遇到任何网络分区故障的时候,仍然须要可以保证对外提供知足一致性和可用性的服务,除非是整个网络环境都发生了故障。
网络分区是指在分布式系统中,不一样的节点分布在不一样的子网络(机房或异地网络) 中,因为一些特殊的缘由致使这些子网络出现网络不连通的情况,但各个子网络的内部网络是正常的,从而致使整个系统的网络环境被切分红了若干个孤立的区域。 须要注意的是,组成一个分布式系统的每一个节点的加入与退出均可以看做是一个特殊的网络分区。
既然一个分布式系统没法同时知足一致性、可用性、分区容错性三个特色,因此咱们就须要抛弃同样:
用一张表格说明一下:
选 择 说 明
CA 放弃分区容错性,增强一致性和可用性,其实就是传统的单机数据库的选择
AP 放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是不少分布式系统设计时的选择,例如不少NoSQL系统就是如此
CP 放弃可用性,追求一致性和分区容错性,基本不会选择,网络问题会直接让整个系统不可用
须要明确的一点是,对于一个分布式系统而言,分区容错性是一个最基本的要求。由于 既然是一个分布式系统,那么分布式系统中的组件必然须要被部署到不一样的节点,不然也就无所谓分布式系统了,所以必然出现子网络。而对于分布式系统而言,网 络问题又是一个一定会出现的异常状况,所以分区容错性也就成为了一个分布式系统必然须要面对和解决的问题。所以系统架构师每每须要把精力花在如何根据业务 特色在C(一致性)和A(可用性)之间寻求平衡。
BASE理论
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即便没法作到强一致性,但每一个应用均可以根据自身业务特色,采用适当的方式来使系统达到最终一致性。接下来看一下BASE中的三要素:
一、基本可用
基本可用是指分布式系统在出现不可预知故障的时候,容许损失部分可用性----注意,这毫不等价于系统不可用。好比:
(1)响应时间上的损失。正常状况下,一个在线搜索引擎须要在0.5秒以内返回给用户相应的查询结果,但因为出现故障,查询结果的响应时间增长了1~2秒
(2)系统功能上的损失:正常状况下,在一个电子商务网站上进行购物的时候,消费者几乎可以顺利完成每一笔订单,可是在一些节日大促购物高峰的时候,因为消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
二、软状态
软状态指容许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的总体可用性,即容许系统在不一样节点的数据副本之间进行数据同步的过程存在延时
三、最终一致性
最终一致性强调的是全部的数据副本,在通过一段时间的同步以后,最终都可以达到一个一致的状态。所以,最终一致性的本质是须要系统保证最终数据可以达到一致,而不须要实时保证系统数据的强一致性。
总的来讲,BASE理论面向的是大型高可用可扩展的分布式系统,和传统的事物ACID特性是相反的,它彻底不一样于ACID的强一致性模型,而是经过牺牲强一致性来得到可用性,并容许数据在一段时间内是不一致的,但最终达到一致状态。但同时,在实际的分布式场景中,不一样业务单元和组件对数据一致性的要求是不一样的,所以在具体的分布式系统架构设计过程当中,ACID特性和BASE理论每每又会结合在一块儿。
怎么考虑数据一致性问题
说说最终一致性的实现方案
你怎么看待微服务
微服务与 SOA 的区别
如何拆分服务
微服务如何进行数据库管理
如何应对微服务的链式调用异常 资源隔离 重试 熔断
对于快速追踪与定位问题
微服务的安全
分布式
谈谈业务中使用分布式的场景
Session 分布式方案
分布式锁的场景
分布是锁的实现方案
分布式事务
集群与负载均衡的算法与实现
Dubbo 负载均衡策略提供下列四种方式:
Random LoadBalance 随机,按权重设置随机几率。 Dubbo的默认负载均衡策略
在一个截面上碰撞的几率高,但调用量越大分布越均匀,并且按几率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance 轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求问题,好比:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,长此以往,全部请求都卡在调到第二台上。
LeastActive LoadBalance 最少活跃调用数,相同活跃数的随机,活跃数指调用先后计数差。
使慢的提供者收到更少请求,由于越慢的提供者的调用先后计数差会越大。
ConsistentHash LoadBalance 一致性Hash,相同参数的请求老是发到同一提供者。
当某一台提供者挂时,本来发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引发剧烈变更。
说说分库与分表设计
分库与分表带来的分布式困境与应对之策
复制代码
全网惟一一个从0开始帮助Java开发者转作大数据领域的公众号~
大数据技术与架构或者搜索import_bigdata关注~
海量【java和大数据的面试题+视频资料】整理在公众号,关注后能够下载~