您当前的位置: 首页 > 

cuiyaonan2000

暂无认证

  • 2浏览

    0关注

    248博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

杠杠的笔记

cuiyaonan2000 发布时间:2018-10-26 19:52:17 ,浏览量:2

---------------------------------------------------------------------------------------  StringBuffer、ArrayList、HashMap的初始容量、已经如何扩充的总结(适用范围:JDK1.7)  StringBuffer:内部实现是一个字符数组。初始默认大小为16,当然也可以在其构造方法中进行设置。当新添加字符或字符串时,发现数组容量不够。这个时候就需要使用Array.copyOf()方法进行扩充。扩充的新的数组大小等于,(原始容量*2+2)和(数组实际字符个数+新增的字符长度)之间的较大值。

ArrayList:内部实现是一个Object的数组。初始默认大小为0,当然也可以在其构造方法中设置。当添加一个Object时,默认扩充数组容量为10。然后每次扩充的新的数组大小等于,(原始容量*3/2)和(数组的长度+1)之间的较大值。根据每次增加一个Object,可得该情况每次扩充的固定大小为3/2。当初始大小为手动设置的时候,每次扩充的新的数组大小等于,(原始容量*3/2)和(数组的长度+1)之间的较大值。

HashMap:内部实现是一个Entry的数组,默认大小是空的数组。初始化的容量是16,加载因子是3/4(当数组元素数量大于总容量的加载因子的时候,扩充数组)。当默认不是空的数组时,当达到加载因子的比例的时候,每次扩充初始容量的2倍。

---------------------------------------------------------------------------------------    ----------------------------------------------------------------------------------------  ArrayList         import java.util.ArrayList;     import java.util.List;     提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表、     从上面介绍的向ArrayList中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过一个公开的方法ensureCapacity(intminCapacity)来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。          ARRAYLIST VECTOR LINKEDLIST 区别与用法     1. Vector & ArrayList     1)  Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。     2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。 ----------------------------------------------------------------------------------------    

