2018届秋季校招Java面试题总结(一)

2018届秋季校招考试题总结系列是在 本菜 面挂一万场后记下的面试题。记下以供闲暇学习之。

1. 简单描述一下Zookeeper

答:Zookeeper是一种为分布式应用所设计的高可用性、高性能且一致的开源协调服务,它提供一项基本服务:分布式锁服务。由于Zookeeper的开元特性,后来我们的开发者在分布式锁的基础上,摸索了出了其他的使用方法:配置维护、组服务、分布式消息队列、分布式通知/协调等。

    ZooKeeper性能上的特点决定了它能够用在大型的、分布式的系统当中。从可靠性方面来说,它并不会因为一个节点的错误而崩溃。除此之外,它严格的序列访问控制意味着复杂的控制原语可以应用在客户端上。ZooKeeper在一致性、可用性、容错性的保证,也是ZooKeeper的成功之处,它获得的一切成功都与它采用的协议——Zab协议是密不可分的。首先它设计一种新的数据结构——Znode,然后在该数据结构的基础上定义了一些关于该数据结构的一些操作。有了这些数据结构和原语还不够,因为我们的ZooKeeper是工作在一个分布式的环境下,我们的服务是通过消息以网络的形式发送给我们的分布式应用程序,所以还需要一个通知机制——Watcher机制。那么总结一下,ZooKeeper所提供的服务主要是通过:数据结构+原语+watcher机制,三个部分来实现的

    ZooKeeper的数据模型,在结构上和标准文件系统的非常相似,都是采用树形层次结构,ZooKeeper树中的每个节点被称为—Znode。每个Znode由3部分组成:
      ① stat:此为状态信息, 描述该Znode的版本, 权限等信息
      ② data:与该Znode关联的数据
      ③ children:该Znode下的子节点

    ZooKeeper虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以KB为大小单位。ZooKeeper的服务器和客户端都被设计为严格检查并限制每个Znode的数据大小至多1M,但常规使用中应该远小于此值。

    ZooKeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。
      ① 临时节点:该节点的生命周期依赖于创建它们的会话。
一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper的临时节点不允许拥有子节点。
      ② 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
    Watch触发器概述

    ZooKeeper可以为所有的读操作设置watch,这些读操作包括:exists()、getChildren()及getData()。watch事件是一次性的触发器,当watch的对象状态发生改变时,将会触发此对象上watch所对应的事件。watch事件将被异步地发送给客户端,并且ZooKeeper为watch机制提供了有序的一致性保证。理论上,客户端接收watch事件的时间要快于其看到watch对象状态变化的时间


2. Hashtable与Hashmap的区别
答:继承不同,hashtable继承自dictionary,hashmap继承自abstractmap。
Hashtable: 生成一个新的,空的hashtable,使用默认的capacity容量为11,factor增长因子为0.75, 其实就是因为这个put方法是synchronized的,所以可以保证其线程安全。
Hashmap:允许null key/value,线程不安全
另外,ConcurrentHashMap所谓线程安全是如果没有哈希冲突使用compareAndSwapObject方式新增节点,如果哈希冲突的时候锁住哈希冲突的节点,这样新增的节点是线程安全的,而 ConcurrentHashMap又不像hashtable那样整个put方法被锁定,所以性能比hashtable要好,因为这样不影响其他节点的插入和读取。(concurrenthashmap也不允许空键值,抛空指针异常,CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。)


3. arraylist linkedlist vector 区别

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

答:1. 对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
    2. 在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
    3. LinkedList不支持高效的随机元素访问。
    4. ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
可以这样说:当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。

    (遗留问题:在长度为10000的链表和数组中分别随机插入一个元素,哪个效率更高?【58到家面试题】答案是:数组。为啥?)


4. 线程五状态:新建、就绪、执行、阻塞、死亡

其中,阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
     1>线程通过调用sleep方法进入睡眠状态;
     2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
     3>线程试图得到一个锁,而该锁正被其他线程持有;
     4>线程在等待某个触发条件;
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态
    死亡状态(Dead)
