头图

写在前面

📌Xmind文件获取:GitHub 持续更新中,别忘了 star 喔~

@TOC
「Java学习+面试指南」思维导图,计算机自学指南,包括Java基础、JVM、数据库、mysql、redis、计算机网络、算法、数据结构、操作系统等,后台技术栈/架构师之路/全栈开发社区,阿里,腾讯,百度,美团,头条等春招/秋招/校招/面试

思维导图(png格式可下载放大)

在这里插入图片描述

mysql

事务

四大特性(ACID)

  • 原子性

    • 要么全部完成,要么完全不起作用
  • 一致性

    • 多个事务对同一个数据读取的结果是相同的
  • 隔离性

    • 各并发事务之间数据库是独立的
  • 持久性

    • 数据的改变是持久的

  • 脏读

    • A读,B读,A回滚,B不正确
  • 不可重复读

    • A读1,B写2,A读2
  • 幻读

    • A读2条,B删了1条,A读1条

四个隔离级别

  • READ-UNCOMMITTED(读取未提交)

    • 允许读取尚未提交的数据

      • 3
  • READ-COMMITTED(读取已提交)

    • 允许读取并发事务已经提交

      • 阻止脏读
  • REPEATABLE-READ(可重复读)

    • 多次读取结果都是一致

      • 阻止脏读和不可重复读
  • SERIALIZABLE(可串行化)

    • 依次逐个执行
  • 隔离机制的实现基于锁机制和并发调度
    (MVVC(多版本并发控制),通过保存修改的旧版本信息)
  • Mysql 默认采用的 REPEATABLE_READ隔离级别,分布式事务SERIALIZABLE(可串行化)
    Oracle 默认采用的 READ_COMMITTED隔离级别

  • Read Uncommitted

    • 不需要加共享锁,不会跟被修改的数据上的排他锁冲突
  • Read Committed

    • 加共享锁,语句执行完以后释放共享锁
  • Repeatable Read

    • 需要加共享锁,必须等待事务执行完毕以后才释放共享锁
  • SERIALIZABLE

    • 锁定整个范围的键,并一直持有锁,直到事务完成

粒度

  • 表级锁

    • 开销小,加锁快
    • 锁定粒度大,发出锁冲突的概率最高,并发度最低
    • MYISAM与INNODB
  • 行级锁

    • 开销大,加锁慢
    • 锁定粒度最小,发生锁冲突的概率最低,并发度也最高
    • INNODB
  • 页级锁

    • 开销和加锁时间界于表锁和行锁之间
    • 一次锁定相邻的一组记录
    • 并发度一般

类别

  • 共享锁
    读锁

    • 可以同时加上多个
  • 排他锁
    写锁

    • 只可以加一个
    • 其他的排他锁,共享锁都相斥

  • 悲观锁

    • 数据库中的锁机制
    • 多写的场景下
  • 乐观锁

    • 使用版本号机制或CAS算法实现
    • 写比较少的情况下(多读场景)

基础知识

三大范式

  • 第一范式 列不可再分
  • 第二范式 非主键完全依赖主键,不能部分依赖
  • 第三范式 非主键只依赖主键,不依赖非主键

权限表

  • user 用户账号信息,全局
  • db 账号各数据库的操作权限
  • table_priv 表级操作权限
  • column_priv 列级操作权限
  • host 给定主机

binlog

  • statement 修改数据的sql:减少日志量、解决io,需保存上下文,函数之类无法被复制
  • row 记录每一行的改动:全部记下来,信息多日志量大
  • mixed 普通用statement,无法使用用row
  • MySQL主从复制,Master把它的二进制日志传递给slaves来达到master-slave数据一致的目的
  • 数据恢复:通过使用 mysqlbinlog工具来使恢复数据

