本人免费整理了Java高级资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G,须要本身领取。
传送门:https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q
目录
1、索引B+ Tree 原理
- MySQL 索引
- 索引优化
- 索引的优势
- 索引的使用条件
2、查询性能优化使用 Explain 进行分析
- 优化数据访问
- 重构查询方式
3、存储引擎InnoDB
- MyISAM
- 比较
4、数据类型整型
- 浮点数
- 字符串
- 时间和日期
5、切分水平切分
- 垂直切分
- Sharding 策略
- Sharding 存在的问题
6、复制主从复制
- 读写分离
1、索引
B+ Tree 原理
1. 数据结构
B Tree 指的是 Balance Tree,也就是平衡树。平衡树是一颗查找树,而且全部叶子节点位于同一层。mysql
B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具备 B Tree 的平衡性,而且经过顺序访问指针来提升区间查询的性能。算法
在 B+ Tree 中,一个节点中的 key 从左到右非递减排列,若是某个指针的左右相邻 key 分别是 keyi 和 keyi+1,且不为 null,则该指针指向节点的全部 key 大于等于 keyi 且小于等于 keyi+1。sql
2. 操做
进行查找操做时,首先在根节点进行二分查找,找到一个 key 所在的指针,而后递归地在指针所指向的节点进行查找。直到查找到叶子节点,而后在叶子节点上进行二分查找,找出 key 所对应的 data。数据库
插入删除操做会破坏平衡树的平衡性,所以在插入删除操做以后,须要对树进行一个分裂、合并、旋转等操做来维护平衡性。缓存
3. 与红黑树的比较
红黑树等平衡树也能够用来实现索引,可是文件系统及数据库系统广泛采用 B+ Tree 做为索引结构,主要有如下两个缘由:性能优化
(一)更少的查找次数服务器
平衡树查找操做的时间复杂度和树高 h 相关,O(h)=O(logdN),其中 d 为每一个节点的出度。数据结构
红黑树的出度为 2,而 B+ Tree 的出度通常都很是大,因此红黑树的树高 h 很明显比 B+ Tree 大很是多,查找的次数也就更多。并发
(二)利用磁盘预读特性分布式
为了减小磁盘 I/O 操做,磁盘每每不是严格按需读取,而是每次都会预读。预读过程当中,磁盘进行顺序读取,顺序读取不须要进行磁盘寻道,而且只须要很短的磁盘旋转时间,速度会很是快。
操做系统通常将内存和磁盘分割成固定大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能彻底载入一个节点。而且能够利用预读特性,相邻的节点也可以被预先载入。
MySQL 索引
索引是在存储引擎层实现的,而不是在服务器层实现的,因此不一样存储引擎具备不一样的索引类型和实现。
1. B+Tree 索引
是大多数 MySQL 存储引擎的默认索引类型。
由于再也不须要进行全表扫描,只须要对树进行搜索便可,因此查找速度快不少。
由于 B+ Tree 的有序性,因此除了用于查找,还能够用于排序和分组。
能够指定多个列做为索引列,多个索引列共同组成键。
适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。若是不是按照索引列的顺序进行查找,则没法使用索引。
InnoDB 的 B+Tree 索引分为主索引和辅助索引。主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。由于没法把数据行存放在两个不一样的地方,因此一个表只能有一个聚簇索引。
辅助索引的叶子节点的 data 域记录着主键的值,所以在使用辅助索引进行查找时,须要先查找到主键值,而后再到主索引中进行查找。
2. 哈希索引
哈希索引能以 O(1) 时间进行查找,可是失去了有序性:
- 没法用于排序与分组;
- 只支持精确查找,没法用于部分查找和范围查找。
InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的很是频繁时,会在 B+Tree 索引之上再建立一个哈希索引,这样就让 B+Tree 索引具备哈希索引的一些优势,好比快速的哈希查找。
3. 全文索引
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。
查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
全文索引使用倒排索引实现,它记录着关键词到其所在文档的映射。
InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
4. 空间数据索引
MyISAM 存储引擎支持空间数据索引(R-Tree),能够用于地理数据存储。空间数据索引会从全部维度来索引数据,能够有效地使用任意维度来进行组合查询。
必须使用 GIS 相关的函数来维护数据。
索引优化
1. 独立的列
在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,不然没法使用索引。
例以下面的查询不能使用 actor_id 列的索引:
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
2. 多列索引
在须要使用多个列做为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例以下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。
SELECT film_id, actor_ id FROM sakila.film_actor WHERE actor_id = 1 AND film_id = 1;
3. 索引列的顺序
让选择性最强的索引列放在前面。
索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每一个记录都有惟一的索引与其对应。选择性越高,每一个记录的区分度越高,查询效率也越高。
例以下面显示的结果中 customer_id 的选择性比 staff_id 更高,所以最好把 customer_id 列放在多列索引的前面。
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity, COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity, COUNT(*) FROM payment; staff_id_selectivity: 0.0001 customer_id_selectivity: 0.0373 COUNT(*): 16049
4. 前缀索引
对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。
前缀长度的选取须要根据索引选择性来肯定。
5. 覆盖索引
索引包含全部须要查询的字段的值。
具备如下优势:
- 索引一般远小于数据行的大小,只读取索引能大大减小数据访问量。
- 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操做系统来缓存。所以,只访问索引能够不使用系统调用(一般比较费时)。
- 对于 InnoDB 引擎,若辅助索引可以覆盖查询,则无需访问主索引。
索引的优势
- 大大减小了服务器须要扫描的数据行数。
- 帮助服务器避免进行排序和分组,以及避免建立临时表(B+Tree 索引是有序的,能够用于 ORDER BY 和 GROUP BY 操做。临时表主要是在排序和分组过程当中建立,不须要排序和分组,也就不须要建立临时表)。
- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,会将相邻的数据都存储在一块儿)。
索引的使用条件
- 对于很是小的表、大部分状况下简单的全表扫描比创建索引更高效;
- 对于中到大型的表,索引就很是有效;
- 可是对于特大型的表,创建和维护索引的代价将会随之增加。这种状况下,须要用到一种技术能够直接区分出须要查询的一组数据,而不是一条记录一条记录地匹配,例如可使用分区技术。
2、查询性能优化
使用 Explain 进行分析
Explain 用来分析 SELECT 查询语句,开发人员能够经过分析 Explain 结果来优化查询语句。
比较重要的字段有:
- select_type : 查询类型,有简单查询、联合查询、子查询等
- key : 使用的索引
- rows : 扫描的行数
优化数据访问
1. 减小请求的数据量
- 只返回必要的列:最好不要使用 SELECT * 语句。
- 只返回必要的行:使用 LIMIT 语句来限制返回的数据。
- 缓存重复查询的数据:使用缓存能够避免在数据库中进行查询,特别在要查询的数据常常被重复查询时,缓存带来的查询性能提高将会是很是明显的。
2. 减小服务器端扫描的行数
最有效的方式是使用索引来覆盖查询。
重构查询方式
1. 切分大查询
一个大查询若是一次性执行的话,可能一次锁住不少数据、占满整个事务日志、耗尽系统资源、阻塞不少小的但重要的查询。
DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH); rows_affected = 0 do { rows_affected = do_query( "DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000") } while rows_affected > 0
2. 分解大链接查询
将一个大链接查询分解成对每个表进行一次单表查询,而后在应用程序中进行关联,这样作的好处有:
- 让缓存更高效。对于链接查询,若是其中一个表发生变化,那么整个查询缓存就没法使用。而分解后的多个查询,即便其中一个表发生变化,对其它表的查询缓存依然可使用。
- 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减小冗余记录的查询。
- 减小锁竞争;
- 在应用层进行链接,能够更容易对数据库进行拆分,从而更容易作到高性能和可伸缩。
- 查询自己效率也可能会有所提高。例以下面的例子中,使用 IN() 代替链接查询,可让 MySQL 按照 ID 顺序进行查询,这可能比随机的链接要更高效。
SELECT * FROM tag JOIN tag_post ON tag_post.tag_id=tag.id JOIN post ON tag_post.post_id=post.id WHERE tag.tag='mysql'; SELECT * FROM tag WHERE tag='mysql'; SELECT * FROM tag_post WHERE tag_id=1234; SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);
3、存储引擎
InnoDB
是 MySQL 默认的事务型存储引擎,只有在须要它不支持的特性时,才考虑使用其它存储引擎。
实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,经过多版本并发控制(MVCC)+ Next-Key Locking 防止幻影读。
主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,所以对查询性能有很大的提高。
内部作了不少优化,包括从磁盘读取数据时采用的可预测性读、可以加快读操做而且自动建立的自适应哈希索引、可以加速插入操做的插入缓冲区等。
支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图须要中止对全部表的写入,而在读写混合场景中,中止写入可能也意味着中止读取。
MyISAM
设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、能够容忍修复操做,则依然可使用它。
提供了大量的特性,包括压缩表、空间数据索引等。
不支持事务。
不支持行级锁,只能对整张表加锁,读取时会对须要读到的全部表加共享锁,写入时则对表加排它锁。但在表有读取操做的同时,也能够往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。
能够手工或者自动执行检查和修复操做,可是和事务恢复以及崩溃恢复不一样,可能致使一些数据丢失,并且修复操做是很是慢的。
若是指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会当即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式能够极大的提高写入性能,可是在数据库或者主机崩溃时会形成索引损坏,须要执行修复操做。
比较
- 事务:InnoDB 是事务型的,可使用 Commit 和 Rollback 语句。
- 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。
- 外键:InnoDB 支持外键。
- 备份:InnoDB 支持在线热备份。
- 崩溃恢复:MyISAM 崩溃后发生损坏的几率比 InnoDB 高不少,并且恢复的速度也更慢。
- 其它特性:MyISAM 支持压缩表和空间数据索引。
4、数据类型
整型
TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,通常状况下越小的列越好。
INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来讲是没有意义的。
浮点数
FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,可是不支持 DECIMAl 类型的计算,所以 DECIMAL 的计算比浮点类型须要更高的代价。
FLOAT、DOUBLE 和 DECIMAL 均可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。
字符串
主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。
VARCHAR 这种变长类型可以节省空间,由于只须要存储必要的内容。可是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操做。MyISAM 会将行拆成不一样的片断存储,而 InnoDB 则须要分裂页来使行放进页内。
在进行存储和检索时,会保留 VARCHAR 末尾的空格,而会删除 CHAR 末尾的空格。
时间和日期
MySQL 提供了两种类似的日期时间类型:DATETIME 和 TIMESTAMP。
1. DATETIME
可以保存从 1000 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。
它与时区无关。
默认状况下,MySQL 以一种可排序的、无歧义的格式显示 DATETIME 值,例如“2008-01-16 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。
2. TIMESTAMP
和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年到 2038 年。
它和时区有关,也就是说一个时间戳在不一样的时区所表明的具体时间是不一样的。
MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。
默认状况下,若是插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。
应该尽可能使用 TIMESTAMP,由于它比 DATETIME 空间效率更高。
5、切分
水平切分
水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。
当一个表的数据不断增多时,Sharding 是必然的选择,它能够将数据分布到集群的不一样节点上,从而缓存单个数据库的压力。
垂直切分
垂直切分是将一张表按列切分红多个表,一般是按照列的关系密集程度进行切分,也能够利用垂直切分将常常被使用的列和不常常被使用的列切分到不一样的表中。
在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不一样的库中,例如将原来的电商数据库垂直切分红商品数据库、用户数据库等。
Sharding 策略
- 哈希取模:hash(key) % N;
- 范围:能够是 ID 范围也能够是时间范围;
- 映射表:使用单独的一个数据库来存储映射关系。
Sharding 存在的问题
1. 事务问题
使用分布式事务来解决,好比 XA 接口。
2. 链接
能够将原来的链接分解成多个单表查询,而后在用户程序中进行链接。
3. ID 惟一性
- 使用全局惟一 ID(GUID)
- 为每一个分片指定一个 ID 范围
- 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)
6、复制
主从复制
主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。
- binlog 线程 :负责将主服务器上的数据更改写入二进制日志(Binary log)中。
- I/O 线程 :负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)。
- SQL 线程 :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。
读写分离
主服务器处理写操做以及实时性要求比较高的读操做,而从服务器处理读操做。
读写分离能提升性能的缘由在于:
- 主从服务器负责各自的读和写,极大程度缓解了锁的争用;
- 从服务器可使用 MyISAM,提高查询性能以及节约系统开销;
- 增长冗余,提升可用性。
读写分离经常使用代理方式来实现,代理服务器接收应用层传来的读写请求,而后决定转发到哪一个服务器。