有两个原因会导致线程死亡:
    1) run方法正常退出而自然死亡,
    2) 一个未捕获的异常终止了run方法而使线程猝死。


5. 线程安全性的五种类别
答:①. 不可变 -- 不可变的对象一定是线程安全的,并且永远也不需要额外的同步。Java 类库中大多数基本数值类如 Integer 、 String 和 BigInteger 都是不可变的。
    ②. 线程安全 -- 线程安全的对象,由类的规格说明所规定的约束在对象被多个线程访问时仍然有效,不管运行时环境如何排列,线程都不需要任何额外的同步。这种线程安全性保证是很严格的 -- 许多类,如 Hashtable 或者 Vector都不能满足这种严格的定义。
    ③. 有条件的线程安全 -- 有条件的线程安全类对于单独的操作可以是线程安全的,但是某些操作序列可能需要外部同步。条件线程安全的最常见的例子是遍历由 Hashtable 或者 Vector 或者返回的迭代器。
    ④. 线程兼容 -- 线程兼容类不是线程安全的,但是可以通过正确使用同步而在并发环境中安全地使用。这可能意味着用一个 synchronized 块包围每一个方法调用,或者创建一个包装器对象,其中每一个方法都是同步的(就像 Collections.synchronizedList() 一样)。许多常见的类是线程兼容的,如集合类 ArrayList 和 HashMap 、 java.text.SimpleDateFormat 、或者 JDBC 类 Connection 和 ResultSet。
    ⑤. 线程对立 -- 线程对立类是那些不管是否调用了外部同步都不能在并发使用时安全地呈现的类。线程对立很少见,当类修改静态数据,而静态数据会影响在其他线程中执行的其他类的行为,这时通常会出现线程对立。线程对立类的一个例子是调用 System.setOut() 的类。

6. 简单理解线程池技术
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
    
    假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
    如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
    一个线程池包括以下四个基本组成部分:
    1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
    2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
    3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
    4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。 
    线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
    线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目
,看一个例子:
    假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。
代码实现中并没有实现任务接口,而是把Runnable对象加入到线程池管理器(ThreadPool),然后剩下的事情就由线程池管理器(ThreadPool)来完成了。
Java通过Executors提供四种线程池,分别为:
    newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
    newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

// 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
    public void execute(Runnable task) {  
        synchronized (taskQueue) {  
            taskQueue.add(task);  
            taskQueue.notify();  
        }  
    }  
    // 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
    public void execute(Runnable[] task) {  
        synchronized (taskQueue) {  
            for (Runnable t : task)  
                taskQueue.add(t);  
            taskQueue.notify();  
        }  
    }  
    // 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
    public void execute(List<Runnable> task) {  
        synchronized (taskQueue) {  
            for (Runnable t : task)  
                taskQueue.add(t);  
            taskQueue.notify();  
        }  
    }  


7. 介绍几个MySQL存储引擎
答:MySQL将数据用各种不同的技术存储在文件中,这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。这些不同的技术以及配套的相关功能在 mysql中被称作存储引擎(也称作表类型)
  1. MyISAM引擎:MyISAM引擎是MySQL默认的存储引擎,MyISAM不支持事务和行级锁,所以MyISAM引擎速度很快,性能优秀MyISAM可以对整张表加锁,支持并发插入,支持全文索引
缺点:不支持事务和行级锁,也不支持外键
优点:访问速度快,对事务的完整性没有要求或者以select、insert为主的应用基本上都可以使用这个引擎来创建

  MyISAM再磁盘上存储成三个文件,其文件名都和表名相同,但扩展名分别是:.frm(存储表定义),.MYD(存储数据),.MYI(存储索引)
 2. InnoDB引擎:InnoDB是专为事务设计的存储引擎,支持事务,支持外键,拥有高并发处理能力。但是,InnoDB在创建索引和加载数据时,比MyISAM慢。-->InnoDB有两种模式的行锁:(面试就问的这里,GG了,如何对表中的一条记录加锁?)
