集合
collection爷接口
list父类接口【数据有下标,有序,可重复】
ArraysList子类
LinkedList子类受
set父类接口【数据无下标,无序,不可重复】
HashSet子类
Map父类接口【键值对的方式存数据】(不是collection的子类)
HashMap子类
1.介绍ArraysList
Arraylist底层使用的是数组(查询数据效率高,插入删除特定位置效率低),存储引用类型
容量可变的非同步,非线程安全的列表,
扩容机制:JDK1.7的时候是初始化就创建一个容量为10的数组,add调用
1.8后是初始化先创建一个空数组,第一次add时才扩容为10,再次调用add方法,先将集合扩大1.5倍,
实现线程安全
List<Map<String,Object>> data=Collections.synchronizedList(new ArrayList<Map<String,Object>>())
2.介绍LinkedList
LinkedList底层使用的是双向循环链表数据结构(插入,删除效率特别高,查询慢)
非同步,线程不安全;
~可以被当作堆栈、队列进行操作
单链表:每个节点指针的指向都是指向自身结点的下一个结点,最后一个结点的head指向为null
双链表:首结点的head指向为null,尾指针tail指向下一个节点的tail;尾结点的head指向前一个结点的head,tail 指向为null,是双向的关系;
3.线程安全的List
- 使用Vector容器 和ArrayList不同的只是它采用了同步关键词synchronized修饰方法。
- 使用Collections的静态方法synchronizedList(List< T> list) ,它采用了同步代码块实现线程间的同步
- 采用CopyOnWriteArrayList容器 ,通过lock来实现线程间的同步的,读写操作互斥,最佳选择
堵塞队列
队列: 先进先出,
堵塞:
当队列为空时,消费者挂起,队列已满时,生产者挂起,这就是生产-消费者模型,堵塞其实就是将线程挂起。让速度快的暂时堵塞,
如何实现
java5增加了concurrent包,concurrent包中的BlockingQueue接口就是堵塞队列,
4.hashMap
无序,键不允许重复,重复会覆盖原来的值
-
HashMap的结构是数组+链表 或者 数组+红黑树 的形式,链表的长度>8时,同时还满足容量大于或等于 MIN_TREEIFY_CAPACITY(默认为 64)的要求,链表会转为红黑树,当链表的长度<6时,会重新恢复成链表
-
key,value键值对结构存放数据,无序,初始容量是16,默认的加载因子是0.75
-
数组被分为一个个桶(bucket),每个桶存储有一个或多个Entry对象,Entry对象:key(键)、value(值),next(指向下一个Entry)
-
**put增加数据原理:**当存放数据时,会根据hash(key)%n算法来计算数据的存放位置,n就是数组的长度,如果该数组在该位置上已经存放了其他元素,利用equals方法,找到是否Key相同,相同则覆盖原来的value值,不同的话,将以链表的形式存放,新加入的Entry 对象放在链头,如果数组中该位置没有元素,就直接将该元素放到数组的该位置上
HashMap 的缺点
当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。
单向链表查询的时间复杂度是O(n):
因为链表的每个节点的内存地址仅存放在前一个节点中(头节点除外),也就是说如果我要获取当前节点的内存地址,就必须先获取前一个节点的内存地址,依次类推,知道头节点。所以查询内存地址慢
数组中对象的内存地址可以直接计算出来,而单向链表中对象的内存地址没法计算,只能是一个一个地查找。
HashMap的扩容机制
当一个map填满了75%的bucket时候,将会创建原来HashMap大小的2倍的bucket数组(jdk1.6,但不超过最大容量),来重新调整map的大小,并将原来的对象放入新的bucket数组中。重新rehashing,找到新的bucket位置。容量范围值:16-2^n个容量
ConcurrentHashMap(分段锁)
JDK1.7 中的 ConcurrentHashMap 是由 Segment
数组结构和 HashEntry
数组结构组成,即 ConcurrentHashMap 把哈希桶数组切分成小数组(Segment ),每个小数组有 n 个 HashEntry 组成。给小数组数据配一把锁,当一个线程占用锁访问其中一段数据时,其他段的数据也能被其他线程访问,实现了真正的并发访问。
JDK1.8 中的ConcurrentHashMap 选择了与 HashMap 相同的Node数组+链表+红黑树结构;在锁的实现,采用CAS + synchronized
实现更加细粒度的锁。锁住这个链表头节点(红黑树的根节点)
HashTable:(全表锁)
Hashtable通过使用synchronized修饰方法的方式来实现多线程同步,因此,Hashtable的同步会锁住整个数组。在高并发的情况下,性能会非常差
HashSet与HashMap区别
HashSet的底层是哈希表,HashSet存储对象,HashMap存储键值对。
向HashSet中存入数据时,相当于把数据作为K存入内部的HashMap中,其中K不允许重复,允许使用null.
5.红黑树
所谓二叉搜索树,放置规则是:任何节点的键值一定小于去其左子树中的每一个节点的键值,并大于其右子树的每一个节点的键值。
红黑树是一种结点带有颜色属性的二叉查找树,平衡二叉树
红黑树的形成有两个阶段:成为二叉搜索树和旋转变色。
- 每个节点,非黑即红。
- 根节点为黑。
- 不能存在连续的两个红节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
- 任何节点,至其下属的、不同的叶节点的每条路径上,黑节点数必须相等。
节点新增原理:
新插入节点默认是红色,如果是黑色的话那么当前分支上就会多出一个黑色节点出来,从而破坏了黑色平衡。
- 如果插入的是根节点,红色变黑色。
- 如果父节点为黑色,则直接插入,不需要变色。
- 如果父节点是红色,没有叔叔节点或者叔叔节点是黑色,则以爷爷节点为支点旋转,旋转之后原来的爷爷节点变红色,原来的父节点变黑色。
- 如果父节点为红色,叔叔节点也是红色(此种情況爷爷节点一定是黑色),则父节点和叔叔节点变黑色,爷爷节点变红色(如果爷爷节点是根节点,则再变成黑色),爷爷节点此时需要递归(把爷爷节点当做新插入的节点再次进行比较)。
左旋: 因为右子树高度高于左子树,对节点进行左旋操作,
(1)节点的右孩子替代此节点位置
(2)右孩子的左子树变为该节点的右子树
(3)节点本身变为右孩子的左子树
右旋: 因为左子树高度高于右子树,对节点进行右旋操作
(1)节点的左孩子代表此节点
(2)节点的左孩子的右子树变为节点的左子树
(3)将此节点作为左孩子节点的右子树。
6.TreeSet
TreeSet 是一个有序的并且没有重复元素"的集合,它是通过TreeMap实现的集合,TreeSet 继承于AbstractSet,支持一系列的导航方法,被克隆,支持序列化
TreeSet是非同步的。TreeSet实际上是TreeMap实现的。构造TreeSet时;若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
Collections.synchroinzedMap()/Set()来实现同步
TreeSet 中所有元素总是根据指定排序规则保持有序状态。treeMap底层采用一棵“红黑树”来保存集合中的 Entry(k,v),treeset相当于保存的是把存储对象以key方式保存在treeMap中
相同点:
TreeMap和TreeSet都是有序的集合,也就是说他们存储的值都是拍好序的。
TreeMap和TreeSet都是非同步集合,因此他们不能在多线程之间共享,不过可以使用方法Collections.synchroinzedMap()来实现同步
运行速度都要比Hash集合慢,他们内部对元素的操作时间复杂度为O(logN),而HashMap/HashSet则为O(1)。
不同点:
最主要的区别就是TreeSet和TreeMap非别实现Set和Map接口
TreeSet只存储一个对象,而TreeMap存储两个对象Key和Value(仅仅key对象有序)
TreeSet中不能有重复对象,而TreeMap中可以存在
MySql数据库
1.事务
1.事务就是将一堆的SQL语句(通常是增删改操作)绑定在一起执行,要么都执行成功,要么都执行失败,
即都执行成功才算成功,否则就会恢复到这堆SQL执行之前的状态。
2.事务4个特性ACID
- 原子性: 要么执行,要么不执行
- 一致性:事务前后,数据完整性没有被破坏,总额一致
- 隔离性:数据库允许多个并发事务同时对其数据进行操作,不会因交叉执行而导致数据的不一致。
- 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
3.隔离级别
-
读未提交: 事务B读取事务A还没有提交的数据,安全性最差,可能发生并发数据问题,性能最好
-
读提交(read committed)一个事务只能看见已经提交事务所做的改变。,orancle
-
可重复读:它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行,MySQL默认的隔离级别,安全性较好,性能一般
-
串行化(Serializable) 它是在每个读的数据行上加上共享锁。效率低下,安全性高,不能并发,解决幻读
引发的问题:
脏读:(读未提交)事务B读取事务A还没有提交的数据,事务A因某种原因回滚了,事务读取的数据不准确了
不可重复读:在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读:在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
4.存储引擎
InnoDB、MyISAM、Memory
InnoDB : MySql 默认引擎,
-
支持事务。默认的事务隔离级别为可重复读
-
锁粒度默认为行级锁, 也支持表锁,可以支持更高的并发
-
支持外键约束
-
缓冲管理,通过缓冲池,将索引和数据全部缓存起来,加快查询的速度,Innodb的索引和数据是紧密捆绑的
MyISAM :
- 不支持事务。不支持锁,在读写(Insert、select)效率上,要高于InnoDB不少
- MyISAM的索引和数据是分开的,并且索引是有压缩的
- 不支持外键约束
- 不支持数据缓存
- 写性能比InnoDB高
- 支持全文搜索
**Memory:**理解为,临时表
- 直接存在内存中 ,一旦服务器宕机,数据就会丢失;
- 不支持数据类型TEXT和BLOB类型
- 只支持表级锁。访问量比较大时,表级锁会成为MEMORY存储引擎的瓶颈
- 默认使用hash索引
5.索引类别(B+树索引、全文索引)、索引的原理
- 单值索引:一个索引只包括一个列,一个表可以有多个列
- 唯一索引:索引列的值必须唯一,但允许有空值;主键会自动创建唯一索引
- 复合索引:一个索引同时包括多列,有最左匹配原则,也称为最左特性
- 全文索引:将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,这部分重新组织的信息我们称之索引。然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。
- B+Tree索引:
6.平衡多路查找树(B-Tree)
B-Tree是为磁盘等外存储设备设计的一种平衡查找树。不可能全部存储在内存中,故要存储到磁盘上
系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。
一个二元组[key, data] ,key为记录的键值,对应表中的主键值,data为一行记录中除主键外的数据,key值互不相同,
结构: 每个节点为一个磁盘块(页),将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入,非叶子节点的页由 指针、key、data构成;指针=key的个数+1,且每个指针指向对应区域(满足二叉查找树)的子节点的页, 但是叶子节点的磁盘块没有指针指向,有key、data
一棵m阶的B-Tree有如下特性:
-
每个节点最多有m个孩子。
-
除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。
-
若根节点不是叶子节点,则至少有2个孩子
7.B+Tree
B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,叶子节点存储了所指向的指针的区域所有数据以及上一个key的数据
优势:与B-Tree结构相比,非叶子节点,去掉了data,如果data数据较大时,会导致树的深度较大,增大查询时的磁盘I/O次数,效率会慢很多,去掉了data,加大每个节点存储的key值数量,降低B+Tree的高度。
特点:
- 非叶子节点只存储键值信息。
- 所有叶子节点之间都有一个链指针。
- 数据记录都存放在叶子节点中。
8.索引失效?如何解决?
1.复合索引未遵循最左特性,索引会失效
2.在索引列上做任何操作(计算、函数、自动或手动类型转换,使用IS NULL和IS NOT NULL)。
解决方案:先查询所有订单记录的数据,再在程序中筛选。
3.条件查询的内容是字符串不加单引号查询,索引失效
4.like以通配符开头(’%abc…’)会导致索引失效。
5.or来连接时会索引失效。 少用
6.在where中对字段进行表达式和使用使用!=或< 或>操作 ,不用
9.优化数据库(看15)
- 对于查询语句,最重要的优化方式就是适当使用索引,书写高效率的SQL。
- 数据库的分表分库,读写分离等
- 数据库设计:使用合适的数据类型,使用合适的存储引擎
- 增加中间表,联合查询
- 允许部分数据冗余,尽量避免join查询,提高效率
2.explain执行计划,显示数据库引擎对于SQL语句的执行的详细情况
3.将字段很多的表分解成多个表,有些字段的使用频率很低,
9.优化sql语句
- 不要使用select *,而是具体字段
- 避免在where子句中使用or来连接条件,索引失效
- 使用varchar代替char (按数据内容实际长度存储,存储空间小,可以节省存储空间)
- 少用or来连接时会索引失效。
- 尽量like以通配符开头(’%abc…’)会导致索引失效
- 条件查询的内容是字符串要加单引号查询,否则索引失效,MySQL会做隐式的类型转换,把它们转换为数值类型再做比较
- where表之间的连接必须写在其他Where条件之前,那些可以过滤掉最大数量记录的条件必须写在Where子句的末尾.HAVING最后。
- 索引不适合建在有大量重复数据的字段上
- 优先使用inner join,返回数据少
- 避免在where子句中使用!=或<>操作符
10.sql语句中的函数
LOWER(‘ABC’) 转小写
upper(dname)转大写
length(dname)求长度
substr(dname,1,3) 截取[1,3]
concat(dname,‘123’)拼接数据
replace(dname,‘a’,‘666’)替换
ifnull(comm,10) 若为null,10替代
round四舍五入,ceil向上取整,floor向下取整
- numeric(5,2) decimal(5,2)—也可以表示小数,表示总共5位,其中可以有两位小数
limit 用法
select * from emp limit 2 --列出前两条
select * from emp limit 1,2 --从第二条开始,展示2条记录
select * from emp limit 0,3 --从第一条开始,展示3条记录--前三条
11.Limit分页 优化
当一个表数据有几百万的数据的时候成了问题!
原因本质: 1)limit语句的查询时间与起始记录(offset)的位置成正比
2)mysql的limit语句是很方便,但是对记录很多:百万,千万级别的表并不适合直接使用。
百万级别:
(1)id < max and limit size
#覆盖索引获取 id<=max max是当页降序后的第一条
select SQL_NO_CACHE * from order_table where company_id = 1 and mark =0 and id <= (select id from order_table where company_id = 1 and mark =0 order by id desc limit 200000 ,1) order by id desc limit 200000;
(2)覆盖索引 + join
select SQL_NO_CACHE p.* from order_table p join (select id from order_table where company_id = 1 and mark =0 order by id desc limit 200000 ,200000) a on a.id = p.id;
千万级别:
(1) id<max and limit size; #max为上一页的最小id
(2) 使用 min<=id<=max # max为上一页的最小id,mix为当页的最小id值
12.字段约束
主键约束 primary key auto_increment
非空约束 not null
唯一约束 unique
平均工资小于8000的部门
group by
having
select deptno, AVG(sal) from emp
group by deptno #按部门分组
having AVG(sal)<8000 #查询条件,类似where,但是group by只能配合having
datetime年月日时分秒,存储和显示是一样的
timestamp时间戳,存储的不是个日期,而是从1970年1月1日到指定日期的毫秒数
13.drop、delete和truncate之间的区别
drop删除库或者表、delete删除表的数据,自增值不会清零、truncate是自增主键会重头开始
14.多表联查
left join (左连接):返回包括左表中的所有记录和右表中连接字段相等的记录。
right join (右连接):返回包括右表中的所有记录和左表中连接字段相等的记录。
inner join (等值连接或者叫内连接):只返回两个表中连接字段相等的行。
full join (全外连接):返回左右表中所有的记录和左右表中连接字段相等的记录。
15.MySQL数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?
a. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。
b. 选择合适的表字段数据类型和存储引擎,适当的添加索引。
c. mysql库主从读写分离。
d. 找规律分表,减少单表中的数据量提高查询速度。
e。添加缓存机制,
f. 不经常改动的页面,生成静态页面。
g. 书写高效率的SQL。
16.三范式与反三范式
三范式:
1.原子性 2.记录的惟一性 3.字段没有冗余
优点: 减少数据冗余,使得更新快,体积小
缺点:查询需要多个表进行关联,减少写得效率增加读得效率,更难进行索引优化
反三范式:
优点:可以减少表得关联,可以更好得进行索引优化
缺点:数据冗余以及数据异常,数据得修改需要更多的成本
17.数据库的读写分离
通过设置主从数据库实现读写分离,主数据库负责“写操作”,从数据库负责“读操作”
怎么实现主从读写分离?
1.编写配置文件jdbc.propreties,配置master和slave 的数据库地址
2.在spring-dao.xml中配置数据源,主从数据库的数据源
3.配置类DataSourceSelector继承AbstractRoutingDataSource,用于动态选择配置数据源(现在实现这个Bean)并配置在spring-dao.xml中
4.写个拦截器DateSourceSelectInterceptor拦截所有的数据库操作请求,通过分析sql语句来判断是读还是写操作,读操作就设置slave源,写操作就给其设置master源
5.在mybatis全局配置文件中配置DateSourceSelectInterceptor这个拦截器的包名
18.表的拆分
水平拆分
如果一张的表的行数过于庞大,可以考虑水平分表,提高查询效率
水平拆分是根据表中的某一字段(通常是主键 ID )取模处理,将一张表的数据拆分到多个表中,
还可以通过时间分表,比如每月生成一张表。
分表之后不能避免的就是查询要比以前复杂,通常不建议 join
,一般的做法是做两次查询。
拆分遇到ID策略问题:
1.以一张表10万数据,继续水平拆分,会导致数据在不同服务器(下张表)分布不均匀,访问服务器不能实现负载均衡
2.以3台服务器水平拆分为例,增加数据以步长为3的,长此以往,1.部分数据因某原因删除,占用服务器资源,2.无法进行第4台服务器的扩展,扩展会有数据id冲突的情况
**解决方案:**雪花算法
垂直拆分
一张表的字段字节内容过多时或者不常用的字段则可以考虑垂直拆分。分为主表以及扩展表
也不建议使用 join
,依然建议使用两次查询。
事务如何保证
一个业务是 A 调用 B,两个执行成功才算最终成功,
B 失败时 通过 MQ 将消息告诉 A,A 再来进行回滚,
A 的回滚操作得是幂等的,可重复调用,在调用方多次调用的情况下,最终得到的结果是一致的
18.全局唯一ID雪花算法
雪花算法
工作机器id
占用10bit,其中高位5bit是数据中心ID(datacenterId),低位5bit是工作节点ID(workerId),做多可以容纳1024个节点。
序列号
同一毫秒同一节点上从0开始不断累加,最多可以累加到4095。
**优点:*
1.生成ID时不依赖于DB,完全在内存生成,高性能高可用。
2.ID呈趋势递增,后续插入索引树的时候性能较好。
*缺点:*
依赖于系统时钟的一致性。如果某台机器的系统时钟回拨,有可能造成ID冲突,或者ID乱序。
Mybatis-Plus中封装了雪花算法:
@TableId(type = IdType.ASSIGN_ID),可以不写,默认使用
mybatis中sql语句#{}与${}区别
都能获取参数的值,$只获取值本身不拼接单引号,#获取到值以后自动拼接单引号 $底层使用Statement不安全低效,#底层使用PreparedStatement高效安全SQL简单
#{}
是预编译处理,以有效的防止SQL注入
,即便用户输入非法参数,也不会对SQL的结构产生影响,
select * from table where field = "a\' or \'1=1";
${}
是字符串替换;
elect * from table where field = 'a' or '1=1'