数据类型

  • 整数类型

    • TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示1字节、2字节、3字节、4字节、8字节整数
    • 加上UNSIGNED表示无符号
    • 指定长度:INT(11)只影响显示字符,需要和UNSIGNED ZEROFILL才有意义
    • int(20)

      • 显示字符的长度。20表示最大显示宽度为20,但仍占4字节存储,存储范围不变,int(1)和int(20)存储和计算均一样
      • 不影响内部存储,只是影响带 zerofill 定义的 int 时,前面补多少个 0,易于报表展示
  • 实数类型

    • DECIMAL可以用于存储比BIGINT还大的整型,能存储精确的小数。
    • 而FLOAT和DOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。
    • 计算时FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。
    • FLOAT类型4字节,DOUBLE类型8字节
  • 字符串类型

    • VARCHAR

      • VARCHAR用于存储可变长字符串,它比定长类型更节省空间。
      • VARCHAR使用额外1或2个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字节表示。
      • VARCHAR存储的内容超出设置的长度时,内容会被截断。
    • CHAR

      • CHAR是定长的,根据定义的字符串长度分配足够的空间。
      • CHAR会根据需要使用空格进行填充方便比较。
      • CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
      • CHAR存储的内容超出设置的长度时,内容同样会被截断。
      • 长度固定,所以存取速度要比varchar快很多,甚至能快50%
        长度固定,所以会占据多余的空间,是空间换时间的做法
    • 综合

      • 对于经常变更的数据来说,CHAR比VARCHAR更好,因为CHAR不容易产生碎片。
      • 对于非常短的列,CHAR比VARCHAR在存储空间上更有效率。
      • 使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。
      • 尽量避免使用TEXT/BLOB类型,查询时会使用临时表,导致严重的性能开销。
      • 性能角度(char更快)和节省磁盘空间角度(varchar更小
      • chart(10)和varchar(10)
        表示存储数据的大小,即表示存储多少个字符

        • char(10)表示存储定长的10个字符,不足10个就用空格补齐,占用更多的存储空间
        • varchar(10)表示存储10个变长的字符,存储多少个就是多少个
          空格也按一个字符存储,这一点是和char(10)的空格不同的,char(10)的空格表示占位不算一个字符
      • 密码散列,盐,用户身份证号等固定长度的字符串应该使用char而不是varchar来存储,这样可以节省空间且提高检索效率。
  • 日期和时间类型

    • 尽量使用timestamp,空间效率高于datetime,
    • 用整数保存时间戳通常不方便处理。
    • 如果需要存储微秒,可以使用bigint存储。

关键字

in 和 exists

  • in

    • 外表和内表作hash 连接
  • exists

    • 对外表作loop循环,每次loop循环再对内表进行查询。
  • 两个表大小相当,那么用in和exists差别不大
  • 子查询表大的用exists,子查询表小的用in
  • not in,那么内外表都进行全表扫描,没有用到索引
    not exists的子查询依然能用到表上的索引

UNION与UNION ALL

  • UNION ALL,不会合并重复的记录行
  • 效率 UNION 高于 UNION ALL

drop、delete与truncate

  • Delete

    • 可回滚
    • 表结构还在,删除表的全部或者一部分数据行
    • 删除速度慢,需要逐行删除
  • Truncate

    • 不可回滚
    • 表结构还在,删除表中的所有数据
    • 删除速度快
  • Drop

    • 不可回滚
    • 从数据库中删除表,所有的数据行,索引和权限也会被删除
    • 删除速度最快
  • 不再需要一张表的时候,用drop;在想删除部分数据行时候,用delete;在保留表而删除所有数据的时候用truncate。

引擎

Innodb引擎

  • ACID事务的支持
  • 行级锁,并发量高
  • 外键的约束
  • INSERT、UPDATE、DELETE
  • B+树索引,Innodb 是索引组织表,聚簇索引,主键索引叶子节点存储着行数据,非主键索引的叶子节点存储的是主键和其他带索引的列数据
  • 哈希索引
  • 按主键大小有序插入
  • 更多的内存和存储,会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引

MyIASM引擎

  • 可被压缩,存储空间较小
  • SELECT,读写插入为主
  • B+树索引,myisam 是堆表,非聚簇索引,叶子节点存储的是行数据地址,再寻址一次
  • 全文索引
  • 按记录插入顺序保存
  • 以文件的形式存储,在跨平台的数据转移中会很方便

索引

优缺点

  • 优点

    • 加快检索速度
    • 使用优化隐藏器,提高系统的性能
  • 缺点

    • 创建索引和维护索引要耗费时间
    • 动态的维护,会降低增/改/删的执行效率
    • 占物理空间

使用场景

  • where
  • order by 没有用外部排序,有只要取出索引表某个范围内的索引对应的数据
  • explain select……

    • possible_keys 可能
    • key 实际用到
  • 索引覆盖

    • 直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表扫描)

类型

  • 主键索引: 不允许重复,不允许为NULL,一个表一个
  • 唯一索引: 不允许重复,允许为NULL值,一个表可多个
  • 普通索引: 没有唯一性的限制,允许为NULL值
  • 全文索引

数据结构

  • b+树

    • 不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符
    • 非终端结点可以看成是索引部分
    • 叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针
      数据对象的插入和删除仅在叶节点上进行
    • 2个头指针,一个是树的根节点,一个是最小关键码的叶节点
    • 天然支持范围,有序
    • 避免不了回表查询数据,聚簇索引、覆盖索引直接通过索引
  • hash

    • 只能用于对等比较
    • 等值查询更快

