笔记整理2020-08-14

笔记

Java基础部分

Java跨平台原理

java提供了对应不同操作系统,不同位数的java虚拟机(JVM),屏蔽java程序在各种不同的硬件和操作系统中对内存的访问的差异,而保证对外统一的接口(java Api)。这样对于开发人员来说,只要按照JavaApi开发即可。如果需要部署到不同环境,只需要在该环境上安装对应版本的虚拟机即可。

Java内存模型

  1. PC寄存器/程序计数器:
    每个线程私有,用于记录当前线程执行到哪一步,意外中断后用于恢复。
  2. Java栈 Java Stack:
    Java栈总是与线程关联在一起的,每当创建一个线程,JVM就会为该线程创建对应的Java栈,在这个Java栈中又会包含多个栈帧(Stack Frame),这些栈帧是与每个方法关联起来的,每运行一个方法就创建一个栈帧,每个栈帧会含有一些局部变量、操作栈和方法返回值等信息。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向该地址。只有这个活动的栈帧的本地变量可以被操作栈使用,当在这个栈帧中调用另外一个方法时,与之对应的一个新的栈帧被创建,这个新创建的栈帧被放到Java栈的栈顶,变为当前的活动栈。同样现在只有这个栈的本地变量才能被使用,当这个栈帧中所有指令都完成时,这个栈帧被移除Java栈,刚才的那个栈帧变为活动栈帧,前面栈帧的返回值变为这个栈帧的操作栈的一个操作数。由于Java栈是与线程对应起来的,Java栈数据不是线程共有的,所以不需要关心其数据一致性,也不会存在同步锁的问题。在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。在Hot Spot虚拟机中,可以使用-Xss参数来设置栈的大小。栈的大小直接决定了函数调用的可达深度。笔记整理2020-08-14_第1张图片
  3. 堆 Heap:
    堆是JVM所管理的内存中最大的一块,是被所有Java线程锁共享的,不是线程安全的,在JVM启动时创建。堆是存储Java对象的地方,这一点Java虚拟机规范中描述是:所有的对象实例以及数组都要在堆上分配。Java堆是GC管理的主要区域。
  4. 方法区Method Area:
    方法区存放了要加载的类的信息(名称、修饰符等)、类中的静态常量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当在程序中通过Class对象的getName.isInterface等方法来获取信息时,这些数据都来源于方法区。方法区是被Java线程锁共享的,不像Java堆中其他部分一样会频繁被GC回收,它存储的信息相对比较稳定,在一定条件下会被GC,当方法区要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。方法区也是堆中的一部分,就是我们通常所说的Java堆中的永久区 Permanet Generation,大小可以通过参数来设置,可以通过-XX:PermSize指定初始值,-XX:MaxPermSize指定最大值。
  5. 常量池Constant Pool:
    常量池本身是方法区中的一个数据结构。常量池中存储了如字符串、final变量值、类名和方法名常量。常量池在编译期间就被确定,并保存在已编译的.class文件中。一般分为两类:字面量和应用量。字面量就是字符串、final变量等。类名和方法名属于引用量。引用量最常见的是在调用方法的时候,根据方法名找到方法的引用,并以此定为到函数体进行函数代码的执行。引用量包含:类和接口的权限定名、字段的名称和描述符,方法的名称和描述符。
  6. 本地方法栈Native Method Stack:
    本地方法栈和Java栈所发挥的作用非常相似,区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行Native方法服务。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

Java的三(四)大特性

  • 抽象:把一类事物的特性(属性)和行为(方法)进行归纳,抽取出来。
  • 封装:抽象后把特征和行为写到一个类中,这就是封装。封装是为了保证安全性(private),外部程序要调用封装好的对象属性,就需要通过指定的公开方法来获取。
  • 继承:优点是减少冗余的代码,提高代码复用性。缺点就是提高了代码的耦合度
    • 子类拥有父类非private的属性,方法。
    • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
    • 子类可以用自己的方式实现父类的方法(重写方法)。
    • 只能单继承,但是可以多重继承。
  • 多态:满足这三个条件 1.有继承 2. 有重写 3. 要有父类引用指向子类对象(向上转型)。同一个行为具有多个不同表现形式或形态的能力
    1. 父类引用可以指向子类对象,子类引用不能指向父类对象。
    2. 把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转型。如Father father = new Son();
    3. 把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型。如father就是一个指向子类对象的父类引用,把father赋给子类引用son 即Son son =(Son)father;其中father前面的(Son)必须添加,进行强制转换。
    4. 向上转型会丢失子类特有的方法,但是对于子类重写的方法,会取子类重写后的。
    5. 向上转型的作用,减少重复代码,父类为参数,调用时用子类作为参数,就是利用了向上转型。这样使代码变得简洁。体现了JAVA的抽象编程思想。
    6. 变量不能被重写(覆盖),”重写“的概念只针对方法,如果在子类中”重写“了父类中的变量,那么在编译时会报错。
    • 方法的重写:发生在子类继承父类的关系中,父类中的方法被子类继承,方法名,返回值类型,参数完全一样,但是方法体不一样那么说明父类中的该方法被子类重写了。重写的方法,访问修饰符的范围不能比原方法小。
    • 方法的重载:参数列表的不同(参数个数,类型,顺序的不同),可以构成方法重载。