----------------------------------------------------------------------------------------     HashMap

    数组:数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;          链表:链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。          哈希表:那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表。哈希表((Hash table)既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。          以Entry[]数组实现的哈希桶数组,用Key的哈希值取模桶数组的大小可得到数组下标. 插入元素时,如果两条Key落在同一个桶(比如哈希值1和17取模16后都属于第一个哈希桶),Entry用一个next属性实现多个Entry以单向链表存放,后入桶的Entry将next指向桶当前的Entry。   查找哈希值为17的key时,先定位到第一个哈希桶,然后以链表遍历桶里所有元素,逐个比较其key值。     当Entry数量达到桶数量的75%时(很多文章说使用的桶数量达到了75%,但看代码不是),会成倍扩容桶数组,并重新分配所有原来的Entry,所以这里也最好有个预估值。

    取模用位运算(hash & (arrayLength-1))会比较快,所以数组的大小永远是2的N次方, 你随便给一个初始值比如17会转为32。默认第一次放入元素时的初始值是16。

    iterator()时顺着哈希桶数组来遍历,看起来是个乱序。     在JDK8里,新增默认为8的閥值,当一个桶里的Entry超过閥值,就不以单向链表而以红黑树来存放以加快Key的查找速度。          1、HashMap是非线程安全的,HashTable是线程安全的。     2、HashMap的键和值都允许有null值存在,而HashTable则不行。     3、因为线程安全的问题,HashMap效率比HashTable的要高。 ----------------------------------------------------------------------------------------                     ---------------------------------------------------------------------------------------- JAVA深复制(深克隆)与浅复制(浅克隆)  (Cloneable接口实现 clone方法)

    ⑴浅复制(浅克隆)     被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。

    ⑵深复制(深克隆)     被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原     ⑴clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:     ①对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象     ②对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样     ③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。

    ⑵Java中对象的克隆     ①为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。     ②在派生类中覆盖基类的clone()方法,并声明为public。     ③在派生类的clone()方法中,调用super.clone()。     ④在派生类中实现Cloneable接口。 ----------------------------------------------------------------------------------------                          ----------------------------------------------------------------------------------------     垃圾回收用到的方法

    (1)finalize()    该方法是用来回收“特殊”的内存,而这内存不是new出来的,所以垃圾回收器无法回收。这种情况主要发生在使用“本地方法”的情况下,本地方法是一种在Java中使用的非Java代码,可以调用任何代码,但只能被C和C++调用。所以我们使用finalize()来释放本地方法产生的内存。

    (2)System.gc()     该方法不推荐,严重消耗性能,除非万不得已,一般不用。 ----------------------------------------------------------------------------------------       ---------------------------------------------------------------------------------------- 关于栈和堆内存

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存

    在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。

    堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。

    引用变量是普通变量,定义时在栈中分配内存,引用变量在程序运行到作用域外释放。而数组&对象本身在堆中分配,即使程序运行到使用new产生数组和对象的语句所在地代码块之外,数组和对象本身占用的堆内存也不会被释放,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!  ----------------------------------------------------------------------------------------                    ---------------------------------------------------------------------------------------- 自动装箱&自动拆箱

    基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。      Integer i = 10; //装箱     int t = i; //拆箱,实际上执行了 int t = i.intValue();          Integer i = 100;     Integer i = Integer.valueOf(100);     //在-128~127 之外的数      Integer i1 =200;        Integer i2 =200;                System.out.println("i1==i2: "+(i1==i2));                         // 在-128~127 之内的数      Integer i3 =100;        Integer i4 =100;        System.out.println("i3==i4: "+(i3==i4));           String str1 ="abc";     String str2 ="abc";     System.out.println(str2==str1);  //输出为 true      System.out.println(str2.equals(str1));  //输出为 true                   String str3 =new String("abc");     String str4 =new String("abc");      System.out.println(str3==str4);  //输出为 false      System.out.println(str3.equals(str4));  //输出为 true ----------------------------------------------------------------------------------------           ---------------------------------------------------------------------------------------- 四种线程池      Java通过Executors提供四种线程池,分别为:     newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程     newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。     newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。    newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。          public static void main(String[] args) {           jervice cachedThreadPool = Executors.newCachedThreadPool();           for (int i = 0; i < 10; i++) {               final int index = i;               try {                   Thread.sleep(10);               } catch (InterruptedException e) {                   e.printStackTrace();               }               cachedThreadPool.execute(new Runnable() {                   public void run() {                       System.out.println(index);                   }               });           }       }             public static void main(String[] args) {           ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);           for (int i = 0; i < 10; i++) {               final int index = i;               fixedThreadPool.execute(new Runnable() {                   public void run() {                       try {                           System.out.println(index);                           Thread.sleep(10);                       } catch (InterruptedException e) {                           e.printStackTrace();                       }                   }               });           }       }            public static void main(String[] args) {           ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);           for (int i = 0; i < 10; i++) {               scheduledThreadPool.schedule(new Runnable() {                   public void run() {                       System.out.println("delay 3 seconds");                   }               }, 3, TimeUnit.SECONDS);           }          }            public static void main(String[] args) {           ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();           for (int i = 0; i < 10; i++) {               final int index = i;               singleThreadExecutor.execute(new Runnable() {                   public void run() {                       try {                           System.out.println(index);                           Thread.sleep(2000);                       } catch (InterruptedException e) {                           e.printStackTrace();                       }                   }               });           }       }                                                                                                                                                       ----------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------- 关于如何停止一个线程的方法:

A:停止一个线程可以用Thread.stop()方法,但最好不要用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全, 而且是已被废弃的方法。

B:System.exit();  在主线程调用则会,同时停止掉所含的子线程.如果在子线程中调用,则只会停掉该子线程.

C:异常停止

D:return 返回

E:interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt方法是在当前线程中打了一个停止标志,并不是真的停止线程。

----------------------------------------------------------------------------------------

      ---------------------------------------------------------------------------------------- 创建线程的3种方法     A: 继承Thread类创建线程类 run()方法 类对象.start()启动     B: 通过Runnable接口创建线程类, run()方法 new Thread(该对象实例,"新线程1").start();       C: 通过Callable接口创建线程类, call()方法,启动: FutureTask ft = new FutureTask(该对象实例);   new Thread(ft,"有返回值的线程").start();  ----------------------------------------------------------------------------------------               ---------------------------------------------------------------------------------------- wait()、notify()、notifyAll()     如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。     如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。     如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。 ----------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------- 关于异常的体系结构         Throwable         A:Error(硬件异常)           B:Exception(程序异常)又包含1:Checked(编译异常,java特有) 2:RuntimeException(运行时异常) ----------------------------------------------------------------------------------------              java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。  Collection   ├List   │├LinkedList   │├ArrayList   │└Vector   │ └Stack   └Set  

java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。                            ---------------------------------------------------------------------------------------- 索引生效规则     btree索引的常见误区     在where条件常用的列上都加上索引     比如:where cat_id=3 and price>100 #查询第3个栏目,100以上的商品     只能用上cat_id或price索引,因为独立的索引同时只能用上1个。     组合索引的生效原则是  从前往后依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用;          比如:     where a=3 and b=45 and c=5 .... 这种三个索引顺序使用中间没有断点,全部发挥作用;     where a=3 and c=5... 这种情况下b就是断点,a发挥了效果,c没有效果     where b=3 and c=4... 这种情况下a就是断点,在a后面的索引都没有发挥作用,这种写法联合索引没有发挥任何效果;     where b=45 and a=3 and c=5 .... 这个跟第一个一样,全部发挥作用,abc只要用上了就行,跟写的顺序无关     (0)    select * from mytable where a=3 and b=5 and c=4;     abc三个索引都在where条件里面用到了,而且都发挥了作用     (1)    select * from mytable where  c=4 and b=6 and a=3;     这条语句列出来只想说明 mysql没有那么笨,where里面的条件顺序在查询之前会被mysql自动优化,效果跟上一句一样     (2)    select * from mytable where a=3 and c=7;     a用到索引,b没有用,所以c是没有用到索引效果的     (3)    select * from mytable where a=3 and b>7 and c=3;     a用到了,b也用到了,c没有用到,这个地方b是范围值,也算断点,只不过自身用到了索引     (4)    select * from mytable where b=3 and c=4;     因为a索引没有使用,所以这里 bc都没有用上索引效果     (5)    select * from mytable where a>4 and b=7 and c=9;     a用到了  b没有使用,c没有使用     (6)    select * from mytable where a=3 order by b;     a用到了索引,b在结果排序中也用到了索引的效果,前面说了,a下面任意一段的b是排好序的     (7)    select * from mytable where a=3 order by c;     a用到了索引,但是这个地方c没有发挥排序效果,因为中间断点了,使用 explain 可以看到 filesort     (8)    select * from mytable where b=3 order by a;     b没有用到索引,排序中a也没有发挥索引效果 ----------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------- 一方库、二方库、三方库说明: 一方库:本工程中的各模块的相互依赖 二方库:公司内部的依赖库,一般指公司内部的其他项目发布的jar包 三方库:公司之外的开源库, 比如apache、ibm、google等发布的依赖 ----------------------------------------------------------------------------------------  

  ----------------------------------------------------------------------------------------  Synchronized 可以修饰:类,方法,静态方法,语句块.    synchronized 和 block区别:  synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。 lock:需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。用法区别比较简单,这里不赘述了,如果不懂的可以看看Java基本语法。  synchronized是托管给JVM执行的,而lock是java写的控制锁的代码。在Java1.5中,synchronize是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地。  ----------------------------------------------------------------------------------------    数据库索引优化    threadlocal 线程间共享数据    安全的集合方式       设计模式:     1:单例模式     2:原型模式    ----即cloneable     3:观察者模式     4:代理模式     5:策略模式---可以利用到注解来选择不同的策略,主要解决if----else   策略模式针对不同物种的共有操作     6:模板模式    模板模式共有操作的不同物种

     javadoc 注释     TODO     FIXME           38.volatile关键字

Spring mvc与Struts区别 :http://blog.csdn.net/chenleixing/article/details/44570681

  ---------------------------------------------------------------------------------------- Hadoop Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提 ----------------------------------------------------------------------------------------       ---------------------------------------------------------------------------------------- zookeeper zookeeper: 统一配置管理(简单) 名字服务(类似DNS) 分布式锁(服务并行执行的协调) 集群管理(节点的管理,与是否可用的监察)

 原理:zookeeper在配置文件中并没有指定master和slave,但是,zookeeper在工作时,只有一个节点为leader,其余节点为follower,leader是通过内部的选举机制临时产生的。

   a:是为别的分布式程序服务的  b:本身就是一个分布式程序(只要半数以上节点存活,zookeeper就能正常服务。)  C:服务范围:主从协调、服务器节点动态上下线、统一配置管理、分布式共享锁、统一名称服务...  D:底层其实只提供了两个功能:     1)管理(存储、读取)用户程序提交的数据     2)为用户程序提交数据节点监听服务                    Zookeeper:一个leader,多个follower组成的集群     全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的     分布式读写,更新请求转发,由leader实施     更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行     数据更新原子性,一次数据更新要么成功,要么失败     实时性,在一定时间范围内,client能读到最新数据

     1、Znode有两种类型:         短暂(ephemeral)(断开连接自己删除)         持久(persistent)(断开连接不删除)