1)共享锁:允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
    ( Select * from table_name where ......lock in share mode)
2)排他锁:允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和  排他写锁。(select * from table_name where.....for update)

 3. Memory引擎(采用哈希索引):内存表,Memory引擎将数据存储在内存中,表结构不是存储在内存中的,查询时不需要执行磁盘I/O操作,所以要比MyISAM和InnoDB快很多倍,但是数据库断电或是重启后,表中的数据将会丢失,表结构不会丢失.


8. 简单的介绍索引

答:索引是存储引擎用于快速找到记录的一种数据结构。索引本身很大,不可能全部存储在内存中,因此索引以索引表的形式存储在磁盘中
 索引是在存储引擎中实现的,而不是在服务器层中实现的。所以,每种存储引擎的索引都不一定完全相同,并不是所有的存储引擎都支持所有的索引类型
    (滴滴、宜信、广联达面试GG了,B+、B-区别?)

{★ B-树索引:每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历B-Tree通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同,很适合查找范围数据
   ★ B+树索引:并不能找到一个给定健值的具体行,B+树索引只能找到被查找数据行所在的页,然后从数据库将页读入内存,在内存中查找B+树索引可以分为聚集索引和辅助索引。聚簇索引是按照数据存放的逻辑地址为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。

    ○ 聚集索引 :聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序。 聚集索引对于那些经常要搜索范围值的列特别有效。使用聚集索引找到包含第一个值的行后,便可以确保包含后续索引值的行在物理相邻。 
    InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有主键),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。 
    ○ 辅助索引:叶结点的data域存放的是对应记录的主键的key。 对于建立辅助索引的表需要先根据辅助索引找到相应的主键,再根据主键在聚集索引中找到相应的记录集。
    ○ 非聚集索引 
    非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序。主键索引中,叶节点的data域存放的是数据记录的地址,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。(MYISAM采用此种索引方式)。
区别:
聚集索引表里数据物理存储顺序和主键索引的顺序一致,所以如果新增数据是离散的,会导致数据块趋于离散,而不是趋于顺序。而非聚集索引表数据写入的顺序是按写入时间顺序存储的。聚簇索引索引的叶节点就是数据节点;而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
适用情景
★ Hash索引:哈希索引基于哈希表实现,只有精确索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码,哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希存储在索引中,同时在哈希表中保存指向每个数据的指针。
哈希索引中存储的是:哈希值+数据行指针 
MySQL中,只有Memory存储引擎显示支持hash索引,是Memory表的默认索引类型,尽管Memory表也可以使用B-Tree索引。Memory存储引擎支持非唯一hash索引,这在数据库领域是罕见的,如果多个值有相同的hash code,索引把它们的行指针用链表保存到同一个hash表项中。
    索引有如下优点与缺点:
★ 优点
1.可以通过建立唯一索引或者主键索引,保证数据库表中每一行数据的唯一性
2.建立索引可以大大提高检索的数据,以及减少表的检索行数
3.在表连接的连接条件,可以加速表与表直接的相连
4.在分组和排序字句进行数据检索,可以减少查询时间中分组和 排序时所消耗的时间(数据库的记录会重新排序)
5.建立索引,在查询中使用索引,可以提高性能
--索引大大减小了服务器需要扫描的数据量
--索引可以帮助服务器避免排序和临时表
--索引可以将随机IO变成顺序IO
△ 缺点
1.创建索引和维护索引会耗费时间,随着数据量的增加而增加
2.索引文件会占用物理空间,除了数据表需要占用物理空间之外,每一个索引还会占用一定的物理空间
3.当对表的数据进行 INSERT,UPDATE,DELETE 的时候,索引也要动态的维护,这样就会降低数据的维护速度,(建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快)。
索引的使用:最好的做法是创建表的时候创建索引,如果创建表之后再修改新建索引的话,对于聚集索引,会根据原来的表,创建一个新的表带有索引数据结构,再把原来的表删去,新创建的表改成原来的表的名字。而非聚集索引则是通过修改索引文件来完成。所以都是需要占用额外的资源来修改或新建索引的。