Java的数据类型(参数传递方式)

基本数据类型:值传递
引用数据类型:地址传递
笔记整理2020-08-14_第2张图片
下图单位是bit,非字节 1B=8bit
笔记整理2020-08-14_第3张图片
笔记整理2020-08-14_第4张图片
String和基本数据类型的包装类型(比如Integer)直接赋值和new对象的区别:

  • 直接赋值:会先去常量池取,若常量池有,则直接引用,假如两次值是一样的,则==和equals都为true,因为直接指向同一个常量对象;如果没有则会创建对象(在堆空间),此时即使两次值是一样的,但是却创建了两个对象,此时equals为true,但==为false。
  • new对象:不管常量池有没有,直接创建对象。

访问修饰符

  • Protected:
    特别说明:
    其不同包的子类可以调用;但是不同包的其他类,就算创建了其子类对象,也无法直接调用。但是可以在子类中写方法,通过子类来调用。
    其实就是:被Protected修饰的成员,在子类中相当于子类的私有成员
    笔记整理2020-08-14_第5张图片

String和Stringbuffer, StringBuilder。

Stringbuffer和StringBuilder类的对象可以被多次修改,而不会创建新的对象(节省内存)。而String中的对象是不可变的,因此可以理解为线程安全,但是每次修改都会创建新对象,对于内存紧张的情况并不适用。

  1. String(字符串常量):final修饰,不可被继承。
  2. Stringbuffer(字符串变量):final修饰,不可被继承。线程安全,方法基本都加了同步锁,效率低。
  3. StringBuilder(字符串变量):final修饰,不可被继承。线程不安全,效率高。

Final

final关键字可以理解为终态。

  1. final修饰类
      final修饰的类不允许被继承。一个类不能既是final的,又是abstract的。因为abstract的主要目的是定义一种约定,让子类去实现这种约定,而final表示该类不能被继承,两者矛盾。
  2. final修饰方法
      final修饰方法,表示该方法不能被子类中的方法覆写Override。
  3. final修饰变量
      final修饰的类属性和变量属性必须要进行显示初始化赋值。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。final成员变量表示常量,只能被赋值一次,赋值后值(地址指向)不再改变。
  4. 当函数的参数类型声明为final时,说明该参数是只读型的。传入的参数,如果是基本数据类型(值传递),就一直是这个值,不会改变;如果是引用数据类型(地址传递),就一直是这个地址,不会改变。这样使用可以避免误操作更改这个固定值。

static

  1. 成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据。
  2. 静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据,不会随着对象的回收而销毁。
  3. 静态方法中不可以使用this或者super关键字。

垃圾回收机制(GC)

  • GC是垃圾收集的意思(Gabage Collection),Java提供的GC功能可以自动监测对象是否达到回收标准。
  • 因为垃圾回收器通常是作为一个单独的低级别的线程运行。
    • 会在cpu空闲的时候自动进行回收
    • 在堆内存存储满了之后
    • 主动调用System.gc()后尝试进行回收(不一定调用后马上会回收)
  • 强引用:就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类的引用。垃圾收集器永远不会回收掉被强引用的对象
  • 软引用:用来描述一些还有用,但并非必需的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中并进行第二次回收。如果这次回收还是没有足够的内存,会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。
  • 弱引用:也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。
  • 虚引用:也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用

抽象类、抽象方法、接口、枚举

  • abstract 关键字,和哪些关键字不能共存。
    • final:被final修饰的类不能有子类(不能被继承)。而被abstract修饰的类一定是一个父类(一定要被继承)。
    • private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。 而抽象方法出现的就是需要被复写。
    • static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。
  1. 抽象类:
    添加abstract修饰;
    如果一个类有抽象方法,那么这个类必须定义为抽象类;
    抽象类可以有抽象方法也可以有具体实现的方法;
    抽象类不能被实例化,只能被继承;
    继承一个抽象类,必须重写其中的抽象方法,否则也会被抽象化。
  2. 抽象方法:
    没有方法体{},必须使用abstract关键字来修饰;
    不能直接调用,只能被重写才能使用。
  3. 接口:
    接口可以理解为一种特殊的类(100%抽象类),里面全部是由全局常量和公共的抽象方法所组成。
    接口是解决Java无法使用多继承的一种手段
    接口中存在的变量一定是final,public,static的,这样可以更明确的限定这类事务的固有特性,不会发生改变。
  4. 枚举(enum):
    枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。
    公用的静态常量(public static final);
    jdk1.6以后switch语句开始支持枚举;
    笔记整理2020-08-14_第6张图片