4、zookeeper数据结构     层次化的目录结构,命名符合常规文件系统规范(见下图)     每个节点在zookeeper中叫做Znode,并且其有一个唯一的路径标识     节点Znode可以包含数据和子节点(但是EPHEMERAL类型的节点不能有子节点)     客户端应用可以在节点上设置监视器

    ----------------------------------------------------------------------------------------     ----------------------------------------------------------------------------------------  子线程等待主线程的方法: join 或者 CountDownLatch  正如每个Java文档所描述的那样,CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。在Java并发中,countdownlatch的概念是一个常见的面试题,所以一定要确保你很好的理解了它。在这篇文章中,我将会涉及到在Java并发编 程中跟CountDownLatch相关的以下几点: ----------------------------------------------------------------------------------------     ----------------------------------------子线程等待父线程结束----------------------------- 进程的 join 和 countdownlatch 

    join用于让当前执行线程等待join线程执行结束。其实现原理是不停检查join线程是否存活.     如上即是: 子线程在main方法中调用了join,则main该主线程一直等待该线程结束了在继续执行.(非常好)       当我们调用一次CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await会阻塞当前线程,直到N变成零。     由于countDown方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是1个线程里的N个执行步骤。用在多个线程时,你只需要把这个CountDownLatch的引用传递到线程里。 ----------------------------------------子线程等待父线程结束-----------------------------    ----------------------------------------------------------------------------------------  final,finally,finalize:       final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。     finally是异常处理语句结构的一部分,表示总是执行。     finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。      try catch finaly 中的return问题:       1, 如果catch块中捕获了异常, 并且在catch块中将该异常throw给上级调用者进行处理, 但finally中return了, 那么catch块中的throw就失效了, 上级方法调用者是捕获不到异常的. 见demo如下:     2, 如果在finally里的return之前执行了其它return , 那么最终的返回值是finally中的return:     3, try 和 catch中都有return, (只要try中的内容没报错).并且catch中可以存在return.    ----------------------------------------------------------------------------------------       ----------------------------------------------------------------------------------------     collection collections:     1.java.util.Collection 是一个集合接口。    它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。     以下接口实现了Collection接口:map,set,list,vector       java.util.Collections 是一个包装类。     它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。 ----------------------------------------------------------------------------------------   ----------------------------------------------------------------------------------------  ThreadLocal  我们看到虽然 threadLocal 是静态变量,但是每个线程都有自己的值,不会受到其他线程的影响。    ThreadLocal 的实现思想,我们在前面已经说了,每个线程维护一个 ThreadLocalMap 的映射表,映射表的 key 是 ThreadLocal 实例本身,value 是要存储的副本变量。ThreadLocal 实例本身并不存储值,它只是提供一个在当前线程中找到副本值的 key。 如下图所示: ----------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------- collectons.sort    

