索引

  1. 数据结构 提升mysql的查询效率
  2. mysql: B+Tree hash

B+Tree

  1. BTree : 自平衡多叉查找树
  2. 度: 度越大 树越矮胖 IO次数越少
  3. B+Tree : 优化 --》 非叶子节点不存储data
  4. 增大度
  5. 所有data都存储叶子节点,有顺序访问指针
  6. 适合范围查询

优缺点

  1. 占空间
  2. 增删改变慢

适合加索引

  1. where
  2. id
  3. 排序分组
  4. 关联字段

不适合

  1. 增删改比较多
  2. 不用来查询的字段
  3. 数据重复特别多
  4. 字段比较大 text

分类

  1. 普通索引
  2. 唯一索引
  3. 联合索引
  4. 全文索引

最左原则

  1. 比如: create index idxxx on 表(a,b,c)
  2. a,b,c

存储引擎

  1. myisam 非聚簇索引 (索引)(索引) (数据)
  2. innodb 事务 外键 聚簇索引 (索引+数据)

SQL优化

  1. 怎么找的: 慢查询日志 skywalking
  2. explain: 执行计划
  3. id: 执行顺序
  4. type: 访问类型 all (全表扫描)
  5. range ref const
  6. index(覆盖索引)
  7. key: 哪个索引
  8. extra: using temporay 分组没用到索引
  9. using filesort 排序没用到索引
  10. 优化:
  11. 有很多情况会让我们索引失效 :
  12. 最左
  13. like %
  14. or
  15. 索引字段做操作
  16. 小表驱动大表

多线程快速回顾

线程创建方式

  1. 集成Thread 重写run
  2. 实现Runnable 实现run
  3. 实现Callable 实现call 带返回值
  4. 通过线程池管理线程

如何停止一个正在运行的线程

  1. 线程.interupt() // 将线程的中断状态 改为true
  2. 线程.isInterupted() // 判断线程的中断状态 false true 中断

如何保证多个线程有序执行

  1. join
  2. A {
  3. B.join() // A会等待B先执行完毕
  4. }

线程分类: 用户线程(默认) 守护线程 setDaemon(true)
线程的优先级 : 抢占式线程调度机制 优先级 影响争夺时间片的概率 取值: 1~10
线程的生命周期

  1. new Thread() ==> 新建 NEW
  2. 线程.start() ==> 启动线程 ==> 就绪 (Runnable) ==> 争夺时间片
  3. ==> 运行run方法 ==> 时间片耗尽 ==> 就绪
  4. 锁,wait,sleep ==> 阻塞的状态
  5. run方法执行完毕 ==> 线程任务完毕 ==> 销毁

线程安全

  1. 多个线程 操作 共享变量 ,可能导致共享变量数据错误

JMM

  1. java 内存模型
  2. 主内存
  3. 线程1 [工作内存 副本]
  4. 线程2 [工作内存 副本] ticket
  5. 线程3 [工作内存 副本]

并发编程三大特性

  1. 原子性
  2. 可见性
  3. 有序性

volatile

  1. 可见性
  2. 有序性

synchronized 同步锁

  1. 修饰代码块 成员方法 静态方法
  2. 必须使用对象作为锁:
  3. 对象头: 锁的信息
  4. 对象监视器: monitor
  5. 要想保证多个线程起到同步的效果,必须使用统一个对象作为锁
  6. 优点:保证通知
  7. 缺点: 性能
  8. 1.6
  9. 无锁 偏向锁 轻量锁 重量级

JUC Lock

加锁:  
   Lock 锁对象 

   锁对象.lock()
   try{
      // 同步代码块
   } finally {
      锁对象.unlock();
   }

Lock 和 Synchronized

内置      api

功能:  lock功能更多些 ,  tryLock(时间)==boolean    读写锁

性能:   并发量不大, 效率差不多  
       并发量大  , lock性能好