创建原则

  • 最左前缀匹配原则
    一直向右匹配直到遇到范围查询
  • 较频繁作为查询条件
  • 更新频繁字段不适合
  • 不能有效区分数据的列不适合
  • 有外键的数据列一定要
  • 尽量的扩展索引,不要新建索引

创建方式

  • CREATE TABLE
  • ALTER TABLE
  • CREATE INDEX 不能创建PRIMARY KEY索引
  • 删除索引 alter table 表名 drop KEY 索引名

百万级别删除

  • 产生额外的对索引文件的操作,会降低增/改/删的执行效率
  • 可以先删除索引
  • 删除其中无用数据
  • 删除完成后重新创建索引

B树和B+树的区别

  • B树

    • 内部节点同时存储键和值,提高热点数据的查询效率
  • B+树

    • 内部节点只存放键,一次性读入内存中可以查找的关键字也就越多,IO读写次数降低;
    • 由一条链相连,范围查询;
    • 必须走一条从根节点到叶节点的路查询效率相当

聚簇索引与非聚簇索引

  • 聚簇索引

    • 数据存储与索引放到了一块
  • 非聚簇索引

    • 将数据存储于索引分开结构,回表查询
  • 在InnoDB中

    • 只有主键索引是聚簇索引
    • 如果没有主键,则挑选一个唯一键建立聚簇索引。
    • 如果没有唯一键,则隐式的生成一个键来建立聚簇索引。
    • 在聚簇索引之上创建的索引称之为辅助索引,总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值
    • 非聚簇索引不一定会回表查询,全部命中了索引不必

联合索引

  • 想要命中索引,需要按照建立索引时的字段顺序挨个使用
  • 将查询需求频繁或者字段选择性高的列放在前面

其他

视图

  • 由基本表(实表)产生的表(虚表)
  • 建立和删除不影响基本表
  • 更新(添加,删除和修改)直接影响基本表
  • 来自多个基本表时,不允许添加和删除数据
  • 缺点

    • 性能
    • 修改限制

存储过程

预编译的SQL语句

  • 优点

    • 预编译过的,执行效率高
    • 直接存放于数据库中,减少网络通讯
    • 安全性高,有一定权限
    • 可以重复使用
  • 缺点

    • 调试麻烦
    • 移植问题
    • 重新编译问题
    • 用户需求的增加会导致数据结构的变化

触发器

一段代码,当触发某个事件时,自动执行这些代码

关联查询

交叉连接(笛卡尔积)

  • select r.,s. from r,s

内连接

  • select r.,s. from r inner join s on r.c=s.c

左连接

  • select r.,s. from r left join s on r.c=s.c

右连接

  • select r.,s. from r right join s on r.c=s.c

全表连接

mysql不支持

  • select r.,s. from r full join s on r.c=s.c
  • SELECT FROM A LEFT JOIN B ON A.id=B.id UNION SELECT FROM A RIGHT JOIN B ON A.id=B.id

优化

explain

  • 查看语句的执行计划
  • id

    • 表示一个查询中各个子查询的执行顺序
      越大优先级越高,越先被执行
  • type

    • ALL 扫描全表数据
    • index 遍历索引
      索引物理文件全扫描,速度非常慢
    • range 索引范围查找
    • ref 使用非唯一索引查找数据
    • consts 主键或者唯一索引
  • possible_keys

    • 可能使用的索引
  • key

    • 在查询中实际使用的索引

大表数据查询

  • 优化shema、sql语句+索引
  • 第二加缓存,memcached, redis
  • 主从复制,读写分离
  • 垂直拆分,根据你模块的耦合度,将一个大的系统分为多个小的系统,也就是分布式系统
  • 水平切分,针对数据量大的表,这一步最麻烦,最能考验技术水平,要选择一个合理的sharding key, 为了有好的查询效率,表结构也要改动,做一定的冗余,应用也要改,sql中尽量带sharding key,将数据定位到限定的表上去查,而不是扫描全部的表;

超大分页

  • MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下
  • 先快速定位需要获取的id段,然后再关联

    • SELECT a.* FROM 表1 a, (select id from 表1 where 条件 LIMIT 100000,20 ) b where a.id=b.id

分页

  • mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
  • mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
  • mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行

慢查询日志

  • 用于记录执行时间超过某个临界值的SQL日志
  • 配置项:slow_query_log
  • 优化

    • 是否load了额外的数据

      • 查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列
    • 使用索引的情况
    • 数据量是否太大

      • 横向或者纵向的分表

自增ID还是UUID

  • 使用自增ID,不要使用UUID
  • 自增ID,那么只需要不断向后排列即可
  • UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动
  • 没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键。

定义为not null

  • null值会占用更多的字节,且会在程序中造成很多与预期不符的情况。

SQL语句优化