A:要排序的类,实现接口Comparable的compareTo 方法 B: 如下做匿名内部类 Collections.sort(listA, new Comparator() {             @Override             public int compare(TestA o1, TestA o2) {                 //升序                 return o1.getOrder().compareTo(o2.getOrder());             }         });

----------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------     redis 是单线程     并不是所有的KV数据库或者内存数据库都应该用单线程,比如ZooKeeper就是多线程的,最终还是看作者自己的意愿和取舍。

     1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。      2、节点的fail是通过集群中超过半数的节点检测失效时才生效。      3、客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。      4、redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护nodeslotvalue。

     5、Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。                  Redis Cluster主从模式      redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。

     上面那个例子里, 集群有ABC三个主节点, 如果这3个节点都没有加入从节点,如果B挂掉了,我们就无法访问整个集群了。A和C的slot也无法访问。

     所以我们在集群建立的时候,一定要为每个主节点都添加了从节点, 比如像这样, 集群包含主节点A、B、C, 以及从节点A1、B1、C1, 那么即使B挂掉系统也可以继续正确工作。

     B1节点替代了B节点,所以Redis集群将会选择B1节点作为新的主节点,集群将会继续正确地提供服务。 当B重新开启后,它就会变成B1的从节点。

     不过需要注意,如果节点B和B1同时挂了,Redis集群就无法继续正确地提供服务了。

    redis:只能保存, 字符串和byte[] 类型的数据. 但是提供了方法可以存入  List 和 Set 格式的数据

----------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------- nginx 相对 apache 的优点:     轻量级,同样起web 服务,比apache 占用更少的内存及资源     抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能     高度模块化的设计,编写模块相对简单     社区活跃,各种高性能模块出品迅速啊

apache 相对nginx 的优点:     rewrite ,比nginx 的rewrite      强大模块超多,基本想到的都可以找到     少bug ,nginx 的bug 相对较多     超稳定      nginx轮询策略

    1、轮询(默认)     每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 

    2、指定权重     指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况

    3、IP绑定 ip_hash     每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 

    4、fair(第三方)     按后端服务器的响应时间来分配请求,响应时间短的优先分配。

    5、url_hash(第三方)     按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

----------------------------------------------------------------------------------------

----------------------------------------fastDFS----------------------------------------------

FastDFS由跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)构成。

                                            跟踪服务器Tracker Server 主要做调度工作,起到均衡的作用;负责管理所有的 storage server和 group,每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。tracker根据storage的心跳信息,建立group==>[storage serverlist]的映射表。 Tracker需要管理的元信息很少,会全部存储在内存中;另外tracker上的元信息都是由storage汇报的信息生成的,本身不需要持久化任何数据,这样使得tracker非常容易扩展,直接增加tracker机器即可扩展为tracker cluster来服务,cluster里每个tracker之间是完全对等的,所有的tracker都接受stroage的心跳信息,生成元数据信息来提供读写服务。

                                            存储服务器Storage Server 主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。以group为单位组织存储能方便的进行应用隔离、负载均衡、副本数定制(group内storage server数量即为该group的副本数),比如将不同应用数据存到不同的group就能隔离应用数据,同时还可根据应用的访问特性来将应用分配到不同的group来做负载均衡;缺点是group的容量受单机存储容量的限制,同时当group内有机器坏掉时,数据恢复只能依赖group内地其他机器,使得恢复时间会很长。

group内每个storage的存储依赖于本地文件系统,storage可配置多个数据存储目录,比如有10块磁盘,分别挂载在/data/disk1-/data/disk10,则可将这10个目录都配置为storage的数据存储目录。storage接受到写文件请求时,会根据配置好的规则选择其中一个存储目录来存储文件。为了避免单个目录下的文件数太多,在storage第一次启动时,会在每个数据存储目录里创建2级子目录,每级256个,总共65536个文件,新写的文件会以hash的方式被路由到其中某个子目录下,然后将文件数据作为本地文件存储到该目录中。                                                客户端Client 主要是上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。每个客户端服务器都需要安装Nginx

                                           文件上传的步骤: 文件上传分为选择Tracker,选择Group,选择Storage,生成Field,选择两级目录,生成文件名这么几个步骤.现在互联网项目,为了保证服务可靠性,都会搭建集群,所以按照这种方式来总结.

选择Tracker: Tracker集群中所有的Tracker地位都是对等的,客户端上传文件时会任意选择一个Tracker.

选择Group: Tracker收到上传请求之后,会分配一个Group来存储文件,提供的规则有:轮询所有的Group,指定一个Group,负载均衡(剩余空间多的优先)

选择Storage: 分配好Group之后,Tracker会在Group中选择一个Storage,提供的规则有:轮询所有的Storage,根据ip排序,根据Storage优先级排序. 在选定好了Storage之后客户端向Storage发送写入文件请求,Storage为文件分配一个数据存储目录,提供的规则有:存储目录轮询,负载均衡

生成Field: 选择好存储目录之后,Storage为文件分配一个Field,由Storage的ip + 文件创建的时间戳 + 文件大小 + 文件crc32校验后+一个随机数拼接而成.再将这个二进制串进行Base64编码转换成String.

选择两级目录:生成field之后,每个存储目录下会有两级256*256的子目录,Storage会按照field进行第一次hash,路由到第一级子目录,再进行第二次hash,存储到对应的子目录下.

                                            关于如何解决 文件同步的问题 一个最简单的解决办法,和文件更新一样,优先选择源Storage server下载文件即可。这可以在Tracker server的配置文件中设置,对应的参数名为download_server。

另外一种选择Storage server的方法是轮流选择(round-robin)。当Client询问Tracker server有哪些Storage server可以下载指定文件时,Tracker server返回满足如下四个条件之一的Storage server:

  1、该文件上传到的源Storage server,文件直接上传到该服务器上的;

2、文件创建时间戳 < Storage server被同步到的文件时间戳,这意味着当前文件已经被同步过来了;

3、文件创建时间戳=Storage server被同步到的文件时间戳,且(当前时间—文件创建时间戳) > 一个文件同步完成需要的最大时间(如5分钟);

4、(当前时间—文件创建时间戳) > 文件同步延迟阈值,比如我们把阈值设置为1天,表示文件同步在一天内肯定可以完成。

----------------------------------------fastDFS----------------------------------------------

-----------------------------------------JVM垃圾回收机制-------------------------------------- 一.如何确定某个对象是“垃圾”?

引用计数: 在java中是通过引用来和对象进行关联的,也就是说如果要操作对象,必须通过引用来进行。那么很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收。不失一般性,如果一个对象没有任何引用与之关联,则说明该对象基本不太可能在其他地方被使用到,那么这个对象就成为可被回收的对象了。这种方式成为引用计数法。

这种方式的特点是实现简单,而且效率较高,但是它无法解决循环引用的问题,因此在Java中并没有采用这种方式(Python采用的是引用计数法)。

可达分析: 为了解决这个问题,在Java中采取了 可达性分析法。该方法的基本思想是通过一系列的“gc Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。

二.典型的垃圾收集算法

1.Mark-Sweep(标记-清除)算法 这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示: 这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:

从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。

2.Copying(复制)算法 为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示:

这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。 很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。

3.Mark-Compact(标记-整理)算法 为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:

4.Generational Collection(分代收集)算法 分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

三.典型的垃圾收集器 垃圾收集算法是 内存回收的理论基础,而垃圾收集器就是内存回收的具体实现。下面介绍一下HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户可以根据自己的需求组合出各个年代使用的收集器。

对象的内存分配,往大方向上讲就是在堆上分配,对象主要分配在新生代的Eden Space和From Space,少数情况下会直接分配在老年代。如果新生代的Eden Space和From Space的空间不足,则会发起一次GC,如果进行了GC之后,Eden Space和From Space能够容纳该对象就放在Eden Space和From Space。在GC的过程中,会将Eden Space和From  Space中的存活对象移动到To Space,然后将Eden Space和From Space进行清理。如果在清理的过程中,To Space无法足够来存储某个对象,就会将该对象移动到老年代中。在进行了GC之后,使用的便是Eden space和To Space了,下次GC时会将存活对象复制到From Space,如此反复循环。当对象在Survivor区躲过一次GC的话,其对象年龄便会加1,默认情况下,如果对象年龄达到15岁,就会移动到老年代中。

一般来说,大对象会被直接分配到老年代,所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组,比如:

byte[] data = new byte[4*1024*1024]

这种一般会直接在老年代分配存储空间。

当然分配的规则并不是百分之百固定的,这要取决于当前使用的是哪种垃圾收集器组合和JVM的相关参数。

1.Serial/Serial Old

Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。

2.ParNew

ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。

3.Parallel Scavenge

Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。

4.Parallel Old

Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。

5.CMS

CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。

-----------------------------------------JVM垃圾回收机制--------------------------------------

---------------------------------------------System.gc()----------------------------------------

其实这个gc()函数的作用只是提醒虚拟机:程序员希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行,而且具体什么时候进行是取决于具体的虚拟机的,不同的虚拟机有不同的对策。

最后的箴言:不要频繁使用gc函数。 我的建议是:保持代码健壮(记得将不用的变量置为null),让虚拟机去管理内存。 ---------------------------------------------System.gc()----------------------------------------

----------------------------------------hibernate 缓存---------------------------------------  一级缓存:指的是Session缓存,基于线程的缓存

 二级缓存: 指的是SessionFactory的缓存,是基于进程的缓存(由于SessionFactory的声明周期和程序的声明周期是相同的,并且是单例的)

     二级缓存又分为内置缓存和外置缓存         内置缓存是hibernate中自己编写的         外置缓存是hibernate借助第三方插件的缓存         外置缓存又分为class缓存和查询缓存             这两种缓存都采用的是map结构,前者的key保存的是sql语句的字符串,value保存的是主键id的集合                                          后者的key保存的是id,value保存的是id所对应的某一条记录封装后的对象    三级缓存:指的是外置缓存中的查询缓存     由于查询缓存依赖于class缓存,所以说三级缓存是基于二级缓存之上的.

  ----------------------------------------hibernate 缓存---------------------------------------

----------------------------------------hibernate get load 的区别--------------------------------- 1. 对于get方法,hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据库中没有就返回null。这个相对比较简单,也没有太大的争议。主要要说明的一点就是在这个版本中get方法也会查找二级缓存!

 

2.  load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true),分情况讨论:

(1)若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一个ObjectNotFoundException。

(2)若为false,就跟get方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException。

总之对于get和load的根本区别,一句话,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,hibernate一定要获取到真实的数据,否则返回null。

----------------------------------------hibernate get load 的区别---------------------------------

-------------------------------------spring 工作原理-------------------------------------

在spring ioc中有三种依赖注入,分别是: a、接口注入; b、setter方法注入; c、构造方法注入;

就是 ioc 和 aop

ioc 就是反射 aop 就是动态代理

-------------------------------------spring 工作原理-------------------------------------

------------------------------------------关于反射--------------------------------------- public Object invoke(Object obj,Object... args)

public Object invoke(Object obj,Object... args)

 Class clazz = Class.forName("net.xsoftlab.baike.Person");   // 调用Person类中的run方法   Method method = clazz.getMethod("run");   method.invoke(clazz.newInstance());   // Java 反射机制 - 调用某个类的方法1.   // 调用Person的Speak方法   method = clazz.getMethod("Speak", int.class, String.class);   method.invoke(clazz.newInstance(), 22, "小明");   // Java 反射机制 - 调用某个类的方法2.   // age -> 22. name -> 小明  

                                           如何获取私有属性和私有方法(如果要调用私有属性还需设置属性可达.                         // 要设置属性可达,不然会抛出IllegalAccessException异常field.setAccessible(true);     //仅能获取共有的属性     Field[] fieldArray = clazz.getFields();         //能够获取共有和私有的所有属性     Field[] declaredArray = clazz.getDeclaredFields();     for(int i = 0 ; i < fieldArray.length ; i++){         System.out.println( fieldArray[i].getName() );     }          for(int i = 0 ; i < declaredArray.length; i++){         System.out.println( declaredArray[i].getName() );     }          //仅能获取共有的方法     Method[] methodArray = clazz.getMethods();     //能够获取公有和私有的方法     Method[] methodDeclaredArray = clazz.getDeclaredMethods();

                                      反射获取所有的成员变量 // 要获取类的信息,首先要获取类的类类型     Class class1 = object.getClass();// 传递的是哪个子类的对象,class1就是该子类的类类型 /**  * 成员变量也是对象 java.lang.reflect.Field Field类封装了关于成员变量的操作  * getFields()方法获取的是所有的public的成员变量的信息  * getDeclaredFields获取的是该类自己声明的成员变量的信息  */     Field[] fs = class1.getFields();

                                      反射获取所有的成员方法  // 要获取类的信息,首先要获取类的类类型     Class class1 = object.getClass();// 传递的是哪个子类的对象,class1就是该子类的类类型 // 获取类的名称     System.out.println("类的名称是:" + class1.getName()); /** * Method类,方法对象 一个成员方法就是一个Method对象 getMehtod()方法 * 获取的是所有得public的函数,包括父类继承的 getDeclaredMethods()获取的是所有该类声明的方法,不同访问权限 */     Method[] ms = class1.getMethods();     for (int i = 0; i < ms.length; i++) { // 得到方法的返回值类型的类类型     Class returnType = ms[i].getReturnType(); // 得到方法的名称     System.out.print(ms[i].getName() + "("); // 获取参数类型     Class[] paramTypes = ms[i].getParameterTypes();

------------------------------------------关于反射---------------------------------------

----------------------------------char varchar varchar2 的区别 ------------------------------------------ 区别: 1.CHAR的长度是固定的,而VARCHAR2的长度是可以变化的, 比如,存储字符串“abc",对于CHAR (20),表示你存储的字符将占20个字节(包括17个空字符),而同样的VARCHAR2 (20)则只占用3个字节的长度,20只是最大值,当你存储的字符小于20时,按实际长度存储。  2.CHAR的效率比VARCHAR2的效率稍高。  3.目前VARCHAR是VARCHAR2的同义词。工业标准的VARCHAR类型可以存储空字符串,但是oracle不这样做,尽管它保留以后这样做的权利。Oracle自己开发了一个数据类型VARCHAR2,这个类型不是一个标准的VARCHAR,它将在数据库中varchar列可以存储空字符串的特性改为存储NULL值。如果你想有向后兼容的能力,Oracle建议使用VARCHAR2而不是VARCHAR。

varchar       存放固定长度的字符数据,最长2000个字符。

varchar2    存放可变长字符数据,最大长度为4000字符。,最大長度為4000字符。

varchar     是标准sql提供的数据类型。

varchar2  是oracle提供的独特的数据类型。

oracle保证在任何版本中该数据类型向上和向下兼容,但不保证varchar。

总之,如果想新版本的数据库兼容就不要用varchar,如果想和其他数据库兼容就不要用varchar2。

何时该用CHAR,何时该用varchar2?  CHAR与VARCHAR2是一对矛盾的统一体,两者是互补的关系.  VARCHAR2比CHAR节省空间,在效率上比CHAR会稍微差一些,即要想获得效率,就必须牺牲一定的空间,这也就是我们在数据库设计上常说的‘以空间换效率’。  VARCHAR2虽然比CHAR节省空间,但是如果一个VARCHAR2列经常被修改,而且每次被修改的数据的长度不同,这会引起‘行迁移’(Row Migration)现象,而这造成多余的I/O,是数据库设计和调整中要尽力避免的,在这种情况下用CHAR代替VARCHAR2会更好一些。

----------------------------------char varchar varchar2 的区别 ------------------------------------------

----------------------------------------代理的3种模式------------------------------------

1.1.静态代理

代理类也需要实现 目标接口类

动态代理 JDK方式: 1.代理对象,不需要实现接口 2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型) 3.动态代理也叫做:JDK代理,接口代理

cglib:(子类代理,同时也是需要 实现MethodInterceptor这个接口的.) 上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理 Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

在Spring的AOP编程中: 如果加入容器的目标对象有实现接口,用JDK代理 如果目标对象没有实现接口,用Cglib代理

----------------------------------------代理的3种模式------------------------------------

----------------------------------------runnabel callable/futuretask 区别----------------------------------

相同点:

    两者都是接口;(废话)     两者都可用来编写多线程程序;     两者都需要调用Thread.start()启动线程;

 

不同点:

    两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;     Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

----------------------------------------runnabel callable/futuretask 区别----------------------------------

-------------------------------------------分布式数据库&大数据下的处理方法------------------------------------

二阶段提交 三阶段提交

在两阶段提交协议中,系统一般包含两类机器(或节点):     一类为协调者(coordinator),通常一个系统中只有一个;      另一类为事务参与者(participants,cohorts或workers),一般包含多个,在数据存储系统中可以理解为数据副本的个数。协议中假设每个节点都会记录写前日志(write-ahead log)并持久性存储,即使节点发生故障日志也不会丢失。协议中同时假设节点不会发生永久性故障而且任意两个节点都可以互相通信。

                 一张表大数据下的解决方案:  分表,分库(垂直分库,水平分库)   分区

-------------------------------------------分布式数据库&大数据下的处理方法------------------------------------

-------------------------------------------hdfs试用场景与不适用场景-------------------------------

使用场景:大文件访问,流失数据访问 不适用场景:存储大量小文件,随机读取低延迟读取

-------------------------------------------hdfs试用场景与不适用场景-------------------------------

-------------------------------------------软件设计中的一些图标---------------------------------- ER图是实体-关系图,包括一些对象和对象的联系,还有对象的属性 用例图是指由参与者(Actor)、用例(Use Case)以及它们之间的关系构成的用于描述系统功能的视图

泳道图,一种UML活动图,能够清晰体现出某个动作发生在哪个部门,常见工具有StarUML、Rose、Visio等。泳道图在纵向上是部门职能,横向是岗位(有时候横向上不区分岗位)。绘图元素与传统流程图类似,但在业务流程主体上,通过泳道(纵向条)区分出执行主体,即部门和岗位来。 -------------------------------------------软件设计中的一些图标----------------------------------

-----------------------------线程的几种状态--------------------- 新建  就绪  运行  阻塞 -----------------------------线程的几种状态----------------------

-------------------------------String 运行时常量池-------------------------

几个关键词: jvm 运行时常量池(符号引用,字面量) String的一些方法  intern(1:如果字符串在 常量池中,则直接返回引用. 2:如果常量池中没有该字符串,则将该字符串放到常量池中)

直接使用new 出来的字符串如果没有intern则不会放入到常量池

关于什么时候会把字符串放入到常量池中 1:页面初始化编译的时候.且是明确的字符串赋值.如果使用s=s1+s2则不会把该字符串放入到常量池中.s="123"会放入     次方法叫静态编译时放入常量池 2:使用intern()方法.此方法交 运行时常量放入

-------------------------------String 运行时常量池-------------------------

---------------------------------------mongodb事务解决方案-------------------

A:作业队列  B:字段同步

---------------------------------------mongodb事务解决方案-------------------

--------------------------------------事务问题-----------------------------------

数据库支持数据块间的事务是有原因的。典型的场景是应用需要修改几个独立的比特时,如果只有一些而不是全部改变存储到了数据库,那么这就会出现不一致问题。因此ACID的概念是:

原子性:所有的改变要么都做了,要么都没做 一致性:数据保持一致性状态 隔离性:其它用户看不到部分改变 持久性:一旦向用户确认了事务,数据就处于安全的状态(通常存在硬盘上)

原子性:实际上你希望所有的改变都完成 一致性:系统短时间不一致没关系,只要最终一致就行 隔离性:缺乏隔离性导致暂时的不一致,这并不理想,但是当今线上服务时代,很多用户对此都习惯了(如用户支持:“它要花几秒传输”)。 持久性:很重要,要支持。 --------------------------------------事务问题-----------------------------------

---------------------------------------mongodb oracle 性能对比----------------------------------------------

数据插入上的对比: 相同表结构,相同数量级的记录  mongodb 始终比 oracle 插入的效率要要快.效率从数量级的增加越来越明显从10万条开始 mondogb就是 一个数量级的差距. 比如 mongodb 是10万毫秒 oracle 就是100万毫秒(在有索引和无索引的下相同)

数据查询上的对比: mongodb 和oracle 在检索上的效率:在有索引和无索引的情况下 以1000万为临界点 mongodb在检索的效率上是 oracle的数值的一半 比如 mongodb 是9000ms oracle 就是 4000ms左右(在1万数据 10万数据 100万数据的检索下效率是差不多的)

---------------------------------------mongodb oracle 性能对比----------------------------------------------

---------------------------------------数据库的横向设计  纵向设计-------------------------------------------

横向设计的利与弊

利:1、预选设定好了所需要的列,对数据库操作方便 弊:1、不易拓展,易冗余

纵向设计的利与弊 利:1、容易拓展,弹性好,遵循数据库设计的XX范式(至于第几条,我真心记不得) 弊:1、失去了对数据的控制权,也就是说很难维持数据的规范化,如果我的程序不规范随意在选修课列中加入‘烹饪技巧课’,那么作为我设定好的理工学校应用场景,这条数据是错误数据     2、要管理和操作纵向表很有难度      在设计中应该追求平衡,必要性      ---------------------------------------数据库的横向设计  纵向设计-------------------------------------------

---------------------------------------数据库视图-------------------------------------------------- 视图具有以下优点: 1.可以限制用户只能通过视图检索数据。这样就可以对最终用户屏蔽建表时底层的基表。 2.可以将复杂的查询保存为视图。可以对最终用户屏蔽一定的复杂性。 3.限制某个视图只能访问基表中的部分列或者部分行的特定数据。这样可以实现一定的安全性。 4.从多张基表中按一定的业务逻辑抽出用户关心的部分,形成一张虚拟表。

语法解析: OR REPLACE:如果视图已经存在,则替换旧视图。 FORCE:即使基表不存在,也可以创建该视图,但是该视图不能正常使用,当基表创建成功后,视图才能正常使用。 NOFORCE:如果基表不存在,无法创建视图,该项是默认选项。 WITH READ ONLY:默认可以通过视图对基表执行增删改操作,但是有很多在基表上的限制(比如:基表中某列不能为空,但是该列没有出现在视图中,则不能通过视图执行insert操作),WITH READ ONLY说明视图是只读视图,不能通过该视图进行增删改操作。现实开发中,基本上不通过视图对表中的数据进行增删改操作。

1.视图是一个虚拟表,其内容由查询定义。 2.视图中的数据是由一张或多张表中的数据组成的。 3.如果你改动了基本表,如果你的视图来源于这个基本表,那视图给你呈现的结果也会随之发生变化。

---------------------------------------数据库视图--------------------------------------------------

--------------------------------Oracle 集群概念和原理----------------------------------------- Oracle RAC 实时应用集群(real applicaiton clusters)

共享存储文件系统(NFS) 集群文件系统(如:OCFS2) Cache Fusion(高缓存合并)技术 主要被用于存储区域网络(所有节点直接访问共享文件系统上存储器), 这就使得节点失效而不影响来自其他节点对文件系统的访问,通常,共享磁盘文件系统用于高可用集群。

硬件上至少需要两台以上的服务器,而且还需要一个共享存储设备;同时还需要两类软件,一类是集群软件,另外一类就是 Oracle 数据库中的 RAC 组件。同时所有服务器上的 OS 都应该是同一类 OS,根据负载均衡的配置策略,当一个客户端发送请求到某一台服务的 listener 后,这台服务器根据负载均衡策略,会把请求发送给本机的 RAC组件处理,也可能会发送给另外一台服务器的 RAC 组件处理,处理完请求后,RAC 会通过群集软件来访问共享存储设备。逻辑结构上看,每一个参加群集的节点有一个独立的实例,这些实例访问同一个数据库。节点之间通过集群软件的通信层(Communication Layer)来进行通信。同时为了减少 I/O 的消耗,存在一个全局缓存服务,因此每一个数据库的实例,都保留了一份相同的数据库 cache。RAC 中的特点如下:

--------------------------------Oracle 集群概念和原理-----------------------------------------

-------------------------------swagger--------------------------- 相信各位在公司写API文档数量应该不少,当然如果你还处在自己一个人开发前后台的年代,当我没说,如今为了前后台更好的对接,还是为了以后交接方便,都有要求写API文档。

手写Api文档的几个痛点:

文档需要更新的时候,需要再次发送一份给前端,也就是文档更新交流不及时。 接口返回结果不明确 不能直接在线测试接口,通常需要使用工具,比如postman 接口文档太多,不好管理 -------------------------------swagger---------------------------

-------------------------关于 volatile修饰的变量 被进程使用---------------------------------------------- volatile修饰的变量值直接存在main memory里面,子线程对该变量的读写直接写入main memory,而不是像其它变量一样在local thread里面产生一份copy。volatile能保证所修饰的变量对于多个线程可见性,即只要被修改,其它线程读到的一定是最新的值。

下面解释一下这段代码为何有可能导致无法中断线程。在前面已经解释过,每个线程在运行过程中都有自己的工作内存,那么线程1在运行的时候,会将stop变量的值拷贝一份放在自己的工作内存当中。

  那么当线程2更改了stop变量的值之后,但是还没来得及写入主存当中,线程2转去做其他事情了,那么线程1由于不知道线程2对stop变量的更改,因此还会一直循环下去。

  但是用volatile修饰之后就变得不一样了:

  第一:使用volatile关键字会强制将修改的值立即写入主存;

  第二:使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);

  第三:由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。

  那么在线程2修改stop值时(当然这里包括2个操作,修改线程2工作内存中的值,然后将修改后的值写入内存),会使得线程1的工作内存中缓存变量stop的缓存行无效,然后线程1读取时,发现自己的缓存行无效,它会等待缓存行对应的主存地址被更新之后,然后去对应的主存读取最新的值。

