JAVA知识点全总结——(四)数据库

上一篇:JAVA知识点全总结——(三)多线程与并发

4. 数据库

4.1 MySQL数据库原理

mysql顶层有连接池,这个类似于线程池,用来在并发的时候进行数据库的操作。当sql语句读取到Mysql中时,有一层封装层用来拆分sql语句,比在处理之后将结果返回提供给上层,做了一层封装层;在这层封装层下面是语句的过滤器,会将分段的语句过滤,如果有的地方不和语法就会报错返回;再下面是优化器,一条语句究竟用什么索引,怎么用索引,怎么去执行,在这一层会给出执行计划;在这一层还有缓存,当一条语句查询完毕后结果会存放在缓存中,如果是同样的语句那么就不会再次执行了。再下面是执行引擎,之后就是数据文件了。

4.2 索引

4.2.1 组合索引匹配规则

  • 全值匹配
  • 最左匹配
  • 左前缀也可以匹配
  • 第一个全值匹配,第二个最左匹配也可以
  • 如果要匹配范围最好把范围放在最后,范围之后的索引都不能能用
  • 如果是匹配多个单值,全部列出的情况,后面的索引还能用
  • 组合索引比单列索引需要的空间更多

4.2.2 索引优化策略

  • 判断字段的选择性,count(col distict)/count(*),尽量找选择性高的数据做索引
  • 也并不绝对,如果选择性低的字段要经常做索引,也可以加上,放在组合索引首,如果要使用则:IN(m,f)
  • 对于varchar等,最好使用前缀索引,city(7)。这个具体的长度可以通过选择性来计算获取,先计算整体的选择性,在分个计算看效率
  • 不能利用前缀索引做orderby和groupby索引,也无法使用前缀索引做覆盖扫描
  • 多个单列的效果在where中and联合查询的时候没有一个组合索引效果好,多个单列之间并不能配合。
  • OR的效率非常差,因为可能涉及到缓存和合并等操作

4.2.3 聚簇索引

  • 聚簇索引实际上是把主键和数据行存放在一起
  • 优点,快速
  • 缺点,插入效率低下,尤其是插入的主键不是自增的时候
  • 为了防止这种主键不自增innodb有主键自动增加的设置,或者一般我们在建表的时候会增加一个id字段,这个字段每张表都有,和业务无关,专门是为了主键索引。
  • MyISAM是根据地址或者说行号确定位置,主键没有那么快速

4.2.4 覆盖索引

  • 覆盖索引指要查询的select字段包括where的查询条件全都是由索引组成的,被索引覆盖不需要回表
  • 尽量避免查询select *,无法用索引来覆盖。不能再索引中含有通配符开头的like,因为索引查不中
  • 在处理大量数据,比如limit 10000,10的时候用覆盖索引,避免回表

4.2.3 索引优化排序

  • order by中的字段要和索引一一对应才行,而且同时采用一种排序方法 否则不能利用索引
  • 索引的第一个字段也可以在where子句中做等值的常量,后几个字段在orderby中做排序
  • limit先用一个嵌套子查询查出来需要的数据,在和原表inner join

4.3 B-Tree

B树是一种多路树,因为二叉树的搜索效率好,但是深度过深,在数据库中每一次找字节点都有可能是一次IO,所以深度对二叉树很重要,B树通过多子的规定降低了二叉树的深度。B树规定根结点的子节点个数最少为2,最多为m。这里的m是一个阶的概念,不同阶的B树不一样。非叶节点的子节点个数是2分之m到m,这样确保了树的深度不高,一页存放的信息能一次IO读取出来。

4.4 B+Tree

B+树和B树差不多,除了B+树为所有叶子结点增加一个链指针,而且所有关键字都在叶子结点出现。B+tree的磁盘读写代价更低,没有了中间索引,可以放下更多的数据,而且范围查找的时候从链表开始走,快的不行。

4.5 事务处理

4.5.1 ACID

ACID是事务的四大特性,想要成为事务,必须具备这四点。事务具有一起成功一起失败的特点。Atomicity:原子性体现在对于一个事务来讲,要么一起执行成功要么一起失败,执行的过程中是不能被打断或者执行其他操作的。Consistency:一致性表现为事务进行过后和执行前,整体系统都是稳定的,比如对于入账出账操作是不会有总资金的变化的。Isolation:隔离性表示各个事务之间不会互相影响,数据库一般会提供多种级别的隔离。实际上多个事务是并发执行的,但是他们之间不会互相影响。Durability:持久性表示一旦一个事务成功了,那么他的改变是永久性的被记录和操作。

