Windows 打开 MySQL 命令行,直接搜索程序,如下图所示:
打开之后输入密码即可。
或者进入 MySQL 安装目录,在 bin 目录下打开命令行窗口,输入 mysql -u 用户名 -p
,如下图所示:
查看MySQL提供的所有存储引擎
mysql> show engines;
从上图我们可以查看出 MySQL 当前默认的存储引擎是 InnoDB,并且在5.7版本所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。
查看MySQL当前默认的存储引擎
我们也可以通过下面的命令查看默认的存储引擎。
mysql> show variables like '%storage_engine%';
查看表的存储引擎
show table status like "table_name" ;
MyISAM 是 MySQL 的默认数据库引擎(5.5版之前)。虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但 MyISAM 不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5版本之后,MySQL 引入了 InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为 InnoDB。
大多数时候我们使用的都是 InnoDB 存储引擎,但是在某些情况下使用 MyISAM 也是合适的比如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。
区别:
READ COMMITTED
和 REPEATABLE READ
两个隔离级别下工作;MVCC可以使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。推荐阅读:MySQL-InnoDB-MVCC多版本并发控制《MySQL高性能》上面有一句话这样写到:
不要轻易相信“MyISAM比InnoDB快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知场景中,InnoDB的速度都可以让MyISAM望尘莫及,尤其是用到了聚簇索引,或者需要访问的数据都可以放入内存的应用。
一般情况下我们选择 InnoDB 都是没有问题的,但是某些情况下你并不在乎可扩展能力和并发能力,也不需要事务支持,也不在乎崩溃后的安全恢复问题的话,选择MyISAM也是一个不错的选择。但是一般情况下,我们都是需要考虑到这些问题的。
总体来说,MyISAM 适合读密集型的表,而 InnoDB 适合写密集型的表。 在数据库做主从分离的情况下,经常选择 MyISAM 引擎作为主库的存储引擎。
MySQL 索引使用的数据结构主要有BTree索引 和 哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择 BTree 索引。
MySQL 的 BTree 索引使用的是B树中的 B+Tree,但对于主要的两种存储引擎的实现方式是不同的。
推荐阅读:MySQL索引完全解读
数据表的主键列使用的就是主键索引。以 InnoDB 作为存储引擎的表,表中的数据都会有一个主键,即使你不创建主键,InnoDB 会选择一个唯一的非空索引代替。如果没有这样的索引,则 InnoDB 会选择内置6字节长的 ROWID 作为隐含的主键索引(ROWID随着行记录的写入而主键递增,这个ROWID不像ORACLE的ROWID那样可引用,是隐含的)。
二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。
唯一索引,普通索引,前缀索引等索引属于二级索引。
聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引。
在 MySQL 中,InnoDB 引擎的表的 .ibd
文件就包含了该表的索引和数据,因为 InnoDB 是把数据存放在B+树中的,而B+树的键值就是主键,在B+树的叶子节点中,存储了表中所有的数据。
优点:
数据访问更快,因为整个B+树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。
缺点:
非聚集索引即索引结构和数据分开存放的索引。二级索引属于非聚集索引。
MYISAM 引擎的表的.MYI文件包含了表的索引, 该表的索引(B+树)的每个非叶子节点存储索引, 叶子节点存储索引和索引对应数据的指针,指向.MYD文件的数据。
非聚集索引的叶子节点并不一定存放数据的指针, 因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
优点:
更新代价比聚集索引要小 。
缺点:
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢,覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
如下述 SQL 代码所示:
select name,age from user where name='hresh' and age=24
创建索引(name,age),查询数据时,就不用做回表操作。
不是所有类型的索引都可以成为覆盖索引。覆盖索引必须要存储索引列的值,而哈希索引、空间索引和全文索引等不存储索引列的值,所以 MySQL 只能使用 B-Tree 索引做覆盖索引。
在一般情况下,模糊查询都是通过 like 的方式进行查询。但是,对于海量数据,这并不是一个好办法,在 like “value%” 可以使用索引,但是对于 like “%value%” 这样的方式,执行全表查询,这在数据量小的表,不存在性能问题,但是对于海量数据,全表扫描是非常可怕的事情,所以 like 进行模糊匹配性能很差。 这种情况下,需要考虑使用全文搜索的方式进行优化。(可能存在精度问题)
全文搜索在 MySQL 中是一个 FULLTEXT 类型索引。
和常用的模糊匹配使用 like + % 不同,全文索引有自己的语法格式,使用 match 和 against 关键字,比如
select * from fulltext_test
where match(content,tag) against('xxx xxx');
MySQL 的全文索引最开始仅支持英语,因为英语的词与词之间有空格,使用空格作为分词的分隔符是很方便的。亚洲文字,比如汉语、日语、汉语等,是没有空格的,这就造成了一定的限制。不过 MySQL 5.7.6 开始,引入了一个 ngram 全文分析器来解决这个问题,并且对 MyISAM 和 InnoDB 引擎都有效。
对于全文搜索场景,更专业的做法是使用全文搜索引擎,例如 ElasticSearch 或 Solr。
推荐阅读:MySQL 之全文索引
我们首先需要了解一个新的概念:写缓存(change buffer)。它有什么作用呢?
它应用在非唯一普通索引页(non-unique secondary index page),对页进行了写操作,并不会立刻将磁盘页加载到缓冲池,而仅仅记录缓冲变更(buffer changes),等未来数据被读取时,再将数据合并(merge)恢复到缓冲池中的技术。写缓冲的目的是降低写操作的磁盘IO,提升数据库性能。
写缓存是否会出现一致性问题?
答案是不会的。原因有以下三点:
(1)数据库异常奔溃,能够从redo log中恢复数据;
(2)写缓冲不只是一个内存结构,它也会被定期刷盘到写缓冲系统表空间;
(3)数据读取时,有另外的流程,将数据合并到缓冲池;
通过上述介绍我们可以发现写缓存仅适用于普通索引,即唯一索引不合适,这是为什么呢?
对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。
要判断表中是否存在这个数据,而这必须要将数据页读入内存才能判断,如果都已经读入到内存了,那直接更新内存会更快,就没必要使用 change buffer 了。
更多详情讲解推荐阅读:写缓冲(change buffer),这次彻底懂了!!!
也可以换个角度思考 B+树中一个节点到底多大合适?
B+树中一个节点为一页或页的倍数最为合适。因为如果一个节点的大小小于1页,那么读取这个节点的时候其实也会读出1页,造成资源的浪费;如果一个节点的大小大于1页,比如1.2页,那么读取这个节点的时候会读出2页,也会造成资源的浪费;所以为了不造成浪费,所以最后把一个节点的大小控制在1页、2页、3页、4页等倍数页大小最为合适。
那么页的大小是多少呢? 在计算机中磁盘存储数据最小单元是扇区,一个扇区的大小是512字节,而文件系统(例如XFS/EXT4)它的最小单元是块,一个块的大小是4k,而对于我们的InnoDB存储引擎也有自己的最小储存单元——页(Page),一个页的大小是16K。
拓展:InnoDB一棵B+树可以存放多少行数据? 答案:约2千万。感兴趣的朋友可以阅读:面试题:InnoDB中一棵B+树能存多少行数据?
1、在经常使用在 WHERE 子句中的列上面创建索引,加快搜索速率;
2、在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
3、对于中到大型表索引都是非常有效的,但是特大型表的话维护开销会很大,不适合建索引;
4、索引经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
5、避免 WHERE 子句中对字段使用函数,如 to_date(create_time) >xxxxx
,这会造成无法命中索引。
6、在使用 InnoDB 引擎时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键;
7、索引列是否设置 null 是不影响性能的。 但是,还是不建议列上允许为空。最好限制 not null,因为 null 需要更多的存储空间并且 null 值无法参与某些运算。
8、删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗;(MySQL 5.7 可以通过查询 sys 库的 chema_unused_indexes
视图来查询哪些索引从未被使用)
9、在使用 limit offset
查询缓慢时,可以借助索引来提高性能 。
10、合理使用索引覆盖。
11、避免冗余索引。冗余索引指的是指多个索引的前缀列相同,或者在联合索引中包含了主键的索引。如果创建了索引(a,b),再创建索引(a)就是冗余索引,因为这只是前面一个索引的前缀索引,因此(a,b)也可以当作(a)来使用,但是(b,a)就不是冗余索引,索引(b)也不是,因为b不是索引(a,b)的最左前缀列。(MySQLS.7版本后,可以通过查询 sys 库的 schemal_redundant_indexes 表来查看冗余索引)
首先 MySQL 的基本存储结构是页(记录都存在页里边):
所以说,如果我们写select * from user where username = 'xxx'
这样没有进行任何优化的 sql 语句,默认会这样做:
使用索引之后
索引做了些什么可以让我们查询加快速度呢?其实就是将无序的数据变成有序(相对):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gta4CVRv-1602682747075)(https://camo.githubusercontent.com/83e4b2a638e8352a21feafeafe97cbad0fc2a335/687474703a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f31382d31302d322f353337333038322e6a7067)]
要找到id为8的记录简要步骤:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ux3TZOlM-1602682747075)(https://camo.githubusercontent.com/c63688b141c3562bbf4fb4b719ab027c6dea91e9/687474703a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f31382d31302d322f38393333383034372e6a7067)]
很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过 “目录” 就可以很快地定位到对应的页上了!(二分查找,时间复杂度近似为O(logn))
如果未使用索引,首先需要遍历双向链表来找到记录所在的页,然后再遍历所在页的单链表,找到对应的数据记录。
如果使用聚簇索引,此时非叶子节点存放主键值和指向子节点的指针,叶子节点中存放的主键值和数据,首先通过二分法来定位到记录所在的页,然后再遍历所在页的单链表,找到对应的数据记录。
如果使用非聚簇索引,此时非叶子节点存放非主键值和指向子节点的指针,叶子节点中存放的非主键值和主键值,首先通过二分法来定位到记录所在的页,然后再遍历所在页的单链表,找到对应的主键值。之后再按照聚簇索引的方式查找一遍。
MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。如User表的name和city加联合索引就是(name,city),在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:
select * from user where name=xx and city=xx ; //可以命中索引
select * from user where name=xx ; // 可以命中索引
select * from user where city=xx ; // 无法命中索引
这里需要注意的是,查询的时候如果两个条件都用上了,但是顺序不同,如 city= xx and name =xx
,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
由于最左前缀原则,在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。ORDER BY子句也遵循此规则。
1.添加PRIMARY KEY(主键索引)
ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
2.添加UNIQUE(唯一索引)
ALTER TABLE `table_name` ADD UNIQUE ( `column` )
3.添加INDEX(普通索引)
ALTER TABLE `table_name` ADD INDEX index_name ( `column` )
4.添加FULLTEXT(全文索引)
ALTER TABLE `table_name` ADD FULLTEXT ( `column`)
5.添加多列索引
ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )
执行 expalin 命令,此命令能够打印出 SQL 语句的执行计划,从而判断要执行的 SQL 语句是否能够命中索引,并做进一步调整。 在优化 SQL 查询时,最好的方式就是使用此命令来判断执行计划是否合理。 如下:
explain select * from test_user where id=1;
这里需要注意的是type、 key和l extra这3列,分别介绍如下。
关于 expalin 命令的详细讲解,推荐阅读:面试前必须知道的MySQL命令【expalin】
MySQL 自身是有查询优化器的,优化器的作用就是在一个查询所有可能的执行方式中找到其中最好的执行计划。 因此 explain 获取到的执行计划并非是固定的,它会随着数据分布情况而变动,执行计划也有可能改变。 而且当数据库计算出使用索引所耗费的时间长于全表扫描或其他操作时(比如当表中索引字段数据重复率太高),将
不会使用索引。
执行查询语句的时候,会先查询缓存。不过,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;
当 MySQL 单表记录数过大时,数据库的 CRUD 性能会明显下降,一些常见的优化措施如下:
1、限定数据的范围。增加条件限制,比如时间段查询。
2、读/写分离,主库负责写,从库负责读。
3、缓存。使用 MySQL 的查询缓存;对重量级、更新少的数据考虑、使用应用级别的缓存。
4、垂直分区,即将一表分为多表,简化表的结构,易于维护,不过会造成主键冗余,让事务变得复杂。
5、水平分区,保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。 需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以水平拆分最好分库 。
水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。
下面补充一下数据库分片的两种常见方案:
表分区是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。
1、和单个磁盘或者文件系统相比,可以存储更多数据。
2、冷热分离:表非常大且只在表的最后部分有热点数据,冷数据根据分区规则自动归档。
3、分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。
4、优化查询:在where字句中包含分区列时,分区可以大大提高查询效率
5、 可以使用分区表来避免某些特殊的瓶颈,例如 InnoDB 的单个索引的互斥访问,ext3 文件系统的 inode 锁竞争等。
查看当前 MySQL 是否支持分区的命令行: show variables like '%partition%'
。
推荐阅读:MySQL分区表使用方法
摘抄之《阿里巴巴开发手册》
1、业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
2、超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引。
说明:即使双表 join 也要注意表索引、SQL 性能。
3、在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可,即建立前缀索引。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
4、页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
说明:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
5、利用延迟关联或者子查询优化超多分页场景。
说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
正例:先快速定位需要获取的 id 段,然后再关联:
SELECT a.* FROM 表1 a, (select id from 表1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
6、SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。
说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range 还低,与全表扫描是小巫见大巫。
7、防止因字段类型不同造成的隐式转换,导致索引失效。