ConcurrentHashMap
- 在JDK1.8中的数据结构以及如何实现线程安全
- 1.8之前ConcurrentHashMap采用的是分段(segment)锁机制
- 1.8开始ConcurrentHashMap的数据结构与1.8中HashMap的数据结构一致,都是数组+链表+红黑树
- 如何实现线程安全: CAS+synchronized
- 什么是CAS机制:
- 就是执行写操作的的时候,先获取版本号,提交的时候再次获取版本号,两次版本号一致则提交成功,否则自旋,超出时间返回失败
- 若桶位置为空则采用CAS机制
- 其中一条线程会先获该桶位的取版本号,插入提交的时候,(再次获取版本号)若发现版本号一致,则成功
- 另一条线程也会获取版本号,提交的时候,(再次获取版本号)发现两次获取的版本号不一致,自旋
- 若桶的位置不为空则
- 多线程并发,谁先获取桶位的头节点,谁就先给头节点加锁,后获取的想进来加锁,发现有锁,进入阻塞等待
- 这样的情况下,相比较于JDK1.7的给segment加锁,这里效率更高,因为是直接给桶位加锁,假设线程B操作2桶位,和线程A操作的1桶位,是并发执行的
- 即:只要位置不同,完全不受影响,
- 并且桶位相同的情况下,只有桶位不为null才会加锁
- 什么是CAS机制:
ConcurrentHashMap实现线程安全的机制:CAS+synchronized
eg:2线程并发执行添加元素操作,且2个线程添加的元素均要向同一散列桶添加
-若桶位置为null,此时采用CAS保证线程安全.
-线程1向下标1位置插入key-value, 此时采用CAS,
-线程2并发向下标1位置插入key-value, 此时采用CAS,
在插入操作时若版本号不一致,则某线程自旋,从而确保线程安全.
-若桶位置不为null,则线程会给该位置的头节点加锁
-假设线程1先访问,则给头节点加上锁,
-此时线程2也想给头节点加锁,只能阻塞
-线程1加上锁,则线程1先根据key进行equals比较,
- 若true,则替换value
- 若false,则在链表或红黑树中添加新的Node节点
索引(Index),一张表可以创建多个主键
- 索引的定义:
- 为该列数据进行排序,继而生成"目录".
- 索引的作用:
- 索引是用来提高查询效率的 -- 当数据量大时,效果显著
- 索引提高查询效率原理:
- 索引类似于对数据创建了目录
- 索引是如何创建目录的?以及创建好的目录中保存的是什么?
- Mysql的存储引擎是Innodb,会使用B+Tree(数据结构)来为索引创建目录
- 数据结构:BTree:多叉平衡查找树(新增的时候实现了排序) B:balance
- Degree度:BTree的度是自定义的:就是有几个分叉,如:二叉树的度就是2,一个节点可以有两个分叉
- 它会分裂提取,如果目前度的数是5,则每个数据块保存的元素个数小于等于n-1的
- 也就是说,一个元素块最多保存4个,如果此时再来一个元素,那么进灰进行分裂提取
- 分裂提取的做法是:将最中间的数据提到上一层去,上一层就会出现数据块(保存提取的值),原来的数据块就会分裂成两个数据块,(左侧数据块都大于父节点的值,右侧数据块都小于父节点的值)
- 提取出的元素前后会有两个指向
- 即:每个元素的前后都会有前驱指针和后继指针
- 上图的BTree 的度为5,已经添加了4个元素,此时再添加一个元素就会分裂 ,如下图:
- 如果此时再添加元素 比如添加 7 ,跟二叉树类似,会先和父节点比大小,如果小,到左侧,左侧有数据块,再次比大小(遍历),比8小,就会添加到8的左侧,再来个6同理,如果再次添加,数据块的节点数即将超过n-1 那么就会提取中间节点,到它的父节点上再次比较
- 如下图
- 当添加元素8的时候,就会分裂
- 也就是说,当我们要查找数据的时候,查找的是对磁盘的IO操作,数据块会保存到内存里,我们在数据块里查找元素,是在内存的操作,查找目录是磁盘操作,然后将数据块保存到内存里的
B+Tree是BTree的变种
- B+Tree与BTree的区别:
- 1、叶子节点数据块之间有指针指向,目的是为了提高查找某区间范围数据的效率
- 例如:查找11-15之间的所有数字,此时只需找到11,则可以根据指向找到后续的内
- 2、分裂提取时,被提取的数依然存在子数据块中,而且往上提的元素保存在偏大的子块里
- 1、叶子节点数据块之间有指针指向,目的是为了提高查找某区间范围数据的效率
Mysql中索引的实现:使用B+Tree数据结构,并 在B+Tree基础上进行了优化
- 索引是干什么的?
- 索引会给某一列(字段)添加,目的要为该列的所有数据进行排序,并生成目录,便于查找。
- id是主键,唯一的(并且是整数,且自增的,根据主键生成索引是有顺序的),根据主键生成索引是最优的选择
- 如果主键不是自增的,且不是整数而是随机生成的,那么每次新增的时候都得重新排序,效率大大降低
聚集索引:
- mysql的Innodb 会自动的 为每张表的主键添加索引,所以若数据库的存储引擎是Innodb,则每张表必须要有主键
- 默认给主键添加的索引,叫做聚集索引/主索引
- 若创建的表没有主键
- 此时先找到有unique约束的列,自动给该列添加索引.
- 若创建的表中没有unique约束的字段
- 此时会为表添加隐式的主键,长度为6,类型为long
叶子节点为 key+data,下图是简略图
- key:表示的是添加索引那列的数据
- 如果添加索引的列是id 那么该列的数据值就是id,如果索引列是name,那么该列的数据值就是name 的值
- data:在Innodb引擎里,保存的是真正的数据
非叶子节点为:key
- 即:只有索引的值,而没有保存真实的
聚集索引:对主键添加的索引,叶子节点保存的是key+data,而且data保存的是真实数据,又叫主索引
如果添加条件这一列,根据where条件 查询条件时非常高的
比如说读2,第一次io操作,比12小 去左侧,找到左侧后,在找到你要去哪一块去找
找到区域之后,找到数据块,数据块里找的是谁,一下就找到了,而且直接找到的是数据
非聚集索引/辅助索引
- 给非主键列添加的索引,即为非聚集索引
- 聚集索引与非聚集索引的叶子节点中保存的数据不同
- 聚集索引叶子节点中保存的为
- key(索引)-data(真实的数据)
- 非聚集索引叶子节点中保存的为
- key(索引)-data(主键),之后再根据找到的主键调用聚集索引查到最终的数据
- 我们创建索引(对name添加索引),
- 比如说当我们要对name进行查询的时候,给name添加索引
- 1、数据库会对所有的数据进行排序生成目录结构
- 2、生成目录结构后,key保存的就是索引(name的值),data保存的就是主键id
- 它查找的方式是,根据索引的名字 找到 主键id,然后根据主键id去聚集索引里去找data
- data为真是数据,就找到了
- 创建索引的sql:
- create index 索引名字 on 表(表字段);
- 删除索引的sql
- drop index 索引名字;
create index index_name on table(col);
删除索引:
drop index index_name;
- 什么时候使用索引,什么时候不要使用索引:
- 若表中的数据量不大,不建议使用索引,因为添加索引tree会排序,也会占用时间和内存
- 若某列的数据值会被频繁改动,不要给这一列添加索引(如金额等),因为值修改之后,对应的索引会更新(添加索引是需要排序的)
- 通常会为where后的某个常用的查询条件的字段添加索引
- 索引失效的情形:
- 若添加索引的字段涉及到运算,此时索引会失效.
- eg:
- age添加索引 where age+1>x,age上的索引失效
- 添加索引的字段使用函数也是失效
- 若使用左侧模糊查询 (like '%a'),索引会失效,肯定会失效的那就挨个找了
- 右侧模糊查询正常使用
- 若添加索引的字段涉及到运算,此时索引会失效.
- 在查询条件部分对添加索引的字段使用not in,索引会失效
- 而in 相当于 指定索引了,而使用not in 相当于范围查找了
- 当数据量大的时候,查询如何优化
- 添加索引,避免使用索引失效的情形
select..from table where id in(1,3,5)-索引有效
select..from..where age not in(10,11)-索引失效
sql语句中各部分的执行顺序
- 一旦on出现,前边的就会出现一次联查,
- 如果再出现on 那么就会拿之前的那张虚拟表和后边的表再进行一次联查,这种情况下,一种执行了2次IO读取操作
- 也就是说一次on就会产生一次联查
笛卡尔积:
- 左表中的每条数据均与右表中的每条数据进行匹配并生成结果
- 最终生成的结果数为:m*n
- m:A标的数据量
- n:B表中的数据量
- from后这时候会产生表的笛卡尔积效果
- 1、from t1 join t2,此时会先产生t1,和t2的笛卡尔积,结果集是一张表是虚拟表(所有数据的拼接,能拼接起来的)V1
- 2、on 确定连接条件,是从刚才的笛卡尔积里面筛出的一些数据 V2(如下图的笛卡尔积筛选),然后可以把它当做一个新的虚拟表
- where 继续筛选得到V3
- group by 得到 v4
- having
多表查询的时候,这种效率最高,因为只出现了一次on 说明只执行了一次
IO操作
select * from t1 join t1 join t3 join t4...on t1.xx = t2.xx and t2.xx=t3.xx and..
这种情况是:出现了两次on,说明执行了两次IO操作
// 应用的场景:要求t1,t2先出结果集,拿此结果集与t3再次联查
select * from t1 join t2
on // 出现一次IO查询,
t1.xx = t2.xx
join t3
on // 拿t1 与 t2的结果集与t3再次进行IO查询
...
select distinct col1,col2..from t1 join t2 on t1.xx=t2.xx
where ...group by ...having....order by...limit...
select * from emp e right join dept d on e.id = d.id
多表执行顺序:
1. from 产生笛卡尔积虚拟表v1 无论什么连接,先执行交叉连接
2. on 确定连接条件,则可以从上述的v1中筛出数据
-虚拟表v2
3. left/right join
-在v2基础上将主表中独有的数据添加到v2中,
另一个表因为无匹配数据,以null填充.产生v3
4. where - 在v3基础上再次筛选条件,得到虚拟表v4
5. group by - 在v4基础上进行分组产生虚拟表v5
6. having - 在v5的基础上再次进行数据筛选得出v6
7. select - 在v6的基础上查询结果得出虚拟表v7
8. distinct - 去重得出虚拟表v8
9. order by - 基础v8进行排序得到虚拟表v9
10. limit - 基于v9是筛选出想要的部分数据
limit m,n
limit x; 筛选出前x条数据
注意点:
select ..from t1 join t2 on t1.xx=t2.xx join t3 on.. --- 应用的场景:要求t1,t2先出结果集,拿此结果集与t3再次联查.
面试题:
1.请描述servlet的生命周期 - 涉及到的方法
servlet的作用是用来接收和响应请求
1. 默认情况下,当请求第一次到达servlet时,该servlet进行实例化
2. 紧接着会调用init()方法进行初始化
3. 客户端每次向该servlet发起请求,均调用service()来进行处理
4. 在实例销毁前调用destroy()方法,通常用于做最后的资源释放.
serlvet内部的方法:
构造方法
init()
service() -- 用来处理请求
doGet()/doPost() -- 内部都调用了service()
destroy() -- servlet实例在销毁前会调用的方法
Navicat使用
连库、创建数据库、创建表
- 连库
- 连接名随便起,自己定义的名字
- 创建一个数据库
- 字符集用utf8mb4 是4个自己,包含的更多
- utf8 默认是utf8mb3 即 3个字节,我们不用
- 排序规则用ci这个
- 字符集用utf8mb4 是4个自己,包含的更多
- 创建字段,可以添加主键
- 添加自增等操作