-------------------------关于 volatile修饰的变量 被进程使用----------------------------------------------

-----------------------创建内部类必须要先创建外部类---------------------------------

A aObj = new Test01().new A();

-----------------------创建内部类必须要先创建外部类---------------------------------

----------------------------------关于hashMap的可以是否可以为null------------------------------ HashMap对象的key、value值均可为null。

HahTable对象的key、value值均不可为null。 ----------------------------------关于hashMap的可以是否可以为null------------------------------

-------------------------------------关于redis的数据类型详解--------------------------------------------------------- Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

A:简单动态字符串 第一篇文章我们就说过 Redis 是用 C 语言写的,但是对于Redis的字符串,却不是 C 语言中的字符串(即以空字符’\0’结尾的字符数组),它是自己构建了一种名为 简单动态字符串(simple dynamic string,SDS)的抽象类型,并将 SDS 作为 Redis的默认字符串表示。

  SDS 定义:

struct sdshdr{      //记录buf数组中已使用字节的数量      //等于 SDS 保存字符串的长度      int len;      //记录 buf 数组中未使用字节的数量      int free;      //字节数组,用于保存字符串      char buf[]; }

B:链表  链表是一种常用的数据结构,C语言内部是没有内置这种数据结构的实现,所以Redis自己构建了链表的实现。关于链表的详细介绍可以参考我的这篇博客。

