switch支持的类型:byte
, short
, int
, char
, enum
,String(jdk7以后支持)
css
关键字:html
final
- 能够修饰类、函数、变量
- 被修饰的类不能够被继承;被修饰的方法不能够被复写;被修饰的变量是一个常量,只能赋值一次
- 内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量
synchronized
volatile
- `保证了不一样线程对这个变量进行读取时的可见性,即一个线程修改 了某个变量的值 , 这新值对其余线程来讲是当即可见的 (解决了线程间共享变量)
- 禁止进行指令重排序 ,阻止编译器对代码的优化
- 程序并发正确地执行,必需要保证原子性、可见性以及有序性,锁保证了原子性,而volatile保证可见性和有序性
在catch中return,finally会不会执行?java
Student s = new Student();在内存中作了哪些事情mysql
Java异常有哪几种?Throwble的子类有哪些redis
序列化serialVersionUID的做用:算法
&和&&的区别:sql
hashCode和equals方法的关系数据库
设计一个类的时候为须要重写equals方法,在重写equals方法的同时,必须重写hashCode方法。编程
- 调用equals方法获得的结果为true,则两个对象的hashcode值一定相等
- 调用equals方法获得的结果为false,则两个对象的hashcode值不必定不一样
- 两个对象的hashcode值不等,则equals方法获得的结果一定为false
- 两个对象的hashcode值相等,则equals方法获得的结果未知
什么是值传递和引用传递设计模式
强引用,软引用和弱引用的区别
数组在内存中如何分配
深刻理解Java中的String
String类被final修饰,不能被继承,而且它的成员方法都默认为final方法
调用sub,concat,replace
方法,会从新生成了一个新的字符串对象,所以原字符串并无被改变
字符串建立方式:(1)使用""引号建立字符串;(2)使用new关键字建立字符串.
代码示例:
String s0 = "helloworld";// 编译期肯定
String s1 = "hello"+"world";// 编译期肯定,即变成“helloworld”
String s2 = new String("helloworld");// 运行期肯定
String s3 = s0 + "";// 运行期肯定,至关于new StringBuilder().append(s0).append("");
String s4 = "hello" + 1;// 编译期肯定
结果:
s0 == s1 -> true
s0 == s2 -> false
s0 == s3 -> false
s2 == s3 -> false
复制代码
List集合
Arraylist
add(E element)
执行的时间复杂度为O(1)
add(int index, E element)
执行的时间复杂度为O(n-i)
E remove(int index)
执行的时间复杂度为O(n-i)
E get(int index)
执行的时间复杂度为O(1)
,性能远高于LinkedList
LinkedList
:
add(E element)
执行的时间复杂度为O(1)
,性能略高于Arraylist,由于Arraylist须要扩容addFirst(E e)
执行的时间复杂度为O(1)
,性能远高于Arraylistadd(int index, E element)
视具体位置而定,index靠前则LinkedList性能会好一点,由于ArrayList性能主要损耗在元素的后移以及扩容上E remove(int index)
同add(int index, E element)
E get(int index)
执行的时间复杂度为O(n/2)
Vector
:全部的方法都是同步的,相对于Arraylist
是线程安全的,
Map集合
HashMap
- 容许null做为键,容许一个或多个键所对应的值为 null
- 默认的初始化大小为16。以后每次扩充,容量变为原来的2倍。若是给了容量的初始值不是2的幂次方大小,会将其扩充为2的幂次方大小
Hashtable
- 相对于HashMap是线程安全的,使用 synchronized 来保证线程安全
- 不容许存在null的键值
- 容量初始值默认大小为11,以后每次扩充,容量变为原来的2n+1。若是给了容量的初始值,会直接使用该默认值
ConcurrentHashMap
- 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不一样数据段的数据,就不会存在锁竞争,提升并发访问率(默认分配16个Segment)
- JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操做
LinkedHashMap
:继承自HashMap,在此基础上增长了一条双向链表,使得上面的结构能够保持键值对的插入顺序。同时经过对链表进行相应的操做,实现了访问顺序相关逻辑
TreeMap
:基于红黑树实现,利用红黑树的性质,实现了键值对排序功能(阅读:TreeMap源码分析)
Set集合
LinkedHashSet
:继承与 HashSet,而且其内部是经过 LinkedHashMap 来实现的HashSet(无序,惟一)
: 基于 HashMap 实现的,底层采用 HashMap 来保存元素TreeSet(有序,惟一)
: 红黑树(自平衡的排序二叉树)线程基础
生命周期:建立(new),就绪(runnable),运行(running),阻塞(blocked、time waiting、waiting),消亡(dead)
有关线程运行状态的方法
如何唤醒处于睡眠状体的线程
如何释放处于等待中的线程
什么叫守护线程,用什么方法实现守护线程(Thread.setDeamon()的含义)
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)用个比较通俗的好比,任何一个守护线程都是整个JVM中全部非守护线程的保姆:只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工做。JVM内部的实现是若是运行的程序只剩下守护线程的话,程序将终止运行,直接结束。因此守护线程是做为辅助线程存在的,主要的做用是提供计数等等辅助的功能
如何中止一个线程
- 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止(标志用volatile修饰)
- 使用stop方法能够强行终止正在运行或挂起的线程(不推荐)
- 使用interrupt方法中断线程,使用interrupt方法来终端线程可分为两种状况:
- 线程处于阻塞状态,如使用了sleep方法,将抛出一个InterruptedException
- 使用
while(!isInterrupted()){}
来判断线程是否被中断,线程将直接退出
ThreadLocal详解
JDK中的动态代理和CGLIB
五层体系结构(TCP/IP协议簇)
物理层->数据链路层->网络层->传输层->应用层
TCP/IP模型
TCP粘包和拆包产生的缘由
TCP粘包和拆包的解决策略
tcp,udp区别
TCP提供面向链接的、可靠的数据流传输;UDP提供的是非面向链接的、不可靠的数据流传输
TCP传输单位称为TCP报文段,UDP传输单位称为用户数据报
TCP注重数据安全性,UDP数据传输快,由于不须要链接等待,少了许多操做,可是其安全性却通常
tcp三次握手
tcp四次挥手
一次完整的HTTP请求过程
域名解析 --> 发起TCP的3次握手 --> 创建TCP链接后发起http请求 --> 服务器响应http请求,浏览器获得html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户
讲一讲长链接
在HTTP1.0和HTTP1.1协议中都有对长链接的支持。其中HTTP1.0须要在request中增长”Connection: keep-alive“ header才可以支持,而HTTP1.1默认支持
- http1.0请求与服务端的交互过程: a. 客户端发出带有包含一个header
Connection:keep-alive
的请求;b. 服务端接收到这个请求后,根据http1.0
和Connection: keep-alive
判断出这是一个长链接,就会在response的header中也增长Connection: keep-alive
,同是不会关闭已创建的tcp链接;c. 客户端收到服务端的response后,发现其中包含Connection: keep-alive
,就认为是一个长链接,不关闭这个链接。并用该链接再发送request.转到a
TCP如何保证可靠传输
详细介绍http
URI和URL的区别
HTTPS和HTTP的区别
https是如何保证数据传输的安全
https实际就是在TCP层与http层之间加入了SSL/TLS来为上层的安全保驾护航,主要用到对称加密、非对称加密、证书,等技术进行客户端与服务器的数据加密传输,最终达到保证整个通讯的安全性。
SSL/TLS协议做用:a. 认证用户和服务器,确保数据发送到正确的客户机和服务器;b. 加密数据以防止数据中途被窃取;c. 维护数据的完整性,确保数据在传输过程当中不被改变
加密算法
-Xms
(最小值)和-Xmx
(最大值)参数设置,一般-Xms
与-Xmx
的值设置成同样(避免在运行时频繁调整Heap的大小)-XX:PermSize
和 -XX:MaxPernSize
参数经过类的全限定名来获取定义此类的二进制字节流,将这个字节流所表明的静态存储结构转换为方法区的运行时数据结构,在内存中生成一个表明类的数据访问入口的java.lang.Class对象
<clinit>()
方法,<clinit>()
方法是由编译器自动收集全部类变量的赋值动做和静态语句块的语句合并而成,同一类中的静态块与类变量按顺序初始化,在同一个加载器下,一个类只会被初始化一次若是一个类加载器收到类加载的请求,它首先不会本身去尝试加载这个类,而是把这个请求委派给父类加载器完成。每一个类加载器都是如此,只有当父加载器在本身的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试本身去加载
概述:比较相邻的元素。若是第一个比第二个大,就交换他们两个。 对每一对相邻元素做一样的工做,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。针对全部的元素重复以上的步骤,除了最后一个。持续每次对愈来愈少的元素重复上面的步骤,直到没有任何一对数字须要比较
概述:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;而后在 剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止
概述:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到所有插入排序完为止
概述:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序
概述:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每一个子序列是有序的。而后再把有序子序列合并为总体有序序列
概述:经过一趟排序将待排序记录分割成独立的两部分,一部分全小于选取的参考值,另外一部分全大于选取的参考值。这样分别对两部分排序以后顺序就能够排好了
概述:将待排序的序列构形成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),而后将剩余的n-1个序列从新构形成一个堆,这样就会获得n个元素中的次最大值。如此反复执行,就能获得一个有序序列了
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime,//线程池中超过corePoolSize数目的空闲线程最大存活时间;能够allowCoreThreadTimeOut(true)成为核心线程的有效时间
TimeUnit unit,//keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue,//阻塞任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler) {//当提交任务数超过限制时
复制代码
Executors.newCachedThreadPool()
:可缓存线程池
Executors.FixedThreadPool()
:定长线程池
Executors.ScheduledThreadPool()
:定时线程池
Executors.SingleThreadExecutor()
:单线程化的线程池
AbortPolicy
:拒绝任务,且还抛出RejectedExecutionException异常,线程池默认策略CallerRunPolicy
:拒绝新任务进入,若是该线程池尚未被关闭,那么这个新的任务在执行线程中被调用DiscardOldestPolicy
:若是执行程序还没有关闭,则位于头部的任务将会被移除,而后重试执行任务(再次失败,则重复该过程),这样将会致使新的任务将会被执行,而先前的任务将会被移除。DiscardPolicy
:没有添加进去的任务将会被抛弃,也不抛出异常。基本上为静默模式Mysql 中 MyISAM 和 InnoDB 的区别有哪些
为何用自增列做为主键
- 表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页
- 若是使用非自增主键(若是身份证号或学号等),因为每次插入主键的值近似于随机,所以每次新纪录都要被插到现有索引页得中间某个位置,此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增长了不少开销,同时频繁的移动、分页操做形成了大量的碎片,获得了不够紧凑的索引结构,后续不得不经过OPTIMIZE TABLE来重建表并优化填充页面
Mysql中InnoDB的一级索引、二级索引
每一个InnoDB表具备一个特殊的索引称为聚簇索引(也叫汇集索引,聚类索引,簇集索引)。若是表上定义有主键,该主键索引就是聚簇索引。若是未定义主键,MySQL取第一个惟一索引(unique)并且只含非空列(NOT NULL)做为主键,InnoDB使用它做为聚簇索引。若是没有这样的列,InnoDB就本身产生一个这样的ID值,它有六个字节,并且是隐藏的,使其做为聚簇索引。表中的聚簇索引(clustered index )就是一级索引,除此以外,表上的其余非聚簇索引都是二级索引,又叫辅助索引(secondary indexes)
B+树索引和哈希索引的区别
B树和B+树的区别
Mysql 索引类型有多少种
主键索引(primary key)、 惟一索引(unique index)、普通索引(index)、全文索引(fulltext key)
alert table student
add primary key ('stu_id'), //主键索引
add unique index 'stu_code' ('stu_code'), //惟一索引
add index 'name_phone' ('name','phone'), //普通索引,复合索引
add fulltext index 'stu_desc' ('stu_desc'); //全文索引
复制代码
Mysql 索引的使用与优化
列独立:保证索引包含的字段独立在查询语句中,不能是在表达式中
左前缀:like匹配模式左边不能以通配符开始,才能使用索引
复合索引由左到右生效,例如:创建索引index(a,b,c)
,以下查询where条件分析
where a = 3 # 是,只使用了a
where a=3 and b=5 # 是,使用了a,b
where a=3 and b=5 and c=4 # 是,使用了a,b,c
where b=3 or where c=4 # 否
where a=3 and c=4 # 是,仅使用了a
where a=3 and b>10 and c=7 # 是,使用了a,b
where a=3 and b like '%xx%' and c=7 # 是,使用了a,b???
复制代码
对索引中全部列都指定具体值,便是对索引中的全部列都有等值匹配的条件
对索引的值可以进行范围查找
仅仅使用索引中的最左边列进行查询,好比在 col1 + col2 + col3 字段上的联合索引可以被包含 col一、(col1 + col2)、(col1 + col2 + col3)的等值查询利用到,但是不可以被 col二、(col二、col3)的等值查询利用到
查询的列都在索引的字段中时,查询的效率更高
仅仅使用索引中的第一列,而且只包含索引第一列的开头一部分进行查找
解决方法:1. 使用全文索引;2. 利用 innodb 的表都是聚簇表的特色,采起一种轻量级别的解决方式:通常状况下,索引都会比表小,扫描索引要比扫描表更快,而Innodb 表上二级索引 idx_last_name 实际上存储字段 last_name 还有主键 actot_id,那么理想的访问应该是首先扫描二级索引 idx_last_name 得到知足条件的last_name like '%NI%' 的主键 actor_id 列表,以后根据主键回表去检索记录,这样访问避开了全表扫描演员表 actor 产生的大量 IO 请求。查询sql为
SELECT * FROM (SELECT actor_id FROM actor WHERE last_name LIKE '%NI%') a, actor b WHERE a.actor_id = b.actor_id
当列的类型是字符串,那么必定记得在 where 条件中把字符常量值用引号引发来,不然即使这个列上有索引,mysql 也不会用到,由于 MySQL 默认把输入的常量值进行转换之后才进行检索
解决办法:1. 倒过来存储并创建索引;2. 新增伪hash字段 把字符串转化为整型
若是查询的列刚好是索引的一部分,那么查询只须要在索引文件上进行,不须要回行到磁盘,这种查询,速度极快
在根据条件查询数据时,若是查询条件不能用的索引,能够先查出数据行的id,再根据id去取数据行
排序的字段上加入索引,能够提升速度
重复索引:在同一列或者相同顺序的几个列创建了多个索引,成为重复索引,没有任何意义,删掉; 冗余索引:两个或多个索引所覆盖的列有重叠,好比对于列m,n ,加索引index m(m),indexmn(m,n),称为冗余索引
在数据表长期的更改过程当中,索引文件和数据文件都会产生空洞,造成碎片。修复表的过程十分耗费资源,能够用比较长的周期修复表
//清理方法
alert table xxx engine innodb;
//或
optimize table xxx;
复制代码
Innodb 表要尽可能本身指定主键,若是有几个列都是惟一的,要选择最常做为访问条件的列做为主键,另外,Innodb 表的普通索引都会保存主键的键值,因此主键要尽量选择较短的数据类型,能够有效的减小索引的磁盘占用,提升索引的缓存效果。
为何使用数据索引能提升效率
- 数据索引的存储是有序的
- 在有序的状况下,经过索引查询一个数据是无需遍历索引记录的
- 极端状况下,数据索引的查询效率为二分法查询效率,趋近于 log2(N)
mysql联合索引
- 联合索引是两个或更多个列上的索引。对于联合索引:Mysql从左到右的使用索引中的字段,一个查询能够只使用索引中的一部份,但只能是最左侧部分。例如索引是key index (a,b,c). 能够支持a、a,b、a,b,c3种组合进行查找,但不支持b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效
- 利用索引中的附加列,您能够缩小搜索的范围,但使用一个具备两列的索引 不一样于使用两个单独的索引。复合索引的结构与电话簿相似,人名由姓和名构成,电话簿首先按姓氏对进行排序,而后按名字对有相同姓氏的人进行排序。若是您知道姓,电话簿将很是有用;若是您知道姓和名,电话簿则更为有用,但若是您只知道名不姓,电话簿将没有用处
什么状况下应不建或少建索引
- 表记录太少
- 常常插入、删除、修改的表
- 数据重复且分布平均的表字段
- 常常和主字段一块查询但主字段索引值比较多的表字段
MySQL分区
四种隔离级别
- Serializable (串行化):可避免脏读、不可重复读、幻读的发生
- Repeatable read (可重复读):可避免脏读、不可重复读的发生
- Read committed (读已提交):可避免脏读的发生
- Read uncommitted (读未提交):最低级别,任何状况都没法保证
MySQL优化
- 开启查询缓存,优化查询
- explain你的select查询,分析你的查询语句或是表结构的性能瓶颈
- 当只要一行数据时使用limit 1,MySQL数据库引擎会在找到一条数据后中止搜索,而不是继续日后查少下一条符合记录的数据
- 为搜索字段建索引
- 垂直分表,选择正确的存储引擎
explain性能分析
+----+-------------+-------+------------+------+---------------+-----+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-----+---------+------+------+----------+-------+
id:select 查询的序列号,包含一组能够重复的数字,表示查询中执行sql语句的顺序。id不一样时,值大的优先执行;
相同时,执行顺序由上而下
select_type:查询类型,主要是用于区别普通查询,联合查询,嵌套的复杂查询。
simple:简单的select 查询,查询中不包含子查询或者union
primary:查询中若包含任何复杂的子查询,最外层查询则被标记为primary
subquery:在select或where 列表中包含了子查询
derived:在from列表中包含的子查询被标记为derived(衍生)MySQL会递归执行这些子查询,把结果放在临时表里
union:若第二个select出如今union以后,则被标记为union,若union包含在from子句的子查询中,外层select将
被标记为:derived
union result:从union表获取结果的select
partitions:表所使用的分区,若是要统计十年公司订单的金额,能够把数据分为十个区,每年表明一个区。
这样能够大大的提升查询效率
* type:链接类型,共有8个级别。性能从最优到最差的排序 system > const > eq_ref > ref > range > index > all
all:(full table scan)全表扫描无疑是最差,如果百万千万级数据量,全表扫描会很是慢。
index:(full index scan)全索引文件扫描比all好不少,毕竟从索引树中找数据,比从全表中找数据要快。
range:只检索给定范围的行,使用索引来匹配行。范围缩小了,固然比全表扫描和全索引文件扫描要快。
sql语句中通常会有between,in,>,< 等查询。
ref:非惟一性索引扫描,本质上也是一种索引访问,返回全部匹配某个单独值的行。好比查询公司全部属于研发团队
的同事,匹配的结果是多个并不是惟一值。
eq_ref:惟一性索引扫描,对于每一个索引键,表中有一条记录与之匹配。好比查询公司的CEO,匹配的结果只多是
一条记录,
const:表示经过索引一次就能够找到,const用于比较primary key 或者unique索引。由于只匹配一行数据,因此很快,
若将主键至于where列表中,MySQL就能将该查询转换为一个常量。
system:表只有一条记录(等于系统表),这是const类型的特列,平时不会出现,了解便可
* possible_keys:显示查询语句可能用到的索引(一个或多个或为null),不必定被查询实际使用。仅供参考使用
* key:显示查询语句实际使用的索引。若为null,则表示没有使用索引
* key_len:显示索引中使用的字节数,可经过key_len计算查询中使用的索引长度
* ref:显示索引的哪一列或常量被用于查找索引列上的值
* rows:根据表统计信息及索引选用状况,大体估算出找到所需的记录所须要读取的行数,值越大越很差
* filtered:一个百分比的值,和rows列的值一块儿使用,能够估计出查询执行计划(QEP)中的前一个表的结果集,从而肯定
join操做的循环次数。小表驱动大表,减轻链接的次数
* extra
Using filesort: 说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中没法利
用索引完成的排序操做称为“文件排序” 。出现这个就要马上优化sql。
Using temporary: 使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和
分组查询 group by。 出现这个更要马上优化sql。
Using index: 表示相应的select 操做中使用了覆盖索引(Covering index),避免访问了表的数据行,效果不错!
若是同时出现Using where,代表索引被用来执行索引键值的查找。若是没有同时出现Using where
,表示索引用来读取数据而非执行查找动做。覆盖索引(Covering Index) :也叫索引覆盖,就是select
的数据列只用从索引中就可以取得,没必要读取数据行,MySQL能够利用索引返回select列表中的字段,而没必要根据索引再
次读取数据文件。
Using index condition: 在5.6版本后加入的新特性,优化器会在索引存在的状况下,经过符合RANGE范围的条数 和
总数的比例来选择是使用索引仍是进行全表遍历。
Using where: 代表使用了where 过滤
Using join buffer: 代表使用了链接缓存
impossible where: where 语句的值老是false,不可用,不能用来获取任何元素
distinct: 优化distinct操做,在找到第一匹配的元组后即中止找一样值的动做
复制代码
IOC容器
SpringMVC处理请求的完整过程
源码相关
// bean建立以前修改bean的定义属性,例如修改scope值从singleton改成prototype
// 注意:不要在BeanFactoryPostProcessor进行可能触发bean实例化的操做
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1);
}
复制代码
// bean的初始化时,设置了属性以后回调
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
复制代码
// bean实例化以后,在执行bean的初始化方法先后,添加一些本身的处理逻辑
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object var1, String var2);
Object postProcessAfterInitialization(Object var1, String var2);
}
复制代码
// 实现类获取Spring的应用上下文ApplicationContext
public interface ApplicationContextAware extends Aware {
void setApplicationContext(ApplicationContext var1);
}
复制代码
// 实现类获取BeanFactory
public interface BeanFactoryAware extends Aware {
void setBeanFactory(BeanFactory var1) throws BeansException;
}
复制代码
// 实现注册事件
public abstract class ApplicationEvent extends EventObject {
private final long timestamp = System.currentTimeMillis();
public ApplicationEvent(Object source) {
super(source);
}
public final long getTimestamp() {
return this.timestamp;
}
}
// 实现类获取ApplicationEventPublisher发布事件
public interface ApplicationEventPublisherAware extends Aware {
void setApplicationEventPublisher(ApplicationEventPublisher var1);
}
// 实现类对发布的事件监听
public interface ApplicationListener<E extends ApplicationEvent>
extends EventListener
{
void onApplicationEvent(E var1);
}
复制代码
参考文章:
Mybatis 是如何执行 sql 语句的
JDBC 获取数据库链接的大概过程
PreparedStatement 和 Statement 区别
MyBatis的Api接口
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean var1);
SqlSession openSession(Connection var1);
SqlSession openSession(TransactionIsolationLevel var1);
SqlSession openSession(ExecutorType var1);
SqlSession openSession(ExecutorType var1, boolean var2);
SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);
SqlSession openSession(ExecutorType var1, Connection var2);
Configuration getConfiguration();
}
复制代码
public interface SqlSession extends Closeable {
<T> T selectOne(String var1);
<T> T selectOne(String var1, Object var2);
<E> List<E> selectList(String var1);
<E> List<E> selectList(String var1, Object var2);
<E> List<E> selectList(String var1, Object var2, RowBounds var3);
<K, V> Map<K, V> selectMap(String var1, String var2);
<K, V> Map<K, V> selectMap(String var1, Object var2, String var3);
<K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);
<T> Cursor<T> selectCursor(String var1);
<T> Cursor<T> selectCursor(String var1, Object var2);
<T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);
void select(String var1, Object var2, ResultHandler var3);
void select(String var1, ResultHandler var2);
void select(String var1, Object var2, RowBounds var3, ResultHandler var4);
int insert(String var1);
int insert(String var1, Object var2);
int update(String var1);
int update(String var1, Object var2);
int delete(String var1);
int delete(String var1, Object var2);
void commit();
void commit(boolean var1);
void rollback();
void rollback(boolean var1);
List<BatchResult> flushStatements();
void close();
void clearCache();
Configuration getConfiguration();
<T> T getMapper(Class<T> var1);
Connection getConnection();
}
复制代码
单线程:指的是网络请求模块使用了一个线程(因此不需考虑并发安全性),即一个线程处理全部网络请求,其余模块仍用了多个线程
redis可以快速执行缘由
Redis的 5 种基础数据结构
String(字符串):将用户信息结构体使用 JSON 序列化成字符串,而后将序列化后的字符串塞进 Redis 来缓存。一样,取用户信息会通过一次反序列化的过程
Redis 的字符串是动态字符串,是能够修改的字符串,内部结构实现上相似于 Java 的 ArrayList,采用预分配冗余空间的方式来减小内存的频繁分配,如图中所示,内部为当前字符串实际分配的空间 capacity 通常要高于实际字符串长度 len。当字符串长度小于 1M 时,扩容都是加倍现有的空间,若是超过 1M,扩容时一次只会多扩 1M 的空间。须要注意的是字符串最大长度为 512M。
# (批量)设置值
set key value
setnx key value
mset key1 value1 key2 value2
# 获取值
get key
mget key1 key2
# 判断值是否存在
exists key
# 删除值
del key
# 设置key设置过时时间,到点自动删除
expire key seconds
setex key seconds value
# value值是整数,可计数。自增范围是signed long的最大值和最小值
incr numberKey
incrby numberKey stepLenth
复制代码
List(列表):至关于LinkedList,底层实现的数据结构是快速列表,数据结构是链表而不是数组。插入和删除操做很是快,时间复杂度为 O(1),可是索引定位很慢,时间复杂度为 O(n)
底层存储快速链表: 首先在列表元素较少的状况下会使用一块连续的内存存储,这个结构是
ziplist
,也便是压缩列表。它将全部的元素紧挨着一块儿存储,分配的是一块连续的内存。当数据量比较多的时候才会改为quicklist
。由于普通的链表须要的附加指针空间太大,会比较浪费空间,并且会加剧内存的碎片化。好比这个列表里存的只是int
类型的数据,结构上还须要两个额外的指针prev
和next
。因此 Redis 将链表和ziplist
结合起来组成了quicklist
。也就是将多个ziplist
使用双向指针串起来使用。这样既知足了快速的插入删除性能,又不会出现太大的空间冗余
# 将值value插入到列表key的表尾(最右边)
rpush key value1 value2 value3
# 将值value插入到列表key的表头(最左边)
lpush key value1 value2 value3
# 返回列表key的长度
llen key
# 移除并返回列表 key 的头元素
lpop key
# 移除并返回列表 key 的尾元素
rpop key
复制代码
hash (字典):至关于HashMap
,它是无序字典。内部实现是数组+链表二维结构,与HashMap
不一样的是rehash
的方式不同,由于Java
的 HashMap
在字典很大时,rehash
是个耗时的操做,须要一次性所有rehash
。Redis为了高性能,不能堵塞服务,因此采用了渐进式rehash
策略。
渐进式
rehash
: 在rehash
的同时,保留新旧两个hash
结构,查询时会同时查询两个hash
结构,而后在后续的定时任务中以及hash
操做指令中,按部就班地将旧hash
的内容一点点迁移到新的hash
结构中。当搬迁完成了,就会使用新的hash
结构取而代之
# 将哈希表 key 中的域 field 的值设为 value
hset key field value
hmset key field value [field value ...]
# 返回哈希表 key 中,全部的域和值
hgetall key
# 返回哈希表 key 中域的数量
hlen key
# 返回哈希表 key 中给定域 field 的值
hget key field
hmget key field [field ...]
复制代码
set (集合):至关于HashSet
,它内部的键值对是无序的惟一的。它的内部实现至关于一个特殊的字典,字典中全部的value
都是一个值NULL
# 将元素加入到集合key当中
sadd key member [member ...]
# 判断 member 元素是否集合 key 的成员
sismember key member
# 返回集合 key 的基数(集合中元素的数量)
scard key
# 移除并返回集合中的一个随机元素
spop key
复制代码
zset(有序列表):至关于 SortedSet
和HashMap
的结合体,一方面它是一个set
,保证了内部value
的惟一性,另外一方面它能够给每一个 value
赋予一个score
,表明这个value
的排序权重。它的内部实现用的是一种叫作「跳跃列表」的数据
跳跃列表:
# 将member元素及其score值加入到有序集key当中
zadd key score1 member1 score2 member2
# 返回有序集key中(按score值从小到大),指定区间内的成员(位置数表示倒数第n位)
zrange key start stop [withscores]
# 返回有序集 key(按score值从大到小) 中,指定区间内的成员
zrevrange key start stop [withscores]
# 返回有序集 key 的数量
zcard key
# 返回有序集 key 中,成员 member 的 score 值
zscore key member
# 返回有序集 key 中成员 member 的排名(按 score 值递增)
zrank key member
# 返回有序集key中,全部score值介于min和max之间(包括等于 min 或 max )的成员(按 score 值递增)
zrangebyscore key min max [withscores] [limit offset count]
# 移除有序集 key 中的一个或多个成员
zrem key member1 member2...
复制代码
list/set/hash/zset
这四种数据结构是容器型数据结构,共享规则:a. 若是容器不存在,那就建立一个,再进行操做;b. 若是容器里元素没有了,数据结构自动删除,内存被回收
Redis分布式锁
# 只在键不存在时,才对键进行设置操做,同时设置过时时间
> set lock:lockkey true ex 5 nx
...处理逻辑...
> del lock:lockkey
# 结合ThreadLocal实现锁的可重入性
private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<>();
private Map<String, Integer> currentLockers() {
Map<String, Integer> refs = lockers.get();
if (refs != null) {
return refs;
}
lockers.set(new HashMap<>());
return lockers.get();
}
public boolean lock(String key) {
Map<String, Integer> refs = currentLockers();
Integer refCnt = refs.get(key);
if (refCnt != null) {
refs.put(key, refCnt + 1);
return true;
}
boolean ok = this._lock(key);
if (!ok) {
return false;
}
refs.put(key, 1);
return true;
}
复制代码
redis队列思路
redis位图
位数组的顺序和字符的位顺序是相反的。应用场景距离:用户签到记录
# 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。value值能够是0也能够是1
setbit key offset value
# 计算给定字符串中,被设置为 1 的比特位的数量
bitcount key [start] [end]
# 返回string的二进制中第一个0或1的位置
bitpos key bit [start] [end]
#
bitfield key [get type offset] [set type offset value] [incrby type offset increment] [overflow wrap|sat|fail]
# 示例:hello的二进制表示为01101000(h) 01100101(e) 01101100(l) 01101100(l) 01101111(o),最左边的是第一位
> setbit s 1 1 (零存)
> setbit s 2 1
> setbit s 4 1
> setbit s 9 1
> setbit s 10 1
> setbit s 13 1
> setbit s 15 1
> get s # 输出结果为he(整取)
> get bit s 1(零取)
> set w h (整存)
> getbit w 2(零取)
> set w hello
> bitcount w # 21,返回字符串hello中1的个数
> bitcount w 0 1 # 7,返回字符串hello前两个字符中1的个数
> bitpos w 0 # 0,返回字符串hello中的第一个0位
> bitpos w 1 # 1,返回字符串hello中的第一个1位
> bitpos w 1 1 1 # 9,字符串hello从第二个字符算起,第一个1位
> bitpos w 1 2 2 # 17,字符串hello从第三个字符算起,第一个1位
复制代码
HyperLogLog统计
提供不精确的去重计数方案,虽然不精确可是也不是很是不精确,标准偏差是0.81%。应用距离:记录用户一天以内每一个网页天天的
UV
数据(用户重复访问须要去重)
# 将任意数量的元素添加到指定的 HyperLogLog 里面
pfadd key element [element ...]
# 返回储存在给定键的 HyperLogLog 的近似基数,多个键返回并集的近似基数
pfcount key [key ...]
# 将多个 HyperLogLog 合并(merge)为一个 HyperLogLog
pfmerge destkey sourcekey [sourcekey ...]
> pfadd codehole user1
> pfcount codehole
> pfadd codehole user1 user2 user3
> pfcount codehole # 结果 3
复制代码
redis布隆过滤器(4.0新特性)
布隆过滤器是一个不怎么精确的
set
结构,会有小小的误判率,例如当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就确定不存在。使用场景:客户端新闻推荐系统不重复推送
原理:向布隆过滤器中添加key时,会使用多个hash函数对key进行hash算得一个整数索引值而后对位数组长度进行取模运算获得一个位置,每一个hash函数都会算得一个不一样的位置。再把位数组的这几个位置都置为1就完成了add。key 是否存在时,跟 add 同样,也会把 hash 的几个位置都算出来,看看位数组中这几个位置是否都位 1,只要有一个位为 0,那么说明布隆过滤器中这个 key 不存在
空间占用估计:布隆过滤器有两个参数,第一个是预计元素的数量 n,第二个是错误率f。公式根据这两个输入获得两个输出,第一个输出是位数组的长度 l,也就是须要的存储空间大小 (bit),第二个输出是 hash 函数的最佳数量 k。hash 函数的数量也会直接影响到错误率,最佳的数量会有最低的错误率
k=0.7*(l/n) # 约等于
f=0.6185^(l/n) # ^ 表示次方计算,也就是 math.pow
结论:
1. 位数组相对越长 (l/n),错误率 f 越低,这个和直观上理解是一致的
2. 位数组相对越长 (l/n),hash 函数须要的最佳数量也越多,影响计算效率
3. 当一个元素平均须要 1 个字节 (8bit) 的指纹空间时 (l/n=8),错误率大约为 2%
4. 错误率为 10%,一个元素须要的平均指纹空间为 4.792 个 bit,大约为 5bit
5. 错误率为 1%,一个元素须要的平均指纹空间为 9.585 个 bit,大约为 10bit
6. 错误率为 0.1%,一个元素须要的平均指纹空间为 14.377 个 bit,大约为 15bit
复制代码
> bf.add codehole user1 # 添加单个元素
> bf.madd codehole user2 user3 user4 # 批量添加元素
> bf.exists codehole user1 # 查询元素是否存在
> f.mexists codehole user4 user5 # 一次查询多个元素是否存在
复制代码
简单限流
原理:zset数据结构的key记录用户的行为历史,value用毫秒时间戳,经过滑动窗口与阈值max_count进行比较就能够得出当前的行为是否容许
public class SimpleRateLimiter {
private Jedis jedis;
public SimpleRateLimiter(Jedis jedis) {
this.jedis = jedis;
}
public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) {
String key = String.format("hist:%s:%s", userId, actionKey);
long nowTs = System.currentTimeMillis();
Pipeline pipe = jedis.pipelined();
pipe.multi();
pipe.zadd(key, nowTs, "" + nowTs);
pipe.zremrangeByScore(key, 0, nowTs - period * 1000);
Response<Long> count = pipe.zcard(key);
pipe.expire(key, period + 1);
pipe.exec();
pipe.close();
return count.get() <= maxCount;
}
}
复制代码
漏斗限流
public class FunnelRateLimiter {
static class Funnel {
int capacity;
float leakingRate;
int leftQuota;
long leakingTs;
public Funnel(int capacity, float leakingRate) {
this.capacity = capacity; # 漏斗容量
this.leakingRate = leakingRate; # 漏嘴流水速率
this.leftQuota = capacity; # 漏斗剩余空间
this.leakingTs = System.currentTimeMillis();# 上一次漏水时间
}
void makeSpace() {
long nowTs = System.currentTimeMillis();
long deltaTs = nowTs - leakingTs; # 距离上一次漏水过去了多久
int deltaQuota = (int) (deltaTs * leakingRate);# 根据时间差计算出能够腾出的空间
if (deltaQuota < 0) { # 间隔时间太长,整数数字过大溢出
this.leftQuota = capacity;
this.leakingTs = nowTs;
return;
}
if (deltaQuota < 1) { # 腾出空间过小,最小单位是1
return;
}
this.leftQuota += deltaQuota; # 增长漏水容量
this.leakingTs = nowTs; # 重置漏水时间
if (this.leftQuota > this.capacity) {
this.leftQuota = this.capacity;
}
}
boolean watering(int quota) {
makeSpace();
if (this.leftQuota >= quota) { # 判断剩余空间是否足够
this.leftQuota -= quota;
return true;
}
return false;
}
}
private Map<String, Funnel> funnels = new HashMap<>();
public boolean isActionAllowed(String userId, String actionKey, int capacity, float leakingRate) {
String key = String.format("%s:%s", userId, actionKey);
Funnel funnel = funnels.get(key);
if (funnel == null) {
funnel = new Funnel(capacity, leakingRate);
funnels.put(key, funnel);
}
return funnel.watering(1); // 须要1个quota
}
}
复制代码
# 建立quota个容量为capacity,漏水速率leakingRate,阈值为quota的名称未key的漏斗
cl.throttle key capacity, leakingRate quota
> cl.throttle laoqian:reply 15 30 60
1) (integer) 0 # 0 表示容许,1表示拒绝
2) (integer) 15 # 漏斗容量capacity
3) (integer) 14 # 漏斗剩余空间left_quota
4) (integer) -1 # 若是拒绝了,须要多长时间后再试(漏斗有空间了,单位秒)
5) (integer) 2 # 多长时间后,漏斗彻底空出来(left_quota==capacity,单位秒)
复制代码
Redis主从复制
过程原理:
- 当从库和主库创建MS关系后,会向主数据库发送SYNC命令
- 主库接收到SYNC命令后会开始在后台保存快照(RDB持久化过程),并将期间接收到的写命令缓存起来
- 当快照完成后,主Redis会将快照文件和全部缓存的写命令发送给从Redis
- 从Redis接收到后,会载入快照文件而且执行收到的缓存的命令以后,主Redis每当接收到写命令时就会将命令发送从Redis,从而保证数据的一致
redis两种持久化方式的优缺点