9. 说一下synchronized原理
答:①.synchronized的字节码表示: 
      在java语言中存在两种内建的synchronized语法:1、synchronized语句;2、synchronized方法。对于synchronized语句当Java源代码被javac编译成bytecode的时候,会在同步块的入口位置和退出位置分别插入monitorenter和monitorexit字节码指令。而synchronized方法则会被翻译成普通的方法调用和返回指令如:invokevirtual、areturn指令,在VM字节码层面并没有任何特别的指令来实现被synchronized修饰的方法,而是在Class文件的方法表中将该方法的access_flags字段中的synchronized标志位置1,表示该方法是同步方法并使用调用该方法的对象或该方法所属的Class在JVM的内部对象表示Class做为锁对象
    ②.JVM中锁的优化:
      简单来说在JVM中monitorenter和monitorexit字节码依赖于底层的操作系统的Mutex Lock来实现的,但是由于使用Mutex Lock需要将当前线程挂起并从用户态切换到内核态来执行,这种切换的代价是非常昂贵的;然而在现实中的大部分情况下,同步方法是运行在单线程环境(无锁竞争环境)如果每次都调用Mutex Lock那么将严重的影响程序的性能。不过在jdk1.6中对锁的实现引入了大量的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销。
    ③.在JVM中创建对象时会在对象前面加上两个字大小的对象头(Object header),在32位机器上一个字为32bit,根据不同的状态位Mark World中存放不同的内容,在轻量级锁中,Mark Word被分成两部分,刚开始时LockWord为被设置为HashCode、最低三位表示LockWord所处的状态,初始状态为001表示无锁状态。
    ④.Monitor Record是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表;那么这些monitor record有什么用呢?每一个被锁住的对象都会和一个monitor record关联(对象头中的LockWord指向monitor record的起始地址,由于这个地址是8byte对齐的所以LockWord的最低三位可以用来作为状态),同时monitor record中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。
    ⑤.获取锁(monitorenter)的大概过程如下:
      当对象处于无锁状态时(RecordWord值为HashCode,状态位为001),线程首先从自己的可用moniter record列表中取得一个空闲的moniter record,初始Nest和Owner值分别被预先设置为1和该线程自己的标识,一旦monitor record准备好然后我们通过CAS原子指令安装该monitor record的起始地址到对象头的LockWord字段来膨胀.
      对象已经被膨胀同时Owner中保存的线程标识为获取锁的线程自己,这就是重入(reentrant)锁的情况,只需要简单的将Nest加1即可。不需要任何原子操作,效率非常高
      对象已膨胀但Owner的值为NULL,当一个锁上存在阻塞或等待的线程同时锁的前一个拥有者刚释放锁时会出现这种状态,此时多个线程通过CAS原子指令在多线程竞争状态下试图将Owner设置为自己的标识来获得锁,竞争失败的线程在则会进入到第四种情况的执行路径。
      对象处于膨胀状态同时Owner不为NULL(被锁住),在调用操作系统的重量级的互斥锁之前先自旋一定的次数,当达到一定的次数时如果仍然没有成功获得锁,则开始准备进入阻塞状态,首先将rfThis的值原子性的加1,由于在加1的过程中可能会被其他线程破坏Object和monitor record之间的关联,所以在原子性加1后需要再进行一次比较以确保LockWord的值没有被改变,当发现被改变后则要重新进行monitorenter过程。同时再一次观察Owner是否为NULL,如果是则调用CAS参与竞争锁,锁竞争失败则进入到阻塞状态。
    ⑥.释放锁(monitorexit)的大概过程如下:
      首先检查该对象是否处于膨胀状态并且该线程是这个锁的拥有者,如果发现不对则抛出异常;
      检查Nest字段是否大于1,如果大于1则简单的将Nest减1并继续拥有锁,如果等于1,则进入到下一步;
      检查rfThis是否大于0,设置Owner为NULL然后唤醒一个正在阻塞或等待的线程再一次试图获取锁,如果等于0则进入到下一步;
      缩小(deflate)一个对象,通过将对象的LockWord置换回原来的HashCode值来解除和monitor record之间的关联来释放锁,同时将monitor record放回到线程是有的可用monitor record列表。