集合

  • 常用集合
    笔记整理2020-08-14_第7张图片
    注意:红黑树就是平衡二叉树。
  • JDK1.8HashMap的更改
    • 数组+链表改成了数组+链表或红黑树
    • 链表的插入方式从头插法改成了尾插法
    • 扩容的时候1.7需要对原数组中的元素进行重新hash定位在新数组的位置,1.8采用更简单的判断逻辑,位置不变或索引+旧容量大小
    • 在插入时,1.7先判断是否需要扩容,再插入,1.8先进行插入,插入完成再判断是否需要扩容;
  • 要在遍历的时候删除元素,使用Iterator(遍历器);要在遍历的时候添加元素,使用listIterator.add(),这是专门针对List集合遍历时的添加操作。
  • 集合排序(Comparable与Comparator)
    • Comparable:内部排序,实现该接口的类可自定义排序规则。
    • Comparator:外部排序,实现该接口可以定义一个针对某个类的排序方式。

反射

定义: 在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。

线程

  • ThreadLocal
  • wait和sleep的区别
    • sleep()是Thread类中的方法,而wait()则是Object类中的方法。
    • sleep()方法导致了程序暂停,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
    • wait()方法会导致线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

Java高级部分

事务的隔离级别

以下级别从低到高

  1. Read Uncommitted(读未提交,会出现脏读)
  2. Read Committed(读已提交,大部分数据库默认的级别,会出现不可重复度)
  3. Repeatable Read(可重复读,mysql的默认级别,会出现幻读)
  4. Serializable(所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别,但并发量下降,吞吐量上不去。通常情况下,我们会使用其他隔离级别加上相应的并发锁的机制来控制对数据的访问,这样既保证了系统性能不会损失太大,也能够一定程度上保证数据的一致性)。
  • 脏读:A读取到B未提交的数据
  • 不可重复读:针对B提交(更新)前后,读取数据本身的对比
  • 幻读:针对B提交(插入/删除)前后,读取数据条数的对比

常用设计模式

  1. 单例模式:将构造器私有化,由该单例类自己创建这个实例,对外提供一个获取接口
  2. 工厂模式:抽取一类事物的行为到一个接口,这类事物的具体类型分别一一实现这个接口。创建一个这类事物的工厂类,告诉工厂类需要什么对象即可,工厂类来创建具体对象
    • 优点:将创建对象的责任明确划分,使用者无需知道细节,只需要关注怎么使用就行,创建对象由工厂负责。一定程度上降低了耦合度。
    • 缺点:由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。如果要添加产品,就不得不修改工厂逻辑。
  • 例子:
    • Mybatis的SqlSessionFactory
    • 将水果类抽象成一个接口,苹果香蕉类实现此接口。创建一个水果工厂,对外提供一个获取具体水果对象的接口,通过传入参数来创建一个苹果或者香蕉的实例对象并返回。
  1. 委派模式:
    • 链接: https://blog.csdn.net/qq_37141773/article/details/100544402.
  2. 代理模式:被代理类只需要关注当前自己的行为,至于行为前后的操作,交给代理类去完成
    • 例子:spring的事务管理就是代理模式
    • 静态代理:在使用静态代理时,被代理对象与代理对象需要一起实现相同的接口或者是继承相同父类,因此要定义一个接口或抽象类。
      • 优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.
      • 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
    • 动态代理
      • JDK代理:反射原理,代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能使用JDK代理,因此这也算是这种方式的缺陷。
      • Cglib代理:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,效率高于JDK代理。
        • 区别:
          • JDK动态代理是代理对象要实现接口,Cglib是继承了被代理对象。
          • JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
          • JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
/**
 * JDK动态代理
**/
//创建一个接口
interface Subject {
    void hello(String param);
}

//实现接口
class SubjectImpl implements Subject {
    @Override
    public void hello(String param) {
        System.out.println("hello  " + param);
    }
}

//创建SubjectImpl的代理类
class SubjectProxy implements InvocationHandler {
    private Subject subject;

    public SubjectProxy(Subject subject) {
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("--------------begin-------------");
        Object invoke = method.invoke(subject, args);
        System.out.println("--------------end-------------");
        return invoke;
    }
}

//通过反射来调用方法
class Main {
	public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler subjectProxy = new SubjectProxy(subject);
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(subjectProxy.getClass().getClassLoader(), subject.getClass().getInterfaces(), subjectProxy);
        proxyInstance.hello("world");
    }
}

输出:

--------------begin-------------
hello world
--------------end-------------
笔记整理2020-08-14_第8张图片
笔记整理2020-08-14_第9张图片

Spring全家桶

Spring :

