【JAVA】
JAVA特点
封装继承多态(也是面向对象的特点)
封装:
封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,
即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
继承:
继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。
对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),
而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更
适合特殊的需要。
多态性:
多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。
多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
重载和重写
重载(Overloading):
Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
重写(Overriding):
(1)父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
(2)若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
(3)子类函数的访问修饰权限不能少于父类的;
抽象类和接口的区别
1)抽象类表示的是一种继承关系,一个类只能使用一次继承关系。
但是,一个类却可以实现多个接口。2)在抽象类中可以有自己的数据成员,也可以有非抽象的成员方法,
而在接口中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在接口中一般不定义数据成员,接口中只能定义常量),所有的成员方法都是抽象的。3) 抽象类和接口所反映出的设计理念不同。其实抽象类表示的是"is-a"关系,接口表示的是"like-a"关系。
4)实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。
5)接口中定义的变量默认是public static final型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。
6)抽象类中的变量默认是friendly访问级别,其值可以在子类中重新定义,也可以重新赋值。
7)接口中的方法默认都是public, abstract类型的。
子类可以从父类中继承哪些
(1)继承public和protected修饰的属性和方法,不管子类和父类是否在同一个包里。
(2)继成默认权限修饰符修饰的属性和方法,但子类和父类必须在同一个包里。
(3)无法继承private修饰的属性和方法。
(4)无法继承父类的构造方法。
JAVA常用包
java.lang: 这个是系统的基础类,比如String等都是这里面的,这个包是唯一一个可以不用引入(import)就可以使用的包。
Math类中提供了三个与取整有关的方法:ceil、floor、round,这些方法的作用与它们的英文名称的含义相对应,例如,ceil的英文意义是天花板,该方法就表示向上取整,所以,Math.ceil(11.3)的结果为12,Math.ceil(-11.3)的结果是-11;floor的英文意义是地板,该方法就表示向下取整,所以,Math.floor(11.6)的结果为11,Math.floor(-11.6)的结果是-12;最难掌握的是round方法,它表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。
Math.random()java.io: 这里面是所有输入输出有关的类,比如文件操作等。
java.nio:为了完善io包中的功能,提高io包中性能而写的一个新包 ,例如NIO非堵 塞应用
java.net: 这里面是与网络有关的类,比如URL,URLConnection等。
java.util: 这个是系统辅助类,特别是集合类Collection,List,Map等。
java.sql: 这个是数据库操作的类,Connection, Statement,ResultSet等。
javax.servlet:这个是JSP,Servlet等使用到的类。
多线程
线程的创建方式
- 1.继承Thread类,重写run方法
public class ThreadDemo01 extends Thread{
public ThreadDemo01(){
//编写子类的构造方法,可缺省
}
public void run(){
//编写自己的线程代码
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args){
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("我是自定义的线程1");
threadDemo01.start();
System.out.println(Thread.currentThread().toString());
}
}
- 2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
public class ThreadDemo02 {
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());
Thread t1 = new Thread(new MyThread());
t1.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+"-->我是通过实现接口的线程实现方式!");
}
}
- 3.通过Callable和FutureTask创建可以获取返回值的线程
a:创建Callable接口的实现类 ,并实现Call方法
b:创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
c:使用FutureTask对象作为Thread对象的target创建并启动线程
d:调用FutureTask对象的get()来获取子线程执行结束的返回值
- 4.通过线程池创建线程
public class ThreadDemo05{
private static int POOL_NUM = 10; //线程池数量
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i = 0; i<POOL_NUM; i++)
{
RunnableThread thread = new RunnableThread();
//Thread.sleep(1000);
executorService.execute(thread);
}
//关闭线程池
executorService.shutdown();
}
}
class RunnableThread implements Runnable
{
@Override
public void run()
{
System.out.println("通过线程池方式创建的线程:" + Thread.currentThread().getName() + " ");
}
}
Sleep()和wait()
1、同步锁的对待不同:
sleep()后,程序并不会不释放同步锁。
wait()后,程序会释放同步锁。2、用法的不同:
sleep()可以用时间指定来使他自动醒过来。如果时间不到你只能调用interreput()来强行打断。
wait()可以用notify()直接唤起。3、属于不同的类:
sleep()的类是Thread。
wait()的类是Object。
锁
synchronized用法:
1.锁方法(成员锁)
方法声明时使用放在范围操作符(public等)之后,返回类型声明(void等)之前.这时,线程获得的是成员锁,即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.
public synchronized void synMethod() { //方法体 }
2.锁代码块(成员锁)
synchronized后跟括号,括号里是变量,这样,一次只有一个线程进入该代码块.此时,线程获得的是成员锁.
public int synMethod(int a1){ synchronized(a1) { //一次只能有一个线程进入 } }
3.锁对象(对象锁)
synchronized后面括号里是一对象,此时,线程获得的是对象锁.
public class MyThread implements Runnable { public static void main(String args[]) { MyThread mt = new MyThread(); Thread t1 = new Thread(mt, "t1"); Thread t2 = new Thread(mt, "t2"); t1.start(); t2.start(); } public void run() { synchronized (this) { System.out.println(Thread.currentThread().getName()); } } }
4.锁类(对象锁)
synchronized后面括号里是类,此时,线程获得的是对象锁.
死锁
产生死锁的原因:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
Socket
Socket服务之间的数据传输本质上都是IO流传输
TCP:
客户端:创建socket服务(Socket),并指定要连接的主机和端口。
服务端:1创建socket服务(ServerSocket)2获取连接过来的客户端对象accept方法(阻塞式),
3客户端如果发来数据,服务端要使用对应的客户端对象,并获取对象的读取流读取数据。
UDP:
DatagramSocket类用来发送和接收数据报包的套接字。无连接包投递。
NIO
为什么说NIO为啥是同步非阻塞?
因为无论多少客户端都可以接入服务端,客户端接入并不会耗费一个线程,只会创建一个连接然后注册到selector上去,这样你就可以去干其他你想干的其他事情了
一个selector线程不断的轮询所有的socket连接,发现有事件了就通知你,然后你就启动一个线程处理一个请求即可,这个过程的话就是非阻塞的。
但是这个处理的过程中,你还是要先读取数据,处理,再返回的,这是个同步的过程。
【数据结构】
基本数据类型与包装类型
基本数据类型包括byte、int、char、long、float、double、boolean和short。
java中Object不包含基本数据类型
在java中不能定义基本数据类型对象,为了能将基本数据类型视为对象处理,并且能够调用方法简化其使用过程,java为每个基本类型都提供了包装类。
.equals和==
简单理解:.equals比较的是值,==比较的是位置
比较的是内存中所存储的数值是否相同,如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如Objet obj = new Object();变量obj是一个内存,newObject()是另一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用操作符进行比较。
String
在Java中String是不可变的和无法改变的,有什么好处?
1,可以使用字符串池来存储字符串,提高存储效率。
2,增加安全性,在存储一些敏感信息,如数据库用户名,密码等是,黑客不能改变它的值。java的类加载器加载类时,字符串的不变性可以确保正确的类被加装。
3,由于String是不可变的,它是安全的,在多线程环境下,我们不需要任何同步。
Map
哈希表结构:结合数组结构和链表结构的优点,从而实现了查询和修改效率高,插入和删除效率也高的一种数据结构
常见的HashMap就是这样的一种数据结构
遍历map
HashMap中的put()和get()的实现原理:
1、map.put(k,v)实现原理
(1)首先将k,v封装到Node对象当中(节点)。
(2)然后它的底层会调用K的hashCode()方法得出hash值。
(3)通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。
2、map.get(k)实现原理
(1)先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。
(2)通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。如果这个位置上什么都没有,则返回null。如果这个位置上有单向链表,那么它就会拿着K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。
HashMap和Hashtable的区别
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。
- HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
- HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
- 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
- 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
- HashMap不能保证随着时间的推移Map中的元素次序是不变的。
HashMap扩容机制
初始容量:
当我们没有设置初始化容量时,HashMap就使用默认的初始化容量,也就是16.
当我们设置了初始化容量,HashMap就会按照我们设置的容量进行设置吗?答案是不一定。当你设置的初始化容量是2的n次方时,就会按照你设置的容量设置;当你设置的初始化容量不是2的n次方时,就会按照大于你设置的那个值但是最接近你设置的那个值的2的n次方进行设置。
扩容:
当我们的容量是16,加载因子是0.75时,当键值对的数量大于16*0.75=12时,HashMap会进行扩容操作,在容量不大于最大值的情况下,HashMap是以2倍的容量进行扩容的。
List 链表
链表结构:存储区间离散、占用内存宽松、空间复杂度小
- 优点:插入删除速度快,内存利用率高,没有固定大小,扩展灵活
- 缺点:不能随机查找,每次都是从第一个开始遍历(查询效率低)
ArrayList和LinkedList
- Linkedlist基于链表的动态数组,数据添加删除效率高,只需要改变指针指向即可,但是访问数据的平均效率低,需要对链表进行遍历。
- ArrayList的当前容足够大,add()操作向数组的尾部的效率非常高。但是当向指定位置添加数据时也还是比Linkedlist慢,后者添加数据只需要改变指针指向即可。Arraylist删除数组也需要移动数组,效率较慢。
Array 数组
数组结构: 存储区间连续、内存占用严重、空间复杂度大
- 优点:随机读取和修改效率高,原因是数组是连续的(随机访问性强,查找速度快)
- 缺点:插入和删除数据效率低,因插入数据,这个位置后面的数据在内存中都要往后移动,且大小固定不易动态扩展。
Queue 队列
队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。
LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。
import java.util.LinkedList;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
//add()和remove()方法在失败的时候会抛出异常(不推荐)
Queue<String> queue = new LinkedList<String>();
//添加元素
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.offer("e");
for(String q : queue){
System.out.println(q);
}
System.out.println("poll="+queue.poll()); //返回第一个元素,并在队列中删除
for(String q : queue){
System.out.println(q);
}
System.out.println("element="+queue.element()); //返回第一个元素
for(String q : queue){
System.out.println(q);
}
System.out.println("peek="+queue.peek()); //返回第一个元素
for(String q : queue){
System.out.println(q);
}
}
}
Stack 堆栈
堆栈:限定了只能从线性表的一端进行数据的存取,这一端称为栈顶,另一端为栈底。入栈操作是先向栈顶方向移动一位,存入数据;出栈操作则是取出栈顶数据,然后向栈底移动一位。
Object peek( )
查看堆栈顶部的对象,但不从堆栈中移除它。
Object pop( )
移除堆栈顶部的对象,并作为此函数的值返回该对象。
Object push(Object element)
把项压入堆栈顶部。
二叉树
二叉树:树中的每个节点最多有两个子节点。这两个子节点分别称为左子节点和右子节点。在建立二叉搜索树时,要求一个节点的左子节点的关键字值小于这个节点而右子节点的关键字值大于或等于这个节点。常见的遍历操作有前序、中序和后序遍历。
【框架】
JSP
九大内置对象
内置对象 | 类型 | 作用域 |
request | javax.servlet.http.HttpServletRequest | request |
response | javax.servlet.http.HttpServletResponse | response |
pageContext | javax.servlet.jsp.PageContext | page |
session | javax.servlet.http.HtpSession | session |
application | javax.servlet.jsp.ServletContext | application |
out | javax.servlet.jsp.JspWriter | page |
config | javax.servlet.ServletConfig | page |
page | java.lang.Object | page |
exception | java.lang.Throwable | page |
作用域
名称 | 作用域 |
application | 在所有应用程序中有效 |
session | 在当前会话中有效 |
request | 在当前请求中有效 |
page | 在当前页面有效 |
Servlet的生命周期:
Servlet的生命周期可以概括为以下几个阶段:
1)当客户端第一次请求Servlet时,Servlet被加载到内存中,容器会创建Servlet实例,并调用其init()方法进行初始化工作。
2)容器创建请求对象和响应对象,然后调用Servlet的service()方法为客户端提供服务。
3)当Servlet不再被需要时,容器调用Servlet的destory()方法将Servlet实例销毁。
SpringMVC
Spring MVC 提供了一种分离式的方法来开发 Web 应用。通过运用像 DispatcherServelet,MoudlAndView 和 ViewResolver 等一些简单的概念,开发 Web 应用将会变的非常简单。
原理
(1)客户端(浏览器)发送请求,直接请求到DispatcherServlet。
(2)DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。
(3)解析到对应的Handler后,开始由HandlerAdapter适配器处理。
(4)HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。
(5)处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。
(6)ViewResolver会根据逻辑View查找实际的View。
(7)DispaterServlet把返回的Model传给View。
(8)通过View返回给请求者(浏览器)
请求中文乱码问题
(1)解决post请求乱码问题:在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;
(2)修改tomcat配置文件添加编码与工程编码一致,或对参数进行重新编码:
SpringMvc拦截器:
有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可:
SpringBoot
Spring特点:ioc aop DI
Ioc:
所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
DI:
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
SpringBoot自动装配原理
- SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
- 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
- 以前我们需要自己配置的东西 , 自动配置类都帮我们解决了
- 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
- 它将所有需要导入的组件以全类名的方式返回 , 这些组件就会被添加到容器中 ;
- 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
Mybatis
Mybatis执行过程
通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。
Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,根据类的全限定名+方法名,唯一定位到一个MapperStatement并调用执行器执行所代表的sql,然后将sql执行结果返回。
Mybatis和Hibernate的区别
Hibernate:一个全自动的orm框架,对象关系映射能力比较强,不需要编写SQL就能对数据库进行操作。操作简单开发快,但是入门门槛比较高,对于SQL的修改和优化比较困难。比较适合需求变化不大的中小型项目:OA,ERP、管理系统等
MyBatis:是一种半自动的orm框架,专注于SQL本身,对SQL修改和优化比较简单,支持对象关系映射,需要是手动实现SQL。比较适合需求变化比较多的互联网项目。
#{}和${}的区别是什么?
${}是字符串替换,#{}是预处理;
Mybatis在处理{}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
使用#{}可以有效的防止SQL注入,提高系统安全性。
Redis
Redis数据类型
String 类型
1、set key value 给键key赋值;
2、get key 得到key的value值 ;
3、set key value [EX] [PX ] 给key设置有效时间其中[EX]表示秒,[PX]表示毫秒
4、set key [{},{},{},{}] 给key赋值多个值。
比如对于短信注册,可以使用 set tel passcode EX 60 ,表示号码tel的验证码passcode在60内有效,60后自动删除。
5、 del key 表示删除key
List类型
List和java中的List有点儿类似。他分为左右两头。
1、lpush key value:从左边进List
2、rpush key value:从右边进list
3、lpop key value:从左边出
4、rpop key value:从右边出
5、llen key 表示key的长度
6、lrange key start end 表示把key中从start到end范围内的value值取出来,这是一个双闭区间。
7、index key count 表示取出第几个value
8、lset key index value 表示把index 下标的值换成value。
9、lrem key count value 删除指定键的值.count 表示杀出几个。
Set类型(无序不能重复)
1、sadd key value 给key赋值
2、scard key 得到key中值的个数
3、sismember key value 是否存在vaue在key中
sort set 类型(不重复,有序)
1、zadd key value 给key赋值
2、zcount key min max 得到在min 到max key的成员个数
3、zscore key member 返回key中成员的分数
4、zrange key start stop 返回key中的索引在start到stop区间上的成员。例如: zrange s 0 3 分数最低三人
5、zrangebyscore key min max 通过分数返回有序集合指定区间内的成员 .例如: zrangebyscore s 1 4 limit 2 3 .分数在1-4上的成员。
6、zrem key 删除keyz中的成员
7、zrank key member 返回member在集合中位置(判断集合中是否存在)
8、zremrangebyscore key start stop 删除分数在start 到stop上的成员。
Hash 类型
1、hset key field value 将哈希表 key 中的字段 field 的值设为 value
2、hdel key field 删除 feild
3、hget key field 得到指定field 的值
4、hexists key field 查询field 是否存在。
Redis的穿透和雪崩
缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。
【SQL】
常用的基本的sql语句
**选择:**select * from table1 where 范围
**插入:**insert into table1(field1,field2) values(value1,value2)
删除:delete from table1 where 范围更新:update table1 set field1=value1 where 范围
查找:select * from table1 where field1 like ’%value1%’
排序:select * from table1 order by field1,field2 [desc]
总数:select count as totalcount from table1
求和:select sum(field1) as sumvalue from table1
平均:select avg(field1) as avgvalue from table1
最大:select max(field1) as maxvalue from table1
最小:select min(field1) as minvalue from table1
分组:SELECT column_name, function(column_name) FROM table_name WHERE column_name operator value GROUP BY column_name;
join 外连接
A、left (outer) join:
左外连接(左连接):结果集几包括连接表的匹配行,也包括左连接表的所有行。
SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
B:right (outer) join:
右外连接(右连接):结果集既包括连接表的匹配连接行,也包括右连接表的所有行。
C:full/cross (outer) join:
全外连接:不仅包括符号连接表的匹配行,还包括两个连接表中的所有记录。
数据库操作
创建数据库
CREATE DATABASE database-name
删除数据库
drop database dbname
表操作
创建新表
create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],…)
根据已有的表创建新表
**A:**create table tab_new like tab_old (使用旧表创建新表)
B:create table tab_new as select col1,col2… from tab_old definition only
删除新表
drop table tabname
增加列
Alter table tabname add column col type
注:列增加后将不能删除。DB2中列加上后数据类型也不能改变,唯一能改变的是增加varchar类型的长度。
添加主键
Alter table tabname add primary key(col)
删除主键
Alter table tabname drop primary key(col)
创建索引
create [unique] index idxname on tabname(col….)
删除索引
drop index idxname
注:索引是不可更改的,想更改必须删除重新建。
高级查询运算符
A:UNION 运算符
UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时(即 UNION ALL),不消除重复行。两种情况下,派生表的每一行不是来自 TABLE1 就是来自 TABLE2。
B:EXCEPT 运算符
EXCEPT运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL),不消除重复行。**
**C:INTERSECT 运算符
INTERSECT运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL随 INTERSECT 一起使用时 (INTERSECT ALL),不消除重复行。
**注:**使用运算词的几个查询结果行必须是一致的。
SQL优化
查询语句以及索引优化
查询语句
查询语句中不要使用*;
减少使用IN或者NOT IN,使用exists,not exists或者关联查询语句代替;
避免在where子句中对字段进行null值判断或使用 != 或 <> 操作符以及表达式,会导致不使用索引
尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些;
过滤掉最大数量记录的条件必须写在Where子句的末尾
索引
首先应考虑在where以及order by涉及的列上建立索引
索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
使用like关键字模糊查询时,% 放在前面索引不起作用,只有“%”不在第一个位置,索引才会生效(like ‘%文’–索引不起作用);
在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致;
索引的类型
普通索引
组合索引
数据库表结构的优化
尽可能减少定义字段宽度,尽量把字段设置NOTNULL
可根据情况建立中间表,分表