10. 100w个数中找到最大的100个数。 
解决方式是,用一个100个容量的最小堆, 这100个数总数目前已知的最大的100个,而且堆顶是小的, 在继续遍历时候,只和堆定比较, 如果这个数,比堆顶的大, 就把这个数加入这个堆。当遍历完成后,这个最小堆中的数,就是最大的100个数了。


11. 二叉排序树的性质: 按中序遍历二叉排序树,所得到的中序遍历序列是一个递增有序序列。


12. 什么是事务?有哪些属性?以及属性的意义。
答:事务是并发操作的单位,是用户定义的一个操作序列。
    事务的四个特性:原子性(事务必须是一个不可分割的整体)、一致性(执行完数据库操作后,数据不会被破坏,A给B充Q币,A充了之后B能看见自己的Q币多了)、隔离性(写写之间隔离)、持久性(数据库必须要保证有一条数据永久地存放在磁盘)。

    详细的关于事务的学习,推荐看这篇文章:http://blog.csdn.net/u014079773/article/details/52808193


13. SQL语句常用优化技巧
答:要提高SQL语句的执行效率,最常见的方法就是建立索引,以及尽量避免全表扫描。一个简单的优化,也许能让你的SQL执行效率提高几倍,甚至几十倍。
①.避免在where子句中使用 is null 或 is not null 对字段进行判断。
如:select id from table where name is null
在这个查询中,就算我们为 name 字段设置了索引,查询分析器也不会使用,因此查询效率底下。为了避免这样的查询,在数据库设计的时候,尽量将可能会出现 null 值的字段设置默认值,这里如果我们将 name 字段的默认值设置为0,那么我们就可以这样查询:
select id from table where name = 0
②.避免在 where 子句中使用 != 或 <> 操作符。
如:select name from table where id <> 0
数据库在查询时,对 != 或 <> 操作符不会使用索引,而对于 < 、 <= 、 = 、 > 、 >= 、 BETWEEN AND,数据库才会使用索引。因此对于上面的查询,正确写法应该是:
select name from table where id < 0
union all
select name from table where id > 0
这里我们为什么没有使用 or 来链接 where 后的两个条件呢?这就是我们下面要说的第3个优化技巧。
③.避免在 where 子句中使用 or来链接条件。
如:select id from tabel where name = 'UncleToo' or name = 'PHP'
这种情况,我们可以这样写:
select id from tabel where name = 'UncleToo'
union all
select id from tabel where name = 'PHP'
④.少用 in 或 not in。
虽然对于 in 的条件会使用索引,不会全表扫描,但是在某些特定的情况,使用其他方法也许效果更好。如:
select name from tabel where id in(1,2,3,4,5)
像这种连续的数值,我们可以使用 BETWEEN AND,如:
select name from tabel where id between 1 and 5
⑤.注意 like 中通配符的使用
下面的语句会导致全表扫描,尽量少用。如:
select id from tabel where name like'%UncleToo%'
或者
select id from tabel where name like'%UncleToo'
而下面的语句执行效率要快的多,因为它使用了索引:
select id from tabel where name like'UncleToo%'
⑥.避免在 where 子句中对字段进行表达式操作。
如:select name from table where id/2 = 100
正确的写法应该是:
select name from table where id = 100*2
⑦.避免在 where 子句中对字段进行函数操作。
如:select id from table where substring(name,1,8) = 'UncleToo'
或 select id from table where datediff(day,datefield,'2014-07-17') >= 0
这两条语句中都对字段进行了函数处理,这样就是的查询分析器放弃了索引的使用。正确的写法是这样的:
select id from table where name like'UncleToo%'
或 select id from table where datefield <= '2014-07-17'
也就是说,不要在 where 子句中的 = 左边进行函数、算术运算或其他表达式运算。
⑧.在子查询中,用 exists 代替 in 是一个好的选择。
如:select name from a where id in(select id from b) 
如果我们将这条语句换成下面的写法:select name from a where id exists(select 1 from b where id = a.id)
这样,查询出来的结果一样,但是下面这条语句查询的速度要快的多。
⑨.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
⑩.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
     如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
     尽量避免大事务操作,提高系统并发能力。