1. 是一个分层的一站式轻量级框架;
2. spring属于低侵入式设计,代码的污染极低;
3. spring的DI(依赖注入)机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
4. Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。
5. spring对于主流的应用框架提供了集成支持。
- 涉及到的注解:
	- 声明bean的注解
		- @Controller 在展现层使用,控制器的声明(控制层)
		- @Service 在业务逻辑层使用(service层)
		- @Repository 在数据访问层使用(dao层)
		- @Component 组件,没有明确的角色
	- 注入bean的注解(一般用在属性上,也可以用在set方法上)
		- @Autowired:由Spring提供
		- @Resource:由JSR-250提供
	- java配置类相关注解
		- @Bean 注解在方法上,声明当前方法的返回值为一个bean(方法上)
		- @Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)
		- @ComponentScan 用于对Component(组件)进行扫描(类上)
		- @WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解
	- 切面(AOP)相关注解(通知注解见下文)
		- @Aspect 声明一个切面(类上)
		- @PointCut 声明切点
	- SpringMVC注解
		- @RequestMapping 用于映射Web请求,包括访问路径和参数(类或方法上)
		- @ResponseBody 支持将返回值放在response内,而不是一个页面,通常用户返回json数据(返回值旁或方法上)
		- @RequestBody 允许request的参数在request体中,而不是在直接连接在地址后面。(放在参数前)
		- @RestController 该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
	- Springboot注解
		- @Configuration 用于定义配置类,指出该类是 Bean 配置的信息源
		- @EnableAutoConfiguration 允许 Spring Boot 自动配置注解
		- @SpringBootApplication 等同于同时使用@Configuration、@EnableAutoConfiguration和@ComponentScan及其默认属性。

Spring的AOP和IOC

  • AOP:主要设计模式是动态代理
    • 例子:
      • 事务管理(spring 的事务就是用AOP实现的,xml文件形式是JDK代理,因为有接口,否则是Cglib代理)
      • 权限验证
    • 提高了功能代码的重用性和可维护性。
    • Spring框架的AOP机制可以让开发者把业务流程中的通用功能抽取出来,单独编写功能代码。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把独立编写的功能代码切入到流程的合适位置。
      笔记整理2020-08-14_第10张图片
    • 切面,切点,连接点,(织入),通知。
      • 切面:切面是通知和切入点的结合。(切面类一定要交给spring管理)
        @Aspect //声明切面,标记类
      • 切点:需要在前后添加通知操作的业务方法
        @Pointcut(“execution(* *.perform(…))”) //定义切点,标记方法
      • 连接点:连接点可以说是切点的全集,可以引入通知的地方都是连接点(不用太过关心连接点)
      • 通知:横向抽取出来的操作
      //下列注解默认属性pointcut(连接点)
      @Before("execution(void *User(..))") //前置通知
      @AfterReturning("execution(void *User(..))") //后置通知
      @Around("execution(void *User(..))")//环绕通知
      @AfterThrowing("execution(void *User(..))")//异常通知
      @After("execution(void *User(..))")//最终通知
      
  • IOC
    • 简要说明:IOC核心思想是依赖注入(DI)或控制反转(IOC),就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。
    • 解释:以往的方式是不管会不会用到,先将需要的对象new好,这样就造成资源的浪费,而且如果其中一个对象new的时候出现问题,那么不管用不用到这个对象,程序都会受到影响,也就是耦合度较高。IOC容器技术就是需要的时候就注入,不需要的时候就不注入。这样就将类和对象解耦,一个对象有问题,没有使用到这个对象的地方依然可以正常运行。注入的主要方式有:set注入,构造器注入,注解注入。
    • 优点:实现对象之间的解耦。
    • 缺点:
      • 由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗(反射创建对象的速度大概是正常生成对象速度的1-2倍),所以对于强调运行效率的项目,不太适用IOC容器技术。
      • 对于IOC框架产品(比如:Spring)来讲,需要配置工作,对于一些小的项目而言,客观上也可能加大一些工作成本。

SpringMVC

Spring MVC 提供了一种分离式的方法来开发 Web 应用。通过运用像 DispatcherServelet(前端控制器)和 ViewResolver(视图解析器) 等一些简单的概念,开发 Web 应用将会变的非常简单。

  • 工作流程
    1. 用户发送请求至前端控制器DispatcherServlet
    2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
    3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
    4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
    5. 执行处理器(Controller,也叫后端控制器)。
    6. Controller执行完成返回ModelAndView
    7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
    8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
    9. ViewReslover解析后返回具体View
    10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
    11. DispatcherServlet响应用户
      笔记整理2020-08-14_第11张图片
  • 主要组件
    1. 前端控制器 DispatcherServlet(不需要程序员开发):接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
    2. 处理器映射器HandlerMapping(不需要程序员开发):根据请求的URL来查找Handler
    3. 处理器适配器HandlerAdapter(手写路径):在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
    4. 处理器Handler(需要程序员开发):具体调用模块处理请求。
    5. 视图解析器 ViewResolver(不需要程序员开发):进行视图的解析,根据视图逻辑名解析成真正的视图(view)
    6. 视图View(前端开发):View是一个接口, 它的实现类支持不同的视图类型(jsp,html,vue等等)
  • springMVC和struts2的区别
    1. springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。
    2. springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。

