MySQL/数据库 知识点总结

 

书籍推荐

  • 《SQL基础教程(第2版)》 (入门级)
  • 《高性能MySQL : 第3版》 (进阶)

文字教程推荐

相关资源推荐

视频教程推荐

基础入门: 与MySQL的零距离接触-慕课网html

MySQL开发技巧: MySQL开发技巧(一)  MySQL开发技巧(二)  MySQL开发技巧(三)java

MySQL5.7新特性及相关优化技巧: MySQL5.7版本新特性  性能优化之MySQL优化mysql

MySQL集群(PXC)入门  MyCAT入门及应用git

常见问题总结

什么是MySQL?

MySQL 是一种关系型数据库,在Java企业级开发中很是经常使用,由于 MySQL 是开源免费的,而且方便扩展。阿里巴巴数据库系统也大量用到了 MySQL,所以它的稳定性是有保障的。MySQL是开放源代码的,所以任何人均可以在 GPL(General Public License) 的许可下下载并根据个性化的须要对其进行修改。MySQL的默认端口号是3306。github

存储引擎

一些经常使用命令

查看MySQL提供的全部存储引擎面试

mysql> show engines;

查看MySQL提供的全部存储引擎

从上图咱们能够查看出 MySQL 当前默认的存储引擎是InnoDB,而且在5.7版本全部的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。redis

查看MySQL当前默认的存储引擎算法

咱们也能够经过下面的命令查看默认的存储引擎。sql

mysql> show variables like '%storage_engine%';

查看表的存储引擎数据库

show table status like "table_name" ;

查看表的存储引擎

MyISAM和InnoDB区别

MyISAM是MySQL的默认数据库引擎(5.5版以前)。虽然性能极佳,并且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,并且最大的缺陷就是崩溃后没法安全恢复。不过,5.5版本以后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。

大多数时候咱们使用的都是 InnoDB 存储引擎,可是在某些状况下使用 MyISAM 也是合适的好比读密集的状况下。(若是你不介意 MyISAM 崩溃恢复问题的话)。

二者的对比:

  1. 是否支持行级锁 : MyISAM 只有表级锁(table-level locking),而InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
  2. 是否支持事务和崩溃后的安全恢复: MyISAM 强调的是性能,每次查询具备原子性,其执行速度比InnoDB类型更快,可是不提供事务支持。可是InnoDB 提供事务支持事务,外部键等高级数据库功能。 具备事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
  3. 是否支持外键: MyISAM不支持,而InnoDB支持。
  4. 是否支持MVCC :仅 InnoDB 支持。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工做;MVCC可使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。推荐阅读:MySQL-InnoDB-MVCC多版本并发控制
  5. ......

《MySQL高性能》上面有一句话这样写到:

不要轻易相信“MyISAM比InnoDB快”之类的经验之谈,这个结论每每不是绝对的。在不少咱们已知场景中,InnoDB的速度均可以让MyISAM可望不可即,尤为是用到了聚簇索引,或者须要访问的数据均可以放入内存的应用。

通常状况下咱们选择 InnoDB 都是没有问题的,可是某些状况下你并不在意可扩展能力和并发能力,也不须要事务支持,也不在意崩溃后的安全恢复问题的话,选择MyISAM也是一个不错的选择。可是通常状况下,咱们都是须要考虑到这些问题的。

字符集及校对规则

字符集指的是一种从二进制编码到某类字符符号的映射。校对规则则是指某种字符集下的排序规则。MySQL中每一种字符集都会对应一系列的校对规则。

MySQL采用的是相似继承的方式指定字符集的默认值,每一个数据库以及每张数据表都有本身的默认值,他们逐层继承。好比:某个库中全部表的默认字符集将是该数据库所指定的字符集(这些表在没有指定字符集的状况下,才会采用默认字符集) PS:整理自《Java工程师修炼之道》

详细内容能够参考: MySQL字符集及校对规则的理解

索引

MySQL索引使用的数据结构主要有BTree索引 和 哈希索引 。对于哈希索引来讲,底层的数据结构就是哈希表,所以在绝大多数需求为单条记录查询的时候,能够选择哈希索引,查询性能最快;其他大部分场景,建议选择BTree索引。