14. 简要说NoSQL 数据库的分布式模型
答:分片 -- 特点:数据的各个部分存放在集群的不同服务器中;比如按字母来划分:以a-g 开头的键值都存放到第一台,服务器上,以h-n 开头的存放到第二台...
    需要考虑的问题 -- 如何存放数据,让用户基本上只需从一台服务器上获取数据?如果经常需要与多个结点交互才能取到需要数据,可能分片策略不合适,或者该场景中分片不是一个理想的方案;
    数据节点的分布 -- 地理位置与访问用户的关系  数据结点分布在全球各地,让北京的用户只需要访问北京的结点就能取到所需数据;
    保持负载均衡 -- 优点:同时提升读取和写入性能、由于分片是将数据分散到多个结点存储,这样在写入时,压力同样分散;横向扩展写入能力   缺点:降低数据库的错误恢复能力,分片后,集群中结点的故障将导致部分数据丢失;

    解决方案 -- 每个分片数据不只存放在一个结点上,冗余存放,增加数据安全性(通过后面讲到的与主从复制的结合使用,是常用的手段)
    主从复制 -- 特点:主节点存放权威数据,负责数据更新操作;主节点将更新的数据复制到从节点;优点:有助于提升数据读取性能,从结点只负责查询,增加从结点提升数据读取性能,增强“读取操作的故障恢复能力”,主节点损坏,依然可处理读取请求;从结点升级为主结点后可以处理更新请求;
    “一拖一” 即时备份的单存储方案 -- 即使不需要分布式部署,主从复制也可以用来做为单机服务器备份的部署方案;缺点:数据的不一致性(未及时更新),主节点更新后,同步到各个从结点的数据不能保证及时,可能导致各个结点上查询的数据不一致(只具有最终一致性)、对提升写入操作性能帮助不大、所有的更新操作都通过主结点处理,对于更新频繁的业务,使用主从复制模型优势不大;
    主节点是系统的瓶颈和弱点 

    对等复制 -- 特点:所有节点地位相同,都可接收查询和写入请求;各节点将自己的更新的数据复制到其他节点;优点:从容处理出错节点,不必担心数据请求的丢失增加节点,轻易提升查询和写入性能;缺点:数据不一致性、写入和读取都有可能发生冲突;
    结合使用 -- 分片和主从复制中的一拖一方案结合使用、分片的作用在于数据的分布式存储;主从复制的作用在于为各个分片结点提供备份,增加数据安全;


15. Java 集合部分都有哪些接口,主要体现了哪些设计模式?
答:Java 集合部分主要有Collection、List、Set、Map、Comparator、Iterator 等,主要体现的设计模式是策略模式迭代模式
策略模式
主要体现在每个接口有不同的实现,可以完成互换,如List 接口下有ArrayList 和LinkedList,在不同的场景下可以互换。
迭代模式主要体现在Iterator 的实现,为不同的数据存储方式(数组、链表、散列表等)提供了统一的访问方式。
Comparator 体现的设计模式是什么? -- 策略模式,即不改变对象自身,而使用一个策略对象去改变它的行为
注:策略模式的优缺点是什么:
优点:(1)将具体算法逻辑与客户类分离,(2)避免了大量的if else 判断
缺点:(1)每个算法一个类,产生了太多的类,(2)客户端要知道所有的策略类,以便决定使用哪一个。

猜你喜欢

转载自blog.csdn.net/Juwenzhe_HEBUT/article/details/78337028