ThreadLocal 线程局部变量

  共享变量

  线程[副本]
  线程[副本]
  线程[副本]

  项目: 存储用户登录信息
  Thread[ThreadLocalMap<ThreadLocal,value>


  ThreadLocal.remove() 删除掉用户信息

CAS compare and swap 比较并替换 java中乐观锁的实现


内存  V
期望值  A
新值   B 

    V == A    true :  没有被修改   将V改成B  
              false: 说明被其它线程修改了  不做处理 

        UnSafe 不能直接使用

        JDK提供了一些原子类供我们直接使用,底层采用CAS

        AtomicInteger  int值

线程池

通过这套api可以让我们 用固定数量的线程,来执行大量的任务

线程可复用,节省了线程的创建和销毁的时间
线程线程的统一管理

线程池API

Executor   执行任务

ExecutorService  项目中使用的线程池接口

ThreadPoolExecutor 线程池的核心实现类  

Executors  线程池的工具类

ThreadPoolExecutor
核心参数

核心线程数
最大线程数
存活时间
存活时间单位
任务阻塞队列
线程工厂
线程拒绝策略

Executors 工具类

newCached  可缓存的线程池      0    int最大值   同步队列[]

newFixed(核心线程数量)   定长的线程池       

newSingle  单例的线程池     只有一个线程工作

schedule   带调度的定长线程池    定长线程池 +  调度(延时执行任务,周期性的执行任务)

项目中


搜索  异步搜索历史

点赞  异步计算热点评论
批量导入   数据分段 使用线程池执行批量插入


基于spring封装后的API

@EnableAsync  // 开启异步处理功能
@Bean  spring封装过后的线程池bean (ThreadPoolExecutor 线程池)

@Async(线程池的名称)  // 该方法使用线程池 异步处理

spring相关面试点

IOC : 控制反转
重要的类

BeanFactory 基础接口 定义了容器最基本的方法
XmlBeanFactory 实现了基本容器功能

ApplicationContext 容器的高级接口 

通过xml来定义容器中的bean   ClassPathXmlApplicationContext

通过注解来定义容器中的bean   AnnotationConfigApplicationContext


IOC容器 初始化的一个流程 

IOC容器 .getBean方法的流程

初始化流程

1. IOC容器bean的配置,  定义xml配置文件  通过bean标签定义对象

2. 加载配置文件,基于dom解析技术 将配置文件内容解析成document文档

3. 容器会将所有的bean标签,每一个bean标签都会封装为一个BeanDefinition对象(bean的定义)

4. 将所有beanDefinition对象注册到容器
     是将这些对象  以bean 的id作为value , beanDefinition本身作为value, 存入到一个map集合中  (DefaultListableBeanFactory 中的beanDefinitionMap ,ConcurrentHashMap)

5. 最后IOC容器会将注册列表中,所有为单例模式的bean,非抽象的  非延迟加载的bean 进行初始化, 初始化后得到的单例对象
   会存储到  单例的map集合中 
   (DefaultListableBeanFactory 中的singletonObjects ,ConcurrentHashMap)

getBean流程

1. IOC容器会在单例的map集合中,根据查询的beanName查询是否有该对象,有直接返回

2. IOC容器会查看是否有父容器,父容器中是否包含该对象,如何包含直接调用父容器的getBean方法获取对象

3. 先查询bean定义的map集合 , 查看是否有关于该bean的定义,如果没有直接报 NoSuchBeanDefinition

4. 查看bean定义的scope属性, 是 singleton 还是prototype
    如果 singleton
        在检查一次单例集合
        创建这个对象
        将这个对象存入单例map中
    如果 prototype
        创建这个对象

5. 创建对象的方法:
    IOC容器会根据你的配置得到 该对象Class
          会根据你的配置选择正确的构造方法
               如果配置了:  factory-method  ==> 使用反射调用工厂方法进行对象的构建
                          有参构造参数      ==> 使用反射调用有参构造器进行对象的构建
                          使用无参构造器  
6. 根据配置完成DI依赖注入

Bean的循环依赖

A -->  B    B --> A  产生了闭环,这种情况成为循环依赖

1. 如果bean的scope都是多例,  会报循环依赖的错误

2. 如果bean他们依赖关系是通过构造器依赖的, 会报循环依赖的错误

3. 如果bean都是单例的,并且关系是通过属性注入的, 默认 不会报错 (可以通过配置关闭允许循环依赖)


spring如何解决了循环依赖:  
    三级缓存: 
       singletonObject : 一级缓存   存放的是完整状态的bean , 对象实例后,所有属性都完成的依赖注入
       earlysingletonObject: 二级缓存  存放原始的bean ,还未完成依赖注入, 解决循环依赖
       singletonFactorys : 三级缓存,  存放 bean工厂对象, 解决动态代理产生的bean的循环依赖问题

**Bean的覆盖问题

在spring的配置中,出现了相同名称的bean 

spring默认允许bean的覆盖,后加载的bean的定义 会把先加载的bean的定义 覆盖掉

如果不想被覆盖,可以通过配置 关闭调用允许bean的覆盖

spring 事务的配置


手动编程事务:   TransactionTemplate 管理事务


声明式事务:   
    AOP配置事务

    注解配置事务:   @Transactional

什么情况会事务失效

1. 数据库本身支持事务  
2. 方法用的不是public
3. 自己将异常try catch
4. 抛出的异常,不属于RuntimeException, 可以在注解后面加上  rollbackFor(异常的class)
5. 分布式事务
6. 事务传播行为 
7. 自身调用

事务的传播行为

一个事务方法,调用另一个包含事务的方法,产生了事务的嵌套

spring定义了7种传播行为:
    Required :  表示该方法必须运行在事务中,如果调用我们的方法包含事务,那么采用该事务即可,如果调用我们的方法没有使用事务,则新建一个事务

    Requires_new:  表示该方法必须运行在事务中,不过不管调用调用的方法是否包含事务,我们的方法都会启动一个新的事务

项目中AOP的使用

AOP的介绍:  面向切面编程

AOP的使用:  1. 引入aop的依赖   2. 定义切面类  3. 定义切入点  4. 定义处理增强的  通知方法 (5种通知方法)

AOP项目中的使用:  统一异常处理 , 统一的事务处理      ,     行为日志记录  , 主从数据源的切换

AOP底层原理: 
    AOP的底层是基于动态代理:
        JDK  jdk自带的   要求被代理对象必须要实现接口

        CGLIB  开源的,需要引依赖   要求被代理的的对象必须能被继承

mybatis相关面试点

mybatis的核心组件

SqlSessionFactoryBuilder 构建器, 配置文件解析,session工厂的创建

Configuration:  核心配置,解析所有配置内容,都会存入到这个配置对象中

MappedStatement: 封装sql描述的对象,mybatis.xml中每一个 select|insert|update|delete 都会封装成这样的一个对象

SqlSessionFactory session工厂对象,用于创建 SqlSession会话对象

SqlSession:  核心的用于和数据库做交互的API,会话对象

Executor: sqlSession中核心的用于和数据库交互的对象,执行器

mybatis运行原理

构建阶段: 
SqlSessionBuilder 的build方法解析配置文件,将解析的全部内容封装到Configuration对象中
在Configuration中  重点  有一个map集合  mappedStatements ==> 存储了所有的xxxx.xml中的
SELECT|INSERT|UPDATE|DELETE的应sql描述   每个描述都封装成 MappedStatement  ms对象
mappedStatements map的格式    key: namespace + "." + sql标签的id    value: ms对象 该sql描述
解析完毕后,builder会构建出 SqlSessionFactory对象

执行CRUD阶段: 
每一次,当我们要操作数据库时,都会通过sqlSessionFactory 获得一个SqlSession 会话对象
代理对象 = session.getMapper(mapper接口);
会基于jdk的动态代理,生成一个代理对象
代理对象.findAll();
代理对象 会拼接 当前 调用接口的 包名+"." +类名+ "." +调用方法名
如: com.itcast.dao.UserMapper.findAll
去Configuration对象的mappedStatement这个map集合中查找对应sql描述 , 如果未查询到会报绑定异常
如果找到了: 
      调用执行器Executor 根据 sql描述执行查询
       Executor 调用JDBC完成数据库的操作( ParameterHandler  ResultHandler TypeHandler)
关闭会话SqlSession

mybatis 动态SQL

if   <where>   
<foreach>  // 批量处理  
resultMap   
<assoiation> <collection>  // 关联查询 
<cache /> // 二级缓存

mybatis批量处理

java   for { mapper.方法  }     效率特别差

Executor 类型设置为 BATCH(批处理)       SIMPLE(默认)    REUSE(可复用)
java   for { mapper.方法  }   效率更高一些

<foreach>  效率最高
拼接成一条sql语句, 批量插入可以使用  insert into (字段)  values (1条),(2条),........

mybatis关联查询

对1的关联查询
A {
  B b;
}

resultMap
    association ==>b  select==>sql fetch=lazy(延迟加载)  eager(饥饿加载)

对多的关联查询
A {
  List<B>  list;
}
resultMap
    collection ==> list select==>sql

mybatis中的缓存

mybatis支持 一级缓存  二级缓存

一级缓存:  无需配置,直接就可以使用   ,称为SqlSession级别的缓存,在sqlSession会有对应的缓存对象
          在同一个sqlSession中,进行相同操作时,无需查询数据库 使用缓存即可

          但是如果在多个会话SqlSession中,无法实现缓存的共享
二级缓存:  需要手动开启 开启方法: 1. 配置EnableCache = true   2. 开启指定mapper缓存 <cache />  3. 对应sql useCache
          二级缓存作用域 :  同一个mapper一个缓存对象


        加载顺序:   二级缓存 -->  一级缓存  -->   数据库

mybatis中的分页实现

基于PageHelper实现的

基于mybatis的插件机制, PageHelper实现了 mybatis 的 Intecepter 拦截器接口,并指明要拦截 Executor.query方法

我们在配置中引入了 PageHelper插件 , 并且配置了数据库方言 

在需要使用分页的时候,只需要你提前调用 PageHelper.startPage(1,10)

对应接下来要执行的sql, 分页拦截器会进行拦截, 拦截后 生成两条sql语句,  根据参数生成一个 查询count的语句 得到的结果设置到page的total中, 在根据语句 及 使用的数据库,拼接一个分页语句  (mysql  拼接  limit (?,?)) 进行分页查询,得到结果也设置到page中

我们就得到分页查询结果

mybatis的mapper接口是否支持方法重载 mybatis,xml中是否可以定义id相同sql

不可以

mybatis.xml需要传递多个参数

指定多个 @Param

封装成一个实体类

map

#取值 和 $取值

# 预编译, 防止sql注入

$    表名  select * from ${}