MySQL的BTree索引使用的是B树中的B+Tree,但对于主要的两种存储引擎的实现方式是不一样的。

  • MyISAM: B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,则取出其 data 域的值,而后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。
  • InnoDB: 其数据文件自己就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件自己就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以InnoDB表数据文件自己就是主索引。这被称为“聚簇索引(或汇集索引)”。而其他的索引都做为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不一样的地方。在根据主索引搜索时,直接找到key所在的节点便可取出数据;在根据辅助索引查找时,则须要先取出主键的值,再走一遍主索引。 所以,在设计表的时候,不建议使用过长的字段做为主键,也不建议使用非单调的字段做为主键,这样会形成主索引频繁分裂。 PS:整理自《Java工程师修炼之道》

更多关于索引的内容能够查看文档首页MySQL目录下关于索引的详细总结。

查询缓存的使用

执行查询语句的时候,会先查询缓存。不过,MySQL 8.0 版本后移除,由于这个功能不太实用

my.cnf加入如下配置,重启MySQL开启查询缓存

query_cache_type=1 query_cache_size=600000

MySQL执行如下命令也能够开启查询缓存

set global query_cache_type=1; set global query_cache_size=600000;

如上,开启查询缓存后在一样的查询条件以及数据状况下,会直接在缓存中返回结果。这里的查询条件包括查询自己、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息。所以任何两个查询在任何字符上的不一样都会致使缓存不命中。此外,若是查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL库中的系统表,其查询结果也不会被缓存。

缓存创建以后,MySQL的查询缓存系统会跟踪查询中涉及的每张表,若是这些表(数据或结构)发生变化,那么和这张表相关的全部缓存数据都将失效。

缓存虽然可以提高数据库的查询性能,可是缓存同时也带来了额外的开销,每次查询后都要作一次缓存操做,失效后还要销毁。 所以,开启缓存查询要谨慎,尤为对于写密集的应用来讲更是如此。若是开启,要注意合理控制缓存空间大小,通常来讲其大小设置为几十MB比较合适。此外,还能够经过sql_cache和sql_no_cache来控制某个查询语句是否须要缓存:

select sql_no_cache count(*) from usr;

什么是事务?

事务是逻辑上的一组操做,要么都执行,要么都不执行。

事务最经典也常常被拿出来讲例子就是转帐了。假如小明要给小红转帐1000元,这个转帐会涉及到两个关键操做就是:将小明的余额减小1000元,将小红的余额增长1000元。万一在这两个操做之间忽然出现错误好比银行系统崩溃,致使小明余额减小而小红的余额没有增长,这样就不对了。事务就是保证这两个关键操做要么都成功,要么都要失败。

事物的四大特性(ACID)

事物的特性

  1. 原子性(Atomicity): 事务是最小的执行单位,不容许分割。事务的原子性确保动做要么所有完成,要么彻底不起做用;
  2. 一致性(Consistency): 执行事务先后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
  3. 隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其余事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性(Durability): 一个事务被提交以后。它对数据库中数据的改变是持久的,即便数据库发生故障也不该该对其有任何影响。

并发事务带来哪些问题?

在典型的应用程序中,多个事务并发运行,常常会操做相同的数据来完成各自的任务(多个用户对同一数据进行操做)。并发虽然是必须的,但可能会致使如下的问题。

  • 脏读(Dirty read): 当一个事务正在访问数据而且对数据进行了修改,而这种修改尚未提交到数据库中,这时另一个事务也访问了这个数据,而后使用了这个数据。由于这个数据是尚未提交的数据,那么另一个事务读到的这个数据是“脏数据”,依据“脏数据”所作的操做多是不正确的。
  • 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,所以称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
  • 不可重复读(Unrepeatableread): 指在一个事务内屡次读同一数据。在这个事务尚未结束时,另外一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,因为第二个事务的修改致使第一个事务两次读取的数据可能不太同样。这就发生了在一个事务内两次读到的数据是不同的状况,所以称为不可重复读。
  • 幻读(Phantom read): 幻读与不可重复读相似。它发生在一个事务(T1)读取了几行数据,接着另外一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些本来不存在的记录,就好像发生了幻觉同样,因此称为幻读。