SpringBoot

  • 特点:

    • 快速构建项目。(最主要的作用)
    • 对主流开发框架的无配置集成。
    • 项目可独立运行,无须外部依赖Servlet容器。
    • 提供运行时的应用监控。
    • 极大地提高了开发、部署效率。
    • 支持各种开发工具,如 IntelliJ IDEA,Eclipse
  • springboot的上下文环境:Environment

    • Environment这个概念是与spring的容器container集成在一起的一个抽象概念。主要为我们的应用程序环境的两个方面的支持:profiles and properties。
    • 配置文件中的属性之所以能被程序发现并使用,是因为spring boot在启动时已经帮我们把这些值都放在了Spring的Environment里面了。
      • 配置文件读取顺序
      1. 在jar包的同一目录下建一个config文件夹,然后把配置文件放到这个文件夹下。
      2. 直接把配置文件放到jar包的同级目录。
      3. 在classpath下建一个config文件夹,然后把配置文件放进去。
      4. 是在classpath下直接放配置文件(默认的yml文件和properties文件其实是优先级最低的)。
    • profile的概念:profile的作用就是符合这个给定profile配置的bean的集合。我们在定义一个bean的时候是可以将其分配到一个指定的profile(如注解方式指定bean时可以使用@Profile注解来指定)。当有多个profile存在时,我们可以指定哪一个profile生效(active),不指定spring会根据默认的profile去运行。
  • 组件

    • Spring Actuator:监视应用程序及交互
    • Spring Initializer:Spring Initializer是一个网络应用程序,它可以生成一个Spring Boot项目,包含快速启动所需的一切。和往常一样,我们需要一个好的项目框架;它有助于你正确创建项目结构/框架。
  • jar包启动的执行命令:
    nohup java -jar *.jar --spring.config.location=config/application.properties >*.log&

    • nohup ==> 守护进程
    • java -jar *.jar ==> jar包启动
    • –spring.config.location=配置文件路径 ==> 指定jar包读取的外部配置文件路径
    • >*.log ==> 日志输出位置
    • & ==> 守护进程(仅当前连接linux终端用户在线时,一旦该用户断开连接,项目将自动停止,因此需要使用nohup)
  • springboot自动装配:

    • springboot帮我们引入了208种可以自动装配的框架笔记整理2020-08-14_第12张图片
    • 但是对于mybatis,并没有被集成到其中,因此需要在pom文件中手动添加mybatis与springboot集成依赖

SpringCloud

  1. 五大组件
    • Eureka——各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里
    • Ribbon——服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台
    • Feign——基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求
    • Hystrix——发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
    • Zuul——如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务

dubbo

docker

MyBatis

链接: https://blog.csdn.net/u014745069/article/details/80637402.
MyBatis 是支持普通SQL查询,存储过程和高级映射的优秀持久层du框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。
1. 代码量大大减少,开发效率高
2. MyBatis相当灵活,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统 一管理和优化,并可重用
3. 运行效率高
笔记整理2020-08-14_第13张图片

  1. Configuration:MyBatis所有的配置信息都保存在Configuration对象之中,配置文件的大部分配置都会存储到该类中
  2. SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能
  3. Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
  4. StatementHandler:负责对JDBC statement 的操作,如设置参数等
  5. ParameterHandler:负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
  6. ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
  7. TypeHandler:负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
  8. MappedStatement:MappedStatement维护一条节点的封装
  9. SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  10. BoundSql:表示动态生成的SQL语句以及相应的参数信息

MyBatis Plus

MyCat

  • Mycat是基于MySQL的数据库中间件,目的是为了降低数据库的压力(分布式关系型数据库解决方案)。
  • 对大数据量的数据库进行水平切分,小数据量的只需要垂直切分就可以了。
    • 垂直拆分:是将单表,或者是有关联的表放在一个数据库,把原有的一个数据库拆分成若干个数据库。
    • 水平拆分:是将一个很大的表,通过取模,按照日期范围等等拆分成若干个小表
  • mysql单表数据量达到100W就需要考虑分库了,达到500W会严重影响数据库性能

http

  • get与post区别
    • get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;
    • 在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式
    • POST 请求仅比 GET 请求略安全一点,它的数据不在 URL 中,但依然以明文的形式存放于 HTTP 的请求头中。
  • 常见的服务器响应码
    • 200:请求被正常处理
    • 204:请求被受理但没有资源可以返回
    • 206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。
    • 301:永久性重定向
    • 302:临时重定向
    • 303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上
    • 304:发送附带条件的请求时,条件不满足时返回,与重定向无关
    • 307:临时重定向,与302类似,只是强制要求使用POST方法
    • 400:请求报文语法有误,服务器无法识别
    • 401:请求需要认证
    • 403:请求的对应资源禁止被访问
    • 404:服务器无法找到对应资源
    • 500:服务器内部错误
    • 503:服务器正忙
  • cookie和Session:
    • cookie实际上是一小段文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个cookie,客户端浏览器会把cookie保存起来,当浏览器再次请求访问该网站时,浏览器把请求的网站连同该cookie一同提交给服务器,服务器检查该cookie,以此来辨认用户状态。
    • Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些。同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 name为 JSESIONID 的一个 Cookie。Session依据这个id来识别是否为同一用户(只认ID不认人)。
    • 区别
      • cookie数据存放在客户的浏览器上,session数据放在服务器上。
      • cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗。考虑到安全应当使用session。
      • session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用cookie。
      • 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