4.5.2 脏读

数据在读取数据的时候读到了其他事务正在进行修改的数据,其他的事务修改数据后又对它进行了其他操作或回滚了,这样读取的那个数据是错误的。

4.5.3 不可重复读

在一个事务中反复读取一个数据时读到了不同的数据,因为在读取的过程中其他的事务对其进行了修改并提交了。

4.5.4 幻读

第二个事务插入或删除一条数据,这个数据本应该是第一个事务读取到的,但是因为第二个事务紧接着第一个事务完成后才完成,导致了第一个事务没有读到这一条,好像出错了一样。

数据库提供了四种级别的事务隔离,来保证不同的效果。数据库锁是为了构建这些隔离级别存在的。

4.6 数据库锁

4.6.1 乐观锁

乐观锁是一种概念,不为数据加显示的锁,但是会维护一个版本号或者时间戳,在完成数据操作的时候会检查这个版本号,如果不大于之前获取的值则说明在此期间没有发生更新,可以完成事务。乐观锁有更好的并行性,但是在事务处理比较集中的时候失败率比较高,在事务分散的时候效率不错。而且在多服务多系统中,如果其他的服务调用了本数据库,需要在多个服务之间同步版本号的逻辑,比较复杂,耦合度高。

4.6.2 悲观锁

悲观锁的概念就是预测一定会发生并发问题,所以在代码中就使用锁来保证安全。悲观锁是对应于乐观锁的一种概念,具体的实现还有很多种。

4.6.3 排他锁

排他锁也叫做写锁,就是严格的排他,在一个锁获得了当前的操作权后其他的任何锁都要等待他完成并释放。

4.6.4 共享锁

共享锁也叫做读锁,多个共享锁可以一起工作,因为只是读取数据。

4.6.5 更新锁

用来预定要对此对象施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的对象将要被更新时,则升级为X锁,主要是用来防止死锁的。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排它锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个对象申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就可以避免死锁。

4.7 事务隔离级别

4.7.1 读未提交

均不可避免。不加锁

4.7.2 读已提交

可以避免 :脏读。读取的时候加行级锁,读取结束释放。

4.7.3 可重复读

可以避免 :脏读、不可重复读。读取的时候加行级锁,一直等到事务结束才会释放。

4.7.4 串行化

可以避免 :脏读、不可重复读、幻读。任何时候直接加表锁,事务结束释放。

4.7 SQL语句


create database xxx

dorp database xxx

create table tablexxx (col1 type1 primary key,
col2 type2 not null ...)

drop table tablexxx

alter table tablexxx add column col type ...

alter table tablexxx add primary key(col)

alter table tablexxx drop primary key(col)

create index idsxxx on tablexxx(col1, col2...)

drop index indexxx

select * from xxx where xxx = 'ss'

insert into tablexxx (col1, col2, ...) values(values1, values2, ...)

delete from tablexxx where xx ='xx'

update tablexxx set xx = 'xx' where xx ='xx'

select * from xx where xx orderby xx desc , xx

select count(*) as ttc where tablexxx

avg max min sum
union union all left join right join join

4.8 数据库调优

没有where的时候用count(*) count(*)与count(1)一样,有主键的差别。统计列的时候要求统计非空的count(列名)

group by having和select必须根据groupby的字段做调整和输出,如果select中输出了无关项,则自动输出第一条

4.9 范式

4.9.1 第一范式

定义:关系中的每个属性都不可再分。

理解:基本的数据库都满足第一范式。

4.9.2 第二范式

定义:关系中不存在非主属性对于主属性的部分函数依赖。或者理解为:非主属性必须对主属性完全依赖。

举例:比如主属性为:A、B,而C作为一个非主属性,只与A有依赖关系,C与B无关,那么此时C对于A、B只存在部分依赖,此时不符合第二范式。

理解:符合第二范式的关系能够保证每一个非主属性都严格的依赖主属性。就是任何一个非主属性都必须和所有的主属性有关。通俗的说,这张表中的内容都和主键有关,主键是最核心的部分。如果存在冗余的字段,将在第二范式中被剔除。