不可重复读和幻读区别:

不可重复读的重点是修改好比屡次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除好比屡次读取一条记录发现记录增多或减小了。

事务隔离级别有哪些?MySQL的默认隔离级别是?

SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,容许读取还没有提交的数据变动,可能会致使脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交): 容许读取并发事务已经提交的数据,能够阻止脏读,可是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读): 对同一字段的屡次读取结果都是一致的,除非数据是被自己事务本身所修改,能够阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化): 最高的隔离级别,彻底服从ACID的隔离级别。全部的事务依次逐个执行,这样事务之间就彻底不可能产生干扰,也就是说,该级别能够防止脏读、不可重复读以及幻读。

隔离级别 脏读 不可重复读 幻影读
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。咱们能够经过SELECT @@tx_isolation;命令来查看

mysql> SELECT @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+

这里须要注意的是:与 SQL 标准不一样的地方在于 InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下使用的是Next-Key Lock 锁算法,所以能够避免幻读的产生,这与其余数据库系统(如 SQL Server) 是不一样的。因此说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经能够彻底保证事务的隔离性要求,即达到了 SQL标准的 SERIALIZABLE(可串行化) 隔离级别。由于隔离级别越低,事务请求的锁越少,因此大部分数据库系统的隔离级别都是 READ-COMMITTED(读取提交内容) ,可是你要知道的是InnoDB 存储引擎默认使用 REPEAaTABLE-READ(可重读) 并不会有任何性能损失。

InnoDB 存储引擎在 分布式事务 的状况下通常会用到 SERIALIZABLE(可串行化) 隔离级别。

锁机制与InnoDB锁算法

MyISAM和InnoDB存储引擎使用的锁:

  • MyISAM采用表级锁(table-level locking)。
  • InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

表级锁和行级锁对比:

  • 表级锁: MySQL中锁定 粒度最大 的一种锁,对当前操做的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的几率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
  • 行级锁: MySQL中锁定 粒度最小 的一种锁,只针对当前操做的行进行加锁。 行级锁能大大减小数据库操做的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。

详细内容能够参考: MySQL锁机制简单了解一下:http://www.javashuo.com/article/p-omwesioc-ba.html

InnoDB存储引擎的锁的算法有三种:

  • Record lock:单个行记录上的锁
  • Gap lock:间隙锁,锁定一个范围,不包括记录自己
  • Next-key lock:record+gap 锁定一个范围,包含记录自己