九大内置对象和四大作用域

  • JSP九大对象:内置对象(又叫隐含对象,有9个内置对象):不需要预先声明就可以在脚本代码和表达式中随意使用。
    1. request对象:代表的是来自客户端的请求,例如我们在FORM表单中填写的信息等,是最常用的对象。客户端的请求信息被封装在request对象中,通过它才能了解到客户的需求,然后做出响应。它是HttpServletRequest类的实例。作用域为request(用户请求期)。
    2. response对象:对象代表的是对客户端的响应,也就是说可以通过response 对象来组织发送到客户端的数据。但是由于组织方式比较底层,所以不建议普通读者使用,需要向客户端发送文字时直接使用。它是HttpServletResponse类的实例。作用域为page(页面执行期)
    3. session对象:指的是客户端与服务器的一次会话,从客户连到服务器的一个WebApplication开始,直到客户端与服务器断开连接为止。它是HttpSession类的实例。作用域session(会话期—)
    4. out对象 :out对象是JspWriter类的实例,是向客户端输出内容常用的对象 。作用域为page(页面执行期)
    5. page对象:page对象就是指向当前JSP页面本身,有点象类中的this指针,它是java.lang.Object类的实例 。“page” 对象代表了正在运行的由JSP文件产生的类对象,不建议一般读者使用。 作用域 page
    6. application对象:实现了用户间数据的共享,可存放全局变量。它开始于服务器的启动,直到服务器的关闭,在此期间,此对象将一直存在;这样在用户的前后连接或不同用户之间的连接中,可以对此对象的同一属性进行操作;在任何地方对此对象属性的操作,都将影响到其他用户对此的访问。服务器的启动和关闭决定了application对象的生命。它是ServletContext类的实例。作用域application
    7. pageContext对象提供了对JSP页面内所有的对象及名字空间的访问,也就是说他可以访问到本页所在的SESSION,也可以取本页面所在的application的某一属性值,他相当于页面中所有功能的集大成者,它的本类名也叫pageContext。用域 Pageconfig对象
    8. config对象:config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象)。作用域 Page
    9. exception对象:是一个例外对象,当一个页面在运行过程中发生了例外,就产生这个对象。如果一个JSP页面要应用此对象,就必须把isErrorPage设为true,否则无法编译。他实际上是java.lang.Throwable的对象。作用域 page
  • 四大作用域
    1. page范围:只在一个页面保留数据(javax.servlet.jsp.PageContext(抽象类))
    2. request范围:只在一个请求中保存数据(javax.servlet.httpServletRequest)
    3. Session范围:在一次会话中保存数据,仅供单个用户使用(javax.servlet.http.HttpSession)
    4. Application范围:在整个服务器中保存数据,全部用户共享(javax.servlet.ServletContext)
  • 三次握手简述
    1. 客户端->服务器(客户端:请求连接)
    2. 服务器->客户端(服务器:服务器应答)
    3. 客户端->服务器(客户端:连接成功)
    • 三次是最少的安全次数,两次不安全,四次浪费资源

Maven

GIT

Redis

  • Redis 是 C 语言开发的一个开源的(遵从 BSD 协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。它是一种 NoSQL(not-only sql,泛指非关系型数据库)的数据库。
  • Redis 的单线程模型:每一条到达redis服务端的命令不会立即执行,而是加入一个命令队列,然后逐个执行,并且多个客户端发送的命令的执行顺序是不确定的。但是可以确定的是不会有两条命令被同时执行,不会产生并发问题。
  • Redis 作为一个内存数据库,为什么能够快速执行:
    1. 绝大部分请求是纯粹的内存操作(非常快速)
    2. 采用单线程,避免了不必要的上下文切换和竞争条件
    3. 非阻塞IO - IO多路复用,Redis采用epoll做为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为了时间,不在I/O上浪费过多的时间。
  • 五大数据类型:
    笔记整理2020-08-14_第14张图片
  • redis的持久化机制
    • RDB:redis默认的持久化策略;把数据以快照的形式保存在磁盘上。
    • AOF:将每一个收到的写命令都通过write函数追加到AOF文件中。
      笔记整理2020-08-14_第15张图片
  • 单线程的redis为什么这么快
    • 纯内存操作
    • 单线程操作,避免了频繁的上下文切换
    • 采用了非阻塞I/O多路复用机制
  • 热点数据和冷数据
    • 热点数据,缓存才有价值。比如置顶的商品信息,首页信息,这种设定了就要被读取很多次的,可以考虑做缓存。
    • 冷数据,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。
    • 也存在修改频率很高,但是又不得不考虑缓存的场景。比如访问量,收藏量,这类会多次访问而又频繁改变。此时可以将数据同步到redis,读取的时候读缓存,改变的时候修改数据库,然后同步缓存。减少数据库的读取压力。
  • 过期策略以及内存淘汰机制
    • Redis采用的是定期删除+惰性删除策略。缺点是定期删除没有抽查到,而且没有获取的key,将不会被清除,这样redis的内存会越来越大。(引入内存淘汰机制)
    • 为什么不适用定时删除?定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略。
    • 定期删除:随机抽查key,过期了就删除
    • 惰性删除:每次获取key的时候,判断是否过期(如果设置了过期时间),过期了就删掉。
    • 内存淘汰机制
      • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
      • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
      • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
      • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
      • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
      • no-enviction(驱逐):禁止驱逐数据,新写入操作会报错
    • ps:如果没有设置expire(到期)的key, 不满足先决条件(prerequisites);那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。
  • 关于redis线程模型
    • 多个套接字–>IO多路复用程序–>文件事件分派器–>事件处理器
    • 因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
    • (原理)尽管多个文件事件可能会并发地出现,但I/O多路复用程序总是会将所有产生事件的套接字都推到一个队列里面,然后通过这个队列,以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字:当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕), I/O多路复用程序才会继续向文件事件分派器传送下一个套接字。
  • 关于主从复制,读写分离
    • Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件
    • 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次
    • 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内
    • 尽量避免在压力很大的主库上增加从库
    • 主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3
  • redis实现分布式锁
    • Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作。使用完毕后删除key即可解锁。
    • 解决死锁:
      • 通过Redis中expire()给锁设定最大持有时间,如果超过,则Redis来帮我们释放锁。
      • 使用 setnx key “当前系统时间+锁持有的时间”和getset key “当前系统时间+锁持有的时间”组合的命令就可以实现。