作用:

1)消除冗余,只有完全依赖的才能留下,依赖性不强的字段都删掉了。

2)降低数据间的耦合,对一类事物的修改不会影响其他事物。

4.9.3 第三范式

定义:关系中不存在非主属性对于非主属性的任何函数依赖。

举例:比如主属性为:A,而B、C作为非主属性,存在“B依赖A,C完全依赖或部分依赖B”的情况。那么此时C对于B存在依赖情况,此时不符合第三范式。

理解:第三范式更加严格,第二范式说普通的属性必须和主属性(如果主属性是多个,那么就必须和所有的主属性(或主属性联合起来的概念)有关)有关,但是没有规定这些普通属性间有什么要求。第三范式说,如果普通属性之间也有关系,比如C会被B决定或影响,那么C就应该被踢出去,单独和B(或含有B和其他的属性)建另外的一张表。

作用:

1)消除冗余,往往C依赖于B,当有大量B的时候C也都是大量一样的数据,浪费。

2)降低数据间的耦合,对一类事物的修改不会影响其他事物。

4.9.4 BC范式

定义:主属性中不存在某些属性部分依赖另外一些属性。

举例:比如主属性为:A、B、C。存在B依赖于A、C的情况。此时不符合BC范式。

理解:看了第二第三范式你会发现一个问题,如果我把主属性也就是主键定义的很大,基本都能满足这两个范式,因为第二第三范式是针对非主属性的规定。范式也想到了这个问题,就出现了BC范式,通俗地说,BC范式就是告诉我们“别吧主键定太多,主键也要符合规矩啊”。主键内不能存在依赖的情况。

作用:1)降低数据间的耦合,对一类事物的修改不会影响其他事物。

4.10 InnoDB与MyISAM

如果说主要进行查询操作,那么MyISAM比较合适一些,MyISAM没有事务的概念,没有外键的概念等,写的时候加表锁。InnoDB更全面,事务、外键、主键自增等。

4.11 SQL慢查询

  • 字段类型转换导致不用索引,如字符串类型的不用引号,数字类型的用引号等,这有可能会用不到索引导致全表扫描;
  • mysql 不支持函数转换,所以字段前面不能加函数,否则这将用不到索引;
  • 不要在字段前面加减运算;
  • 字符串比较长的可以考虑索引一部份减少索引文件大小,提高写入效率;
  • like % 在前面用不到索引;
  • 排序请尽量使每个条件是同样的升降;
  • or 的查询尽量用 union 代替 (Innodb);
  • 复合索引高选择性的字段排在前面;
  • order by / group by 字段包括在索引当中减少排序,效率会更高。
  • 尽量规避大事务的 SQL,大事务的 SQL 会影响数据库的并发性能及主从同步;

4.12 MVCC

MVCC是多版本并发控制,相比较普通的加锁机制,MVCC用不同的版本来隔离读和写之间的冲突。每个连接到数据库的线程池实例看到的都是数据库的一个快照版本。写操作在完成前对其他读操作是不可见的,每个数据都拥有一个版本号,表示这个数据当前的版本。读取可能读取到的是过时的数据,但是这个数据不是一个错误的数据。

4.13 EXPLAIN

explain命令 查看执行计划,后面直接跟语句

mysql> explain select * from servers;
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | servers | ALL  | NULL          | NULL | NULL    | NULL |    1 | NULL  |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
row in set (0.03 sec)
  • id:包含一组数字,表示查询中执行select子句或操作表的顺序,id值越大优先级越高,越先被执行
  • select_type:标记在查询语句中包含的子查询的类型
  • table:查询的表名
  • type:查询类型(ALL, index, range, ref, eq_ref, const, system, NULL)
  • possible_keys:表示使用的索引的全部,如果使用的是组合索引的一部分,这里展示全部的组合索引的样子
  • key:表示使用的索引,如果使用的是组合索引的一部分,这里展示使用的部分
  • key_len:索引的长度
  • ref:
  • rows:根据查询的结果估算需要读取的行数
  • Extra:(Using index:使用了覆盖索引;Using where:先读取了where的条件再查行,是一种优化;Using temporary:需要临时表来存放信息;Using filesort:无法利用索引排序;Using join buffer:链接的时候没有使用索引,需要优化)

  • EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况

  • EXPLAIN不考虑各种Cache

  • EXPLAIN不能显示MySQL在执行查询时所作的优化工作

  • 部分统计信息是估算的,并非精确值

  • EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。