  链表定义:

typedef  struct listNode{        //前置节点        struct listNode *prev;        //后置节点        struct listNode *next;        //节点的值        void *value;   }listNode

通过多个 listNode 结构就可以组成链表,这是一个双端链表,Redis还提供了操作链表的数据结构: typedef struct list{      //表头节点      listNode *head;      //表尾节点      listNode *tail;      //链表所包含的节点数量      unsigned long len;      //节点值复制函数      void (*free) (void *ptr);      //节点值释放函数      void (*free) (void *ptr);      //节点值对比函数      int (*match) (void *ptr,void *key); }list;

C:字典 字典又称为符号表或者关联数组、或映射(map),是一种用于保存键值对的抽象数据结构。字典中的每一个键 key 都是唯一的,通过 key 可以对值来进行查找或修改。C 语言中没有内置这种数据结构的实现,所以字典依然是 Redis自己构建的。

  Redis 的字典使用哈希表作为底层实现,关于哈希表的详细讲解可以参考我这篇博客。

  哈希表结构定义:

typedef struct dictht{      //哈希表数组      dictEntry **table;      //哈希表大小      unsigned long size;      //哈希表大小掩码,用于计算索引值      //总是等于 size-1      unsigned long sizemask;      //该哈希表已有节点的数量      unsigned long used;   }dictht    哈希表是由数组 table 组成,table 中每个元素都是指向 dict.h/dictEntry 结构,dictEntry 结构定义如下:

typedef struct dictEntry{      //键      void *key;      //值      union{           void *val;           uint64_tu64;           int64_ts64;      }v;        //指向下一个哈希表节点,形成链表      struct dictEntry *next; }dictEntry   key 用来保存键,val 属性用来保存值,值可以是一个指针,也可以是uint64_t整数,也可以是int64_t整数。

  注意这里还有一个指向下一个哈希表节点的指针,我们知道哈希表最大的问题是存在哈希冲突,如何解决哈希冲突,有开放地址法和链地址法。这里采用的便是链地址法,通过next这个指针可以将多个哈希值相同的键值对连接在一起,用来解决哈希冲突。 -------------------------------------关于redis的数据类型详解---------------------------------------------------------

----------------------------------------数组 链表的优缺点------------------------------------------------ 数组与链表的优缺点;         数组:

    优点:使用方便 ,查询效率 比链表高,内存为一连续的区域 (随机查找方便,顺序查找是否跟链表一样)

    缺点:大小固定,不适合动态存储,不方便动态添加,不容易扩展,空间满了需要重新申请空间          链表:

     优点:可动态添加删除   大小可变   ,容易扩展,不怎么收到长度限制,(顺序查找更好吧)      缺点:只能通过顺次指针访问,查询效率低      

----------------------------------------数组 链表的优缺点------------------------------------------------

      ---------高级java必掌握流的几个应用:IO、BIO、NIO、AIO-----------------------

IO/BIO BIO就是指IO,即传统的Blocking IO,即同步并阻塞的IO。这也是jdk1.4之前的唯一选择,依赖于ServerSocket实现,即一个请求对应一个线程,如果线程数不够连接则会等待空余线程或者拒绝连接。所以用这种方式,在高并发情况下效率是很低的,也不可靠,一般只应用于连接数比较小且固定架构的应用,但api也比较容易使用。

NIO 新的IO,即New IO或者Non-Blocking IO,即同步不阻塞的IO。jdk1.4之后提供了一系列的方法来操作流,定义在java.nio包下面。相比于传统的BIO,NIO 提供了高速的面向快的I/O,它加入了Buffer、Channel、Selector等概念。它是基于事件驱动的,采用了Reactor模式,它使用一个线程管理所有的socket通道,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。它的特点是要不断主动地去询问数据有没有处理完,一般只适用于连接数目较大但连接时间短的应用,如聊天应用等。

AIO 新的IO2.0,即NIO2.0,jdk1.7开始应用,叫做异步不阻塞的IO。AIO引入异常通道的概念,采用了Proactor模式,简化了程序编写,一个有效的请求才启动一个线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间长的应用。

几种IO的综合对比: java提供的NIO的api使用比较复杂,一般建议使用像netty这样的框架,而不要使用jdk自带的api。

同步阻塞:你到饭馆点餐,然后在那等着,还要一边喊:好了没啊!                  ------------------------ BIO 同步非阻塞:在饭馆点完餐,就去遛狗了。不过溜一会儿,就回饭馆喊一声:好了没啊!------------------------ NIO 异步阻塞:遛狗的时候,接到饭馆电话,说饭做好了,让您亲自去拿。  异步非阻塞:饭馆打电话说,我们知道您的位置,一会给你送过来,安心遛狗就可以了。------------------------ AIO

---------高级java必掌握流的几个应用:IO、BIO、NIO、AIO-----------------------

-------------------------------------------守护进程------------------------------------------------

守护进程(daemon)是指在UNIX或其他多任务操作系统中在后台执行的电脑程序,并不会接受电脑用户的直接操控。此类程序会被以进程的形式初始化。守护进程程序的名称通常以字母“d”结尾:例如,syslogd就是指管理系统日志的守护进程。 通常,守护进程没有任何存在的父进程(即PPID=1),且在UNIX系统进程层级中直接位于init之下。守护进程程序通常通过如下方法使自己成为守护进程:对一个子进程调用fork,然后使其父进程立即终止,使得这个子进程能在init下运行。这种方法通常被称为“脱壳”。

-------------------------------------------守护进程------------------------------------------------

--------------------------------------------缓存一致性  内存一致性--------------------------------

                        --------------------------------总结-------------------------------------- 缓存一致性问题 。硬件层面的问题,指的是由于多核计算机中有多套缓存,各个缓存之间的数据不一致性问题。

PS:这里还需要再重复一遍,Java多线程中,每个线程都有自己的工作内存,需要和主存进行交互。这里的工作内存和计算机硬件的缓存并不是一回事儿,只是可以相互类比。所以,并发编程的可见性问题,是因为各个线程之间的本地内存数据不一致导致的,和计算机缓存并无关系。

缓存一致性协议。用来解决缓存一致性问题的,常用的是MESI协议。

内存一致性模型。屏蔽计算机硬件问题,主要来解决并发编程中的原子性、有序性和一致性问题。

实现内存一致性模型的时候可能会用到缓存一致性模型。

                        --------------------------------思考-----------------------------------        最后,再给大家留一道思考题: 既然在硬件层面,已经有了缓存一致性协议,可以保证缓存的一致性即并发编程中的可见性,那么为什么在写多线程的代码的时候,程序员要自己使用volatile、synchronized等关键字来保证可见性?

--------------------------------------------缓存一致性  内存一致性--------------------------------

-----------------------------------------------java 内存模型-------------------------------------------- 前面介绍过了计算机内存模型,这是解决多线程场景下并发问题的一个重要规范。那么具体的实现是如何的呢,不同的编程语言,在实现上可能有所不同。

我们知道,Java程序是需要运行在Java虚拟机上面的,Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

提到Java内存模型,一般指的是JDK 5 开始使用的新的内存模型,主要由JSR-133: JavaTM Memory Model and Thread Specification 描述。感兴趣的可以参看下这份PDF文档(http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf)

Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。

而JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。

这里面提到的主内存和工作内存,读者可以简单的类比成计算机内存模型中的主存和缓存的概念。特别需要注意的是,主内存和工作内存与JVM内存结构中的Java堆、栈、方法区等并不是同一个层次的内存划分,无法直接类比。《深入理解Java虚拟机》中认为,如果一定要勉强对应起来的话,从变量、主内存、工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分。工作内存则对应于虚拟机栈中的部分区域。

所以,再来总结下,JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。

-----------------------------------------------java 内存模型--------------------------------------------

-----------------------------------------------java 内存模型的实现---------------------------------------

了解Java多线程的朋友都知道,在Java中提供了一系列和并发处理相关的关键字,比如volatile、synchronized、final、concurren包等。其实这些就是Java内存模型封装了底层的实现后提供给程序员使用的一些关键字。

在开发多线程的代码的时候,我们可以直接使用synchronized等关键字来控制并发,从来就不需要关心底层的编译器优化、缓存一致性等问题。所以,Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。

本文并不准备把所有的关键字逐一介绍其用法,因为关于各个关键字的用法,网上有很多资料。读者可以自行学习。本文还有一个重点要介绍的就是,我们前面提到,并发编程要解决原子性、有序性和一致性的问题,我们就再来看下,在Java中,分别使用什么方式来保证。

    -----原子性 在Java中,为了保证原子性,提供了两个高级的字节码指令monitorenter和monitorexit。在synchronized的实现原理文章中,介绍过,这两个字节码,在Java中对应的关键字就是synchronized。

因此,在Java中可以使用synchronized来保证方法和代码块内的操作是原子性的。

    -----可见性 Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存作为传递媒介的方式来实现的。

Java中的volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。

除了volatile,Java中的synchronized和final两个关键字也可以实现可见性。只不过实现方式不同,这里不再展开了。

    -----有序性 在Java中,可以使用synchronized和volatile来保证多线程之间操作的有序性。实现方式有所区别:

volatile关键字会禁止指令重排。synchronized关键字保证同一时刻只允许一条线程操作。

好了,这里简单的介绍完了Java并发编程中解决原子性、可见性以及有序性可以使用的关键字。读者可能发现了,好像synchronized关键字是万能的,他可以同时满足以上三种特性,这其实也是很多人滥用synchronized的原因。

但是synchronized是比较影响性能的,虽然编译器提供了很多锁优化技术,但是也不建议过度使用。

-----------------------------------------------java 内存模型的实现---------------------------------------

--------------------------------------------------一个大牛的博客---安排到学习计划----------------------------

一定要研究的博客主 https://blog.csdn.net/hollis_chuang/article/details/81062775

--------------------------------------------------一个大牛的博客---安排到学习计划----------------------------

-----------------------------分布式系统设计的2大原则-----------------------

A:通过复制来提高高可用性 B:使用CAP理论指导分布式系统设计

-----------------------------分布式系统设计的2大原则-----------------------

------------------------------------ioc 的依赖注入详解-----------------------------------

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

表面上是在一定程度上缓解了以上问题,但实质上这种代码耦合并没有改变。通过IoC模式可以彻底解决这种耦合,它把耦合从代码中移出去,放到统一的XML 文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中,这可能就是“依赖注入”说法的来源了。 IoC模式,系统中通过引入实现了IoC模式的IoC容器,即可由IoC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分离。其中一个特点就是通过文本的配置文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。 当前比较知名的IoC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等。 在上面的几个IoC容器中,轻量级的有Pico Container、Avalon、Spring、HiveMind等,超重量级的有EJB,而半轻半重的有容器有JBoss,Jdon等。 可以把IoC模式看作工厂模式的升华,把IoC容器看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的。利用Java 的“反射”编程,根据XML中给出的类定义生成相应的对象。从实现来看,以前在工厂模式里写死了的对象,IoC模式改为配置XML文件,这就把工厂和要生成的对象两者隔离,极大提高了灵活性和可维护性。 IoC中最基本的Java技术就是“反射”编程。通俗的说,反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让应用在运行时才动态决定生成哪一种对象。反射的应用是很广泛的,像Hibernate、Spring中都是用“反射”做为最基本的技术手段。 在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为什么反射技术没有普遍应用开来的原因。但经SUN改良优化后,反射方式生成对象和通常对象生成方式,速度已经相差不大了(但依然有一倍以上的差距)。 [1] 

------------------------------------ioc 的依赖注入详解-----------------------------------

关注
打赏
1638267374
查看更多评论
立即登录/注册

微信扫码登录

0.0416s