消息中间件

  • 消息队列(消息中间件)好处:
    1. 解耦:生产者和消费者实现间接对接,生产者不用等待消费者,消费者也不用放弃手中的事情而先去消费。
    2. 异步:生产者将消息放入消息队列后可以去做别的事情。提高了工作效率。
    3. 削峰:假如一个时间段内消息量暴增,比如在批量处理一些数据的时候,这时消费者可能不能瞬间处理这些消息,所以我们可以把消息暂存在消息队列中,依次处理,同时生产者那边也可以不用等待。

ActiveMQ

  • ActiveMQ采用push模式。

kafka

  • Kafka采用的是:主动pull,即Producer将消息推送到broker,Consumer主动从broker拉取消息进行消费
    • 原因主要有以下两点:
      • 首先,主要是避免push模式下,当Producer推送的速率远大于Consumer消费的速率时,Consumer承受不住压力而崩溃或消息丢失而重新推送,浪费资源
      • 再者,Push模式下,Broker不知道下游Consumer消费能力和消费策略的情况下,不知道采用立即推送单条消息还是缓存并批量推送,因此采用哪种策略可能都不合适。而Pull模式下,Consumer可以自主决定是否批量的从broker拉取数据,根据自己的消费能力去决定数据拉取策略。
  • 各组件
    • Topic:消息主题,可以理解为消息的分类(比如车辆审核,车辆消息变更),kafka的数据就保存在topic。在每个broker上都可以创建多个topic。

    • Partition:Topic的分区,每个topic可以有多个分区,分区的作用是做负载,提高kafka的吞吐量。同一个topic在不同的分区的数据是不重复的,partition的表现形式就是一个一个的文件夹!为什么要做分区?

    • Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的时候会选择一个备胎(Follower)上位,成为Leader。在kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自己)。

    • Producer:生产者,写入数据

    • Consumer:消费者,即消息的消费方,是消息的出口。

    • Consumer Group:我们可以将多个消费组组成一个消费者组,在kafka的设计中同一个分区的数据只能被消费者组中的某一个消费者消费。同一个消费者组的消费者可以消费同一个topic的不同分区的数据,这也是为了提高kafka的吞吐量!

    • Broker:kafka节点(kafka实例),一个分区主题的不同分区可以存入不同的broker,但是一个分区只能存在于一个broker中

      • 如果不分区,相当于说一个topic只有一个分区,那么就只会放在一个broker中,这样broker机器的IO性能就将成为kafka的瓶颈。分区之后,每条消息被发送到Topic之前,会根据分区规则,有选择的存储在哪个分区,使消息不只是往一个Broker上写,就降低了单个Broker的IO写频率,也充分利用了多机提高了消息写入吞吐量。
      • 提高并发。以partition为读写单位,可以多个消费者同时消费数据,提高了消息的处理效率。
    • Zookeeper:kafka集群依赖zookeeper来保存集群的的元信息,来保证系统的可用性。

  • 存储策略
    • 无论消息是否被消费,kafka都会保存所有的消息。那对于旧数据有什么删除策略呢?
    • 基于时间,默认配置是168小时(7天)。
    • 基于大小,默认配置是1073741824。
    • 需要注意的是,kafka读取特定消息的时间复杂度是O(1),所以这里删除过期的文件并不会提高kafka的性能!
  • 存入数据笔记整理2020-08-14_第16张图片
    • 消息写入leader后,follower是主动的去leader进行同步的!producer采用push模式将数据发布到broker,每条消息追加到分区中,顺序写入磁盘,所以保证同一分区内的数据是有序的!写入示意图如下:笔记整理2020-08-14_第17张图片
  • 消费数据
    • 生产消息相同的是,消费者在拉取消息的时候也是找leader去拉取。
    • 多个消费者可以组成一个消费者组(consumer group),每个消费者组都有一个组id!同一个消费组者的消费者可以消费同一topic下不同分区的数据,但是不会组内多个消费者消费同一分区的数据。也就是一个分区的数据只可能让一个消费者来消费,但是一个消费者可以消费几个分区的数据。建议消费者组的consumer的数量与partition的数量一致
      笔记整理2020-08-14_第18张图片

