MySQL基础

关系型数据库

       MySQL是一种典型的关系型数据库,主要用于持久化存储系统中的一些数据。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多)。关系型数据库就是一种建立在关系模型的基础上的数据库。关系型数据库中,我们的数据都被存放在了各种表中(比如用户表),表中的每一行就存放着一条数据(比如一个用户的信息)。大部分关系型数据库都使用 SQL 来操作数据库中的数据。并且,大部分关系型数据库都支持事务的四大特性(ACID——原子性、一致性、隔离性、持久性)。

MySQL基础框架

MySQL框架
MySQL 主要由下面几部分构成:
连接器: 身份认证和权限相关(登录 MySQL 的时候)。
查询缓存: 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。
分析器: 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。
优化器: 按照 MySQL 认为最优的方案去执行。
执行器: 执行语句,然后从存储引擎返回数据。 执行语句之前会先判断是否有权限,如果没有权限的话,就会报错。
插件式存储引擎 : 主要负责数据的存储和读取,采用的是插件式架构,支持 InnoDB、MyISAM、Memory 等多种存储引擎。

MySQL存储引擎

MySQL支持多种存储引擎,如:MEMORY、InnoDB、MyISAM、CSV等,默认的存储引擎是InnoDB,支持的多种存储引擎中只有InnoDB支持事务。MySQL 存储引擎采用的是插件式架构,支持多种存储引擎,我们甚至可以为不同的数据库表设置不同的存储引擎以适应不同场景的需要。存储引擎是基于表的,而不是数据库
MySQL 5.5.5 之前,MyISAM 是 MySQL 的默认存储引擎。5.5.5 版本之后,InnoDB 是 MySQL 的默认存储引擎。
**InnoDB 存储引擎详细介绍:**https://dev.mysql.com/doc/refman/8.0/en/innodb-storage-engine.html

MyISAM和InnoDB的区别

MyISAM不支持事务和行级锁,一旦奔溃无法安全恢复

MyISAM 只有表级锁(table-level locking),而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。MyISAM 一锁就是锁住了整张表,不适合并发写的情况!InnoDB 在并发写的时候,性能更牛皮!
MyISAM 不提供事务支持。InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别,具有提交(commit)和回滚(rollback)事务的能力。并且,InnoDB 默认使用的 REPEATABLE-READ(可重读)隔离级别是可以解决幻读问题发生的(基于 MVCC 和 Next-Key Lock)。
而且使用 InnoDB 的数据库在异常崩溃后,数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 redo log 。

MyISAM不支持外键

MyISAM 不支持,而 InnoDB 支持。
外键对于维护数据一致性非常有帮助,但是对性能有一定的损耗。因此,通常情况下,我们是不建议在实际生产项目中使用外键的,在业务代码中进行约束即可!

两者的索引实现不一样

MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是两者的实现方式不太一样。
InnoDB 引擎中,其数据文件本身就是索引文件。**相比 MyISAM,索引文件和数据文件是分离的,**其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录。

MySQL事务

事务

事务是逻辑上的一组操作,要么都执行,要么都不执行。
数据库事务可以保证多个对数据库的操作(也就是 SQL 语句)构成一个逻辑上的整体。构成这个逻辑上的整体的这些数据库操作遵循:要么全部执行成功,要么全部不执行 。
ACID:
原子性(Atomicity) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性(Consistency): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应是不变的;
隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性(Durabilily): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障

并发事务带来的问题

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

事务隔离级别

READ-UNCOMMITTED(读取未提交) : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。 READ-COMMITTED(读取已提交) : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读) : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化) : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)

MySQL锁

表级锁和行级锁

MyISAM 仅仅支持表级锁(table-level locking),一锁就锁整张表,这在并发写的情况下性能非常差。
InnoDB 不光支持表级锁(table-level locking),还支持行级锁(row-level locking),默认为行级锁行级锁的粒度更小,仅对相关的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说, InnoDB 的性能更高。
表级锁: MySQL 中锁定粒度最大的一种锁,是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
行级锁: MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字段加的锁。当我们执行 UPDATE、DELETE 语句时,如果 WHERE条件中字段没有命中唯一索引或者索引失效的话,就会导致扫描全表对表中的所有行记录进行加锁。
MySQL InnoDB 支持三种行锁定方式
记录锁(Record Lock) :也被称为记录锁,属于单个行记录上的锁。
间隙锁(Gap Lock) :锁定一个范围,不包括记录本身。
临键锁(Next-key Lock) :Record Lock+Gap Lock,锁定一个范围,包含记录本身。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。

共享锁和排他锁

不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类:
共享锁(S 锁) :又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
排他锁(X 锁) :又称写锁/独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。
排他锁与任何的锁都不兼容,共享锁仅和共享锁兼容。
S 锁 X 锁
S 锁 不冲突 冲突
X 锁 冲突 冲突

意向锁