优化WHERE子句

  • 在 where 及 order by 涉及的列上建立索引
  • 避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
  • 避免在 where 子句中使用!=或<>操作符
  • 避免在 where 子句中使用or 来连接条件
  • in 和 not in 也要慎用
  • 避免在 where 子句中使用参数
  • 避免在 where 子句中对字段进行表达式操作
  • 避免在where子句中对字段进行函数操作

优化查询过程中的数据访问

  • 查询不需要的数据

    • 使用limit解决
  • 多表关联返回全部列

    • 指定列名
  • 总是返回全部列

    • 避免使用SELECT *
  • 重复查询相同的数据

    • 可以缓存数据

优化长难的查询语句

  • 使用尽可能小的查询是好的
  • 将一个大的查询分为多个小的相同的查询
  • 分解关联查询

化特定类型的查询语句

  • count(*)会忽略所有的列,不要使用count(列名)
  • 增加汇总表
  • 使用缓存

优化关联查询

  • ON或者USING子句中是否有索引
  • GROUP BY和ORDER BY只有一个表中的列

优化子查询

  • 优化GROUP BY和DISTINCT,使用索引来优化,是最有效的优化方法
  • 不需要ORDER BY,进行GROUP BY时加ORDER BY NUL

数据库优化

结构优化

  • 将字段很多的表分解成多个表
  • 增加中间表

    • 需要经常联合查询的表
  • 增加冗余字段

    • 表的规范化程度越高,表和表之间的关系越多,需要连接查询的情况也就越多,性能也就越差。

cpu飙升

  • 看看里面跑的 session 情况,是不是有消耗资源的 sql 在运行

    • 找出消耗高的 sql,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成

      • kill 掉这些线程

        • 进行相应的调整(比如说加索引、改 sql、改内存参数)之后,再重新跑这些 SQL
  • 每个 sql 消耗资源并不多,但是突然之间,有大量的 session 连进来导致 cpu 飙升

    • 分析为何连接数会激增,再做出相应的调整,比如说限制连接数等

大表优化

  • 限定数据的范围
  • 读/写分离

    • 主库负责写,从库负责读
  • 缓存

    • 使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存
  • 分库分表

    • 垂直分区

      • 数据表列的拆分;某些列常用,另外一些列不常用
      • 使得行数据变小,在查询时减少读取的Block数,减少I/O次数
      • 会出现冗余,需要管理冗余列,并会引起Join操作;对于应用层来说,逻辑算法增加开发成本
    • 水平分区

      • 数据表行的拆分,水平拆分最好分库
      • 优点

        • 降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询次数
        • 支持非常大的数据量存储,应用端改造也少
      • 缺点

        • 分片事务难以解决 ,跨界点Join性能较差,逻辑复杂
        • 通常查询时需要多个表名,查询所有数据都需UNION操作
    • 数据库分片

      • 客户端代理

        • 分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现
      • 中间件代理

        • 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。
    • 问题

      • 事务支持

        • 数据库本身的分布式事务管理
        • 应用程序去协助控制
      • 跨库join

        • 分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。
      • 跨节点的count,order by,group by以及聚合函数问题

        • 与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并
        • 可以并行执行,因此很多时候它的速度要比单一大表快很多
      • ID问题

        • 自生成的ID无法保证在全局上是唯一的
        • 在插入数据之前需要先获得ID,以便进行SQL路由,常见的主键生成策略
        • UUID

          • UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。
      • 跨分片的排序分页

        • 排序字段非分片字段

          • 在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序

主从复制

通过二进制日志(BINLOG)传输到从数据库上,然后将这些日志重新执行(重做);保持一致

作用

  • 主数据库出现问题,可以切换到从数据库。
  • 可以进行数据库层面的读写分离。
  • 可以在从数据库上进行日常备份。

解决问题

  • 数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
  • 负载均衡:降低单个服务器的压力
  • 高可用和故障切换:帮助应用程序避免单点失败
  • 升级测试:可以用更高版本的MySQL作为从库

流程

  • 主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;
  • 从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进自己的relay log中;
  • 从:sql执行线程——执行relay log中的语句;

解决方案

  • 使用mysql-proxy代理

    • 降低性能, 不支持事务
  • 使用AbstractRoutingDataSource+aop+annotation在dao层决定数据源

    • mybatis

      • 通过mybatis plugin拦截sql语句,所有的insert/update/delete都访问master库,所有的select 都访问salve
        还需要重写一下DataSourceTransactionManager, 将read-only的事务扔进读库, 其余的有读有写的扔进写库
    • 使用AbstractRoutingDataSource+aop+annotation在service层决定数据源

      • 可以支持事务.

HNUJSY
1 声望0 粉丝

湖南大学 计算机科学与技术硕士在读;腾讯PCG 后端开发实习生