Tomcat

SQL优化

  • 索引优化
    • 创建(优先考虑where、group by使用到的字段)
      • 多个条件时可以创建多列索引,比多个单列索引效率高
      • 让选择性最强的索引列(能排除更多记录的列)放在前面
      • 对于BLOB、TEXT 和 VARCHAR类型的列,加前缀索引
      • 覆盖索引:满足查询语句中所有字段与条件,这样可以直接在索引中查询,而不用再到数据库读取行了
    • 注意:
      • MySql在使用like进行模糊查询的时候把%放后面,避免开头模糊查询,因为MySql在使用like查询的时候只有使用后面的%时,才会使用到索引。
      • 尽量避免使用in 和not in,会导致数据库引擎放弃索引进行全表扫描,可使用exists代替
      • 尽量避免使用or,会导致数据库引擎放弃索引进行全表扫描,可使用union代替
      • 尽量避免进行null值的判断,会导致数据库引擎放弃索引进行全表扫描,可以给字段添加默认值0,对0值进行判断
      • 尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描,可以将表达式、函数操作移动到等号右侧
      • 当数据量大时,避免使用where 1=1的条件。通常为了方便拼装查询条件,我们会默认使用该条件,数据库引擎会放弃索引进行全表扫描
    • 优点:建立索引后,查询时不会扫描全表,而会查询索引表锁定结果。
    • 缺点:在数据库进行DML操作的时候,除了维护数据表之外,还需要维护索引表,运维成本增加。
    • 应用场景:数据量比较大,查询字段较多的情况。
  • 查询优化
    • Explain 用来分析 SELECT 查询语句,开发人员可以通过分析结果来优化查询语句。
      • 关于Explain:
        • table:显示这一行的数据是关于哪张表的
        • type:这是最重要的字段之一,显示查询使用了何种类型。从最好到最差的连接类型为system、const、eq_reg、ref、range、index和ALL,一般来说,得保证查询至少达到range级别,最好能达到ref。
          • system、const: 可以将查询的变量转为常量. 如id=1; id为 主键或唯一键.
          • eq_ref: 访问索引,返回某单一行的数据.(通常在联接时出现,查询使用的索引为主键或惟一键)
          • ref: 访问索引,返回某个值的数据.(可以返回多行) 通常使用=时发生
          • range: 这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西,并且该字段上建有索引时发生的情况(注:不一定好于index)
          • index: 以索引的顺序进行全表扫描,优点是不用排序,缺点是还要全表扫描
          • ALL: 全表扫描,应该尽量避免
        • possible_keys:显示可能应用在这张表中的索引。如果为空,表示没有可能应用的索引。
        • key:实际使用的索引。如果为NULL,则没有使用索引。MySQL很少会选择优化不足的索引,此时可以在SELECT语句中使用FORCE INDEX(index_name)来强制使用一个索引或者用IGNORE INDEX(index_name)来强制忽略索引。
        • key_len:使用的索引的长度。在不损失精确性的情况下,长度越短越好
        • ref:显示索引的哪一列被使用了,如果可能的话,是一个常数
        • rows:MySQL认为必须检索的用来返回请求数据的行数
        • Extra:关于MySQL如何解析查询的额外信息,主要有以下几种
          • using index: 只用到索引,可以避免访问表,性能很高。
          • using where: 使用到where来过滤数据, 不是所有的where clause都要显示using where. 如以=方式访问索引。
          • using tmporary: 用到临时表去处理当前的查询。
          • using filesort: 用到额外的排序,此时mysql会根据联接类型浏览所有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行。(当使用order by v1,而没用到索引时,就会使用额外的排序)。
          • range checked for eache record(index map:N): 没有好的索引可以使用。
          • Using index for group-by:表明可以在索引中找到分组所需的所有数据,不需要查询实际的表。explain select user_id from t_order group by user_id;
        • 见到Using temporary和Using filesort,就意味着MySQL根本不能使用索引,结果是检索会很慢,需要优化sql了。
    • 只返回必要的列:最好不要使用 SELECT * 语句。
    • 只返回必要的行:使用 LIMIT 语句来限制返回的数据。
    • 给经常要被查询的数据设置缓存
  • 重构查询
    • 切分大查询:一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。
    • 分解大连接查询:将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联,这样做的好处有:
      • 让缓存更高效:对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
      • 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
      • 减少锁竞争。
      • 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可伸缩。

编码习惯

- 两张数据库表(或数据库表与缓存)间接交互时,在逻辑处理过程中首先要进行空值判断。

你可能感兴趣的:(私密)