意向锁来快速判断是否可以对某个表使用表锁。
意向锁是表级锁,共有两种,意向锁和共享锁和排它锁互斥(这里指的是表级别的共享锁和排他锁,意向锁不会与行级的锁互斥):
意向共享锁(Intention Shared Lock,IS 锁):事务有意向对表中的某些加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁。
意向排他锁(Intention Exclusive Lock,IX 锁):事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。
**意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,**在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。

MySQL性能优化

设计规范化优化

  • 数据库对象名命名规范
  • 数据库和表的字符集统一使用 UTF8
  • 给表和字段添加必要的注释
  • 优先选择符合存储需要的最小的数据类型
  • 尽可能把所有列定义为 NOT NULL

单表设计优化

  • 所有表必须使用 InnoDB 存储引擎,InnoDB 支持事务,支持行级锁,更好的恢复性,高并发下性能更好。
  • 尽量控制单表数据量的大小,过大会造成修改表结构,备份,恢复都会有很大的问题。
  • 经常一起使用的列放到一个表中,避免更多的关联操作。
  • 禁止在数据库中存储文件(比如图片)这类大的二进制数据,在数据库中存储文件会严重影响数据库性能,消耗过多存储空间。
    文件(比如图片)这类大的二进制数据通常存储于文件服务器,数据库只存储文件地址信息。
  • 单表不要包含过多字段,如果一个表包含过多字段的话,可以考虑将其分解成多个表,必要时增加中间表进行关联。

索引设计优化

  • 限制每张表上的索引数量,建议单张表索引不超过 5 个,索引并不是越多越好!索引可以提高效率同样可以降低效率。
    索引可以增加查询效率,但同样也会降低插入和更新的效率,甚至有些情况下会降低查询效率。
  • 禁止给表中的每一列都建立单独的索引
  • 每个 InnoDB 表必须有个主键,InnoDB 是一种索引组织表:数据的存储的逻辑顺序和索引的顺序是相同的。每个表都可以有多个索引,但是表的存储顺序只能有一种。InnoDB 是按照主键索引的顺序来组织表的
    不要使用更新频繁的列作为主键,不适用多列主键(相当于联合索引)
    不要使用 UUID,MD5,HASH,字符串列作为主键(无法保证数据的顺序增长)
    主键建议使用自增 ID 值
  • 对于频繁的查询优先考虑使用覆盖索引,覆盖索引:就是包含了所有查询字段 (where,select,order by,group by 包含的字段) 的索引
  • 充分利用表上已经存在的索引,同时避免索引失效。避免使用双%号的查询条件。如:a like ‘%123%’,(如果无前置%,只有后置%,是可以用到列上的索引的)一个 SQL 只能利用到复合索引中的一列进行范围查询。如:有 a,b,c 列的联合索引,在查询条件中有 a 列的范围查询,则在 b,c 列上的索引将不会被用到。在定义联合索引时,如果 a 列要用到范围查找的话,就要把 a 列放到联合索引的右侧,使用 left join 或 not exists 来优化 not in 操作,因为 not in 也通常会使用索引失效

SQL优化

  • 优化对性能影响较大的 SQL 语句,要找到最需要优化的 SQL 语句。要么是使用最频繁的语句,要么是优化后提高最明显的语句,可以通过查询 MySQL 的慢查询日志来发现需要进行优化的 SQL 语句;
  • **禁止使用 SELECT *** 必须使用 SELECT <字段列表> 查询
    SELECT * 消耗更多的 CPU 和 IO 以网络带宽资源
    SELECT * 无法使用覆盖索引
    SELECT <字段列表> 可减少表结构变更带来的影响
  • 禁止使用不含字段列表的 INSERT 语句
  • 避免数据类型的隐式转换
  • 避免使用子查询,可以把子查询优化为 join 操作。通常子查询在 in 子句中,且子查询中为简单 SQL(不包含 union、group by、order by、limit 从句) 时,才可以把子查询转化为关联查询进行优化。
  • 对应同一列进行or判断时,使用 in 代替 or,in 的值不要超过 500 个,in 操作可以更有效的利用索引,or 情况下很少能利用到索引。
  • 禁止使用 order by rand() 进行随机排序
    order by rand() 会把表中所有符合条件的数据装载到内存中,然后在内存中对所有数据根据随机生成的值进行排序,并且可能会对每一行都生成一个随机值,如果满足条件的数据集非常大,就会消耗大量的 CPU 和 IO 及内存资源。推荐在程序中获取一个随机值,然后从数据库中获取数据的方式。
  • WHERE 从句中禁止对列进行函数转换和计算,对列进行函数转换或计算时会导致无法使用索引
  • 拆分复杂的大 SQL 为多个小 SQL
    大 SQL 逻辑上比较复杂,需要占用大量 CPU 进行计算的 SQL
    MySQL 中,一个 SQL 只能使用一个 CPU 进行计算
    SQL 拆分后可以通过并行执行来提高处理效率
  • 程序连接不同的数据库使用不同的账号,禁止跨库查询

本文大部分知识转载自以下链接,有需要请自取!
[1]:https://javaguide.cn/database/mysql/mysql-questions-01.html#mysql-%E5%9F%BA%E7%A1%80%E6%9E%B6%E6%9E%84

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值