相关知识点:

  1. innodb对于行的查询使用next-key lock
  2. Next-locking keying为了解决Phantom Problem幻读问题
  3. 当查询的索引含有惟一属性时,将next-key lock降级为record key
  4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会致使幻读问题的产生
  5. 有两种方式显式关闭gap锁:(除了外键约束和惟一性检查外,其他状况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1

大表优化

当MySQL单表记录数过大时,数据库的CRUD性能会明显降低,一些常见的优化措施以下:

1. 限定数据的范围

务必禁止不带任何限制数据范围条件的查询语句。好比:咱们当用户在查询订单历史的时候,咱们能够控制在一个月的范围内;

2. 读/写分离

经典的数据库拆分方案,主库负责写,从库负责读;

3. 垂直分区

根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登陆信息又有用户的基本信息,能够将用户表拆分红两个单独的表,甚至放到单独的库作分库。

简单来讲垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 以下图所示,这样来讲你们应该就更容易理解了。 数据库垂直分区

  • 垂直拆分的优势: 可使得列数据变小,在查询时减小读取的Block数,减小I/O次数。此外,垂直分区能够简化表的结构,易于维护。
  • 垂直拆分的缺点: 主键会出现冗余,须要管理冗余列,并会引发Join操做,能够经过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;

4. 水平分区

保持数据表结构不变,经过某种策略存储数据分片。这样每一片数据分散到不一样的表或者库中,达到了分布式的目的。 水平拆分能够支撑很是大的数据量。

水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时能够把一张的表的数据拆成多张表来存放。举个例子:咱们能够将用户信息表拆分红多个用户信息表,这样就能够避免单一表数据量过大对性能形成影响。

数据库水平拆分

水平拆分能够支持很是大的数据量。须要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但因为表的数据仍是在同一台机器上,其实对于提高MySQL并发能力没有什么意义,因此 水平拆分最好分库 。

水平拆分可以 支持很是大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的做者推荐 尽可能不要对数据进行分片,由于拆分会带来逻辑、部署、运维的各类复杂度 ,通常的数据表在优化得当的状况下支撑千万如下的数据量是没有太大问题的。若是实在要分片,尽可能选择客户端分片架构,这样能够减小一次和中间件的网络I/O。

下面补充一下数据库分片的两种常见方案:

  • 客户端代理: 分片逻辑在应用端,封装在jar包中,经过修改或者封装JDBC层来实现。 当当网的 Sharding-JDBC 、阿里的TDDL是两种比较经常使用的实现。
  • 中间件代理: 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 咱们如今谈的 Mycat 、360的Atlas、网易的DDB等等都是这种架构的实现。

详细内容能够参考: MySQL大表优化方案: http://www.javashuo.com/article/p-cclvigrf-k.html

解释一下什么是池化设计思想。什么是数据库链接池?为何须要数据库链接池?

池话设计应该不是一个新名词。咱们常见的如java线程池、jdbc链接池、redis链接池等就是这类设计的表明实现。这种设计会初始预设资源,解决的问题就是抵消每次获取资源的消耗,如建立线程的开销,获取远程链接的开销等。就比如你去食堂打饭,打饭的大妈会先把饭盛好几份放那里,你来了就直接拿着饭盒加菜便可,不用再临时又盛饭又打菜,效率就高了。除了初始化资源,池化设计还包括以下这些特征:池子的初始值、池子的活跃值、池子的最大值等,这些特征能够直接映射到java线程池和数据库链接池的成员属性中。——这篇文章对池化设计思想介绍的还不错,直接复制过来,避免重复造轮子了。

数据库链接本质就是一个 socket 的链接。数据库服务端还要维护一些缓存和用户权限信息之类的 因此占用了一些内存。咱们能够把数据库链接池是看作是维护的数据库链接的缓存,以便未来须要对数据库的请求时能够重用这些链接。为每一个用户打开和维护数据库链接,尤为是对动态数据库驱动的网站应用程序的请求,既昂贵又浪费资源。**在链接池中,建立链接后,将其放置在池中,并再次使用它,所以没必要创建新的链接。若是使用了全部链接,则会创建一个新链接并将其添加到池中。**链接池还减小了用户必须等待创建与数据库的链接的时间。

分库分表以后,id 主键如何处理?

由于要是分红多个表以后,每一个表都是从 1 开始累加,这样是不对的,咱们须要一个全局惟一的 id 来支持。

生成全局 id 有下面这几种方式:

  • UUID:不适合做为主键,由于太长了,而且无序不可读,查询效率低。比较适合用于生成惟一的名字的标示好比文件的名字。
  • 数据库自增 id : 两台数据库分别设置不一样步长,生成不重复ID的策略来实现高可用。这种方式生成的 id 有序,可是须要独立部署数据库实例,成本高,还会有性能瓶颈。
  • 利用 redis 生成 id : 性能比较好,灵活方便,不依赖于数据库。可是,引入了新的组件形成系统更加复杂,可用性下降,编码更加复杂,增长了系统成本。
  • Twitter的snowflake算法 :Github 地址:https://github.com/twitter-archive/snowflake。
  • 美团的Leaf分布式ID生成系统 :Leaf 是美团开源的分布式ID生成器,能保证全局惟一性、趋势递增、单调递增、信息安全,里面也提到了几种分布式方案的对比,但也须要依赖关系数据库、Zookeeper等中间件。感受还不错。美团技术团队的一篇文章:https://tech.meituan.com/2017/04/21/mt-leaf.html 。
  • ......

一条SQL语句在MySQL中如何执行的

一条SQL语句在MySQL中如何执行的

MySQL高性能优化规范建议

MySQL高性能优化规范建议

一条SQL语句执行得很慢的缘由有哪些?

腾讯面试:一条SQL语句执行得很慢的缘由有哪些?---不看后悔系列

相关文章
相关标签/搜索