4.14 子查询与关联查询

  • 关联查询是将两个或多个的表按某个条件连接起来,从中选取需要的数据,是同时查询两个或两个以上的表的使用的。当不同的表中存在相同意义的字段时,可以通过该字段来连接这几个表。
  • 子查询是将一个查询语句嵌套在另外一个查询语句中,内层查询语句的查询结果,可以为外层查询语句提供查询条件。

4.15 数据库连接池

连接池和线程池很像,都是创建一些连接的对象放在内存里,一直维护这些,如果有请求要查询的话就使用连接池中的对象进行查询。在spring中集成c3p0之类的数据源也可以对连接池进行配置,让连接池按照我们希望的样子构建,比如可以配置最大空闲连接数、最小空闲连接数、初始化连接数、最大连接数量。

4.15 NoSQL

NoSQL表示的是一类非关系型的数据库,其中比较有代表性的是redis。

4.16 CAP

C表示一致性,A表示可用性,P表示分区容忍性。一般来讲这三种原则能够符合其中的两种。

4.16.1 BASE

BA表示基本可用性,S表示柔软性,E表示最终一致性。

4.17 redis

redis是一种kv的数据存储系统

4.17.1 特点

  1. 纯内存操作
  2. 多路复用IO
  3. 单线程避免锁
  4. 内部数据结构是hash,而不是B树
  5. 数据结构简单,hash,string,list,set,zset等

4.17.2 缓存

redis可以做很多功能,主要有数据库、缓存、消息队列等。对于缓存而言做缓存服务器,将耗时久的语句结果放入redis中,下次查询直接返回结果。高并发,大量数据访问数据库时可能出现异常,用redis缓存先接受访问请求进行处理,预防并发问题。

  • 没有表的概念,采用时效限制(TimeToLive,TTL)的key-value存放数据,分为16个区(类似命名空间)查key即可
  • 过期策略:定期抽样删除、惰性删除
  • 双写一致性:可以降低发生概率,但是完全避免很难。如果有强烈的一致性要求则不能使用缓存

4.17.3 缓存穿透

大量访问缓存中不存在的数据,导致直接连到数据库上出现异常,解决方法:

  • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。采用异步更新策略,无论key是否取到值,都直接返回。
  • value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作
  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回

4.17.4 缓存雪崩

因为失效时间相同,缓存大面积同时失效导致直接连到数据库上出现异常,解决方法:

  • 给缓存的失效时间,加上一个随机值,避免集体失效
  • 使用互斥锁,但是该方案吞吐量明显下降了
  • 双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间

4.17.5 缓存算法

主要有FIFO,LRU,LFU等几种算法 - FIFO,先进先出,先进入缓存的先淘汰 - LRU,最近最少使用,最少使用的缓存先淘汰 - LRU,最近最不常用,定期之内,最不常用的先淘汰

缓存预热:新的缓存系统没有任何缓存数据,在缓存重建数据的过程中,系统性能和数据库负载都不太好,所以最好是在系统上线之前就把要缓存的热点数据加载到缓存中

缓存热备:当一台缓存服务器不可用时能实时切换到备用缓存服务器,不影响缓存使用。集群模式下,每个主节点都会有一个或多个从节点来当备用,一旦主节点挂点,从节点立即充当主节点使用

缓存更新:除了redis自带的缓存更新方法,为了降低内存使用我们可以进行其他更新方法:

定时清理过期的缓存,因为是惰性删除和定期抽样删除,定时清理还是有必要的

当用户请求时判断缓存是否过期,过期就去底层更新数据并更新缓存

缓存降级:当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级

4.17.6 RDB

一种是RDB持久化,原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化。

一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。

对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。

性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

4.17.7 AOF

另外一种是AOF(append only file)持久化,原理是将Reids的操作日志以追加的方式写入文件。

如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。

下一篇:JAVA知识点全总结——(五)网络

猜你喜欢

转载自blog.csdn.net/QuinnNorris/article/details/81201761