在设计数据库结构的时候,要尽量遵守三范式。如果不遵守,必须有足够的理由。比如性能,事实上我们经常会为了性能而妥协数据库的设计。
MySQL 服务器通过权限表来控制用户对数据库的访问,权限表存放在 MySQL 数据库里,由mysql_install_db
脚本初始化。这些权限表分别user
,db
,table_priv
,columns_priv
和host
。下面分别介绍一下这些表的结构和内容:
user权限表
:记录允许连接到服务器的用户帐号信息,里面的权限是全局级的。db权限表
:记录各个帐号在各个数据库上的操作权限。table_priv权限表
:记录数据表级的操作权限。columns_priv权限表
:记录数据列级的操作权限。host权限表
:配合 db 权限表对给定主机上数据库级操作权限作更细致的控制。这个权限表不受 GRANT 和 REVOKE 语句的影响。有三种格式,statement
,row
和 mixed
。
frm(表结构定义)
,.MYI(索引)
,.MYD(数据)
;ibd
和 frm(表结构定义)
。索引是一种数据结构
。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用 B 树及其变种 B+树。更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。
索引的优点:
索引的缺点:
主键索引:
数据列不允许重复,不允许为 NULL,一个表只能有一个主键。
唯一索引:
数据列不允许重复,允许为 NULL 值,一个表允许多个列创建唯一索引。
ALTER TABLE table_name ADD UNIQUE (column);
创建唯一索引。普通索引:
基本的索引类型,没有唯一性的限制,允许为 NULL 值。
ALTER TABLE table_name ADD INDEX index_name (column);
创建普通索引 ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);
创建组合索引。全文索引:
: 是目前搜索引擎使用的一种关键技术。
ALTER TABLE table_name ADD FULLTEXT (column);
创建全文索引。表级锁
:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。行级锁
:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。页面锁
:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。SQL 标准定义的四个隔离级别为:
表格的每一行都由主键唯一标识,一个表只有一个主键。
主键也是候选键。按照惯例,候选键可以被指定为主键,并且可以用于任何外键引用。
UNIX_TIMESTAMP
:是从 MySQL 时间戳转换为 Unix 时间戳的命令。FROM_UNIXTIME
:是从 Unix 时间戳转换为 MySQL 时间戳的命令。每个 MyISAM 表格以三种格式存储在磁盘上:
.frm文件
: 存储表定义.MYD文件
:数据文件,具有(MYData)扩展名.MYI文件
: 索引文件,具有(MYIndex)扩展名NUMERIC 和 DECIMAL 类型被 MySQL 实现为同样的类型,这在 SQL92 标准允许。
他们被用于保存值,该值的准确精度是极其重要的值,例如与金钱有关的数据。当声明一个类是这些类型之一时,精度和规模的能被(并且通常是)指定。
例如:
在这个例子中,9(precision)代表将被用于存储值的总的小数位数,而2(scale)代表将被用于存储小数点后的位数。
因此,在这种情况下,能被存储在salary
列中的值的范围是从-9999999.99 到9999999.99
。
非空字段
:应该指定列为 NOT NULL,除非你想存储 NULL。在 MySQL 中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。应该用 0、一个特殊的值或者一个空串代替空值;
取值离散大的字段
:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过 count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高;
索引字段越小越好
:数据库的数据存储以页为单位一页存储的数据越多一次 I/O 操作获取的数据越大效率越高
通常,通过索引查询数据比全表扫描要快。但是我们也必须注意到它的代价。
索引需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时,索引本身也会被修改。
这意味着每条记录的 INSERT,DELETE,UPDATE将为此多付出 4,5 次的磁盘 I/O。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。
使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加、修改、删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的 IO,会降低增、改、删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询 MySQL 官方手册得知删除数据的速度和创建的索引数量是成正比的。
先删除索引
(此时大概耗时三分多钟)删除其中无用数据
(此过程需要不到两分钟)顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where 子句中使用最频繁的一列放在最左边。
最左前缀匹配原则,非常重要的原则,MySQL 会一直向右匹配直到遇到范围查询(>、<、between、like)
就停止匹配,比如:
a = 1 and b = 2 and c > 3 and d = 4
如果建立(a,b,c,d)顺序的索引,d 是用不到索引的,如果建立(a, b, d, c)的索引则都可以用到,a, b, d
的顺序可以任意调整。
= 和 in
可以乱序,比如 a = 1 and b = 2 and c = 3
建立(a, b, c)索引可以任意顺序,MySQL 的查询优化器会帮你优化成索引可以识别的形式。
聚簇索引
:将数据存储与索引放到了一块,找到索引也就找到了数据
非聚簇索引
:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam 通过key_buffer
把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer
命中时,速度慢的原因。
首先需要在 MySQL 客户端登陆才能使用,所以需要个连接器来连接用户和 MySQL 数据库。
我们一般是使用mysql -u用户名 -p密码
来进行 MySQL 登陆,和服务端建立连接。
在完成 TCP 握手后,连接器会根据你输入的用户名和密码验证你的登录身份。如果用户名或者密码错误,MySQL 就会提示 Access denied for user
,来结束执行。如果登录成功后,MySQL 会根据权限表中的记录来判定你的权限。
连接完成后,你就可以执行 SQL 语句了,这行逻辑就会来到第二步:查询缓存。
MySQL 在得到一个执行请求后,会首先去查询缓存 中查找,是否执行过这条SQL 语句,之前执行过的语句以及结果会以 key-value 对的形式,被直接放在内存中。key 是查询语句,value 是查询的结果。
如果通过 key 能够查找到这条 SQL 语句,就直接返回 SQL 的执行结果。
如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果就会被放入查询缓存中。
可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,效率会很高。
如果没有命中查询,就开始执行真正的 SQL 语句。
首先,MySQL 会根据你写的 SQL 语句进行解析,分析器会先做词法分析,你写的 SQL 就是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串是什么,代表什么。
然后进行语法分析,根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。如果 SQL 语句不正确,就会提示You have an error in your SQL syntax
。
经过分析器的词法分析和语法分析后,你这条 SQL 就合法了,MySQL 就知道你要做什么了。但是在执行前,还需要进行优化器的处理,优化器会判断你使用了哪种索引,使用了何种连接,优化器的作用就是确定效率最高的执行方案。
MySQL 通过分析器知道了你的 SQL 语句是否合法,你想要做什么操作,通过优化器知道了该怎么做效率最高,然后就进入了执行阶段,开始执行这条 SQL 语句在执行阶段
MySQL 首先会判断你有没有执行这条语句的权限,没有权限的话,就会返回没有权限的错误。
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。对于有索引的表,执行的逻辑也差不多。
什么是临时表?
:MySQL 在执行 SQL 语句的过程中 通常会临时创建一些存储中间结果集的表。
临时表只对当前连接可见,在连接关闭时,临时表会被删除并释放所有表空间。
临时表分为两种
:
什么区别呢?
:
MySQL 会在下面这几种情况产生临时表。
使用 UNION 查询:UNION 有两种,一种是 UNION,一种是 UNION ALL,它们都用于联合查询;区别是使用 UNION 会去掉两个表中的重复数据,相当于对结果集做了一下 去重(distinct)。使用 UNIONALL,则不会排重,返回所有的行。使用 UNION 查询会产生临时表。
使用 TEMPTABLE 算法或者是 UNION 查询中的视图。TEMPTABLE 算法是一种创建临时表的算法,它是将结果放置到临时表中,意味这要 MySQL 要先创建好一个临时表,然后将结果放到临时表中去,然后再使用这个临时表进行相应的查询。
ORDER BY 和 GROUPBY 的子句不一样时也会产生临时表。
DISTINCT 查询并且加上 ORDER BY 时;
SQL 中用到 SQL_SMALL_RESULT 选项时;如果查询结果比较小的时候,可以加上 SQL SMALL RESULT 来优化,产生临时表
FROM 中的子查询;
EXPLAIN 查看执行计划结果的 Extra 列中,如果使用 Using Temporary 就表示会用到临时表
查询语句无论是使用哪种判断条件等于、小于、大于,WHERE 左侧的条件查询字段不要使用函数或者表达式
使用 EXPLAIN 命令优化你的 SELECT 查询,对于复杂、效率低的 SQL 语句,我们通常是使用 explainsql 来分析这条 SQL 语句,这样方便我们分析,进行优化。
当你的 SELECT 查询语句只需要使用一条记录时,要使用 LIMIT 1。不要直接使用 SELECT*,而应该使用具体需要查询的表字段,因为使用 EXPLAIN进行分析时,SELECT"使用的是全表扫描,也就是 type =all 。
为每一张表设置一个 ID 属性。
避免在 MHERE 字句中对字段进行 NULL
判断避免在 WHERE 中使用!或>操作符
使用 BETWEEN AND 替代 IN
为搜索字段创建索引
选择正确的存储引擎,InnoDB、MyISAM、MEMORY 等
使用 LIKE%abc%不会走索引,而使用 LIKE abc%会走索引。
对于枚举类型的字段(即有固定罗列值的字段),建议使用 ENUM 而不是VARCHAR,如性别、星期、类型、类别等。
拆分大的 DELETE 或 INSERT 语句
选择合适的字段类型,选择标准是尽可能小、尽可能定长、尽可能使用整数。
字段设计尽可能使用 NOT NULL
进行水平切割或者垂直分割
外连接分为三种,分别是是:
左外连接(LEFT OUTER J0IN 或 LEFT JOIN)
:又称为左连接,这种连接方式会显示左表不符合条件的数据行,右边不符合条件的数据行直接显示 NULL。
右外连接(RIGHT OUTER JOIN 或 RIC GHT JOIN)
: 也被称为右连接,他与左连接相对,这种连接方式会显示右表不符合条件的数据行,左表不符合条件的数据行直接显示 NULL。
全外连接(FULL OUTER JOIN 或 FULLJOIN)
。
结合两个表中相同的字段,返回关联字段相符的记录就是内链接。
通过 union 连接的 SQL 分别单独取出的列数必须相同。
使用 union 时,多个相等的行将会被合并,由于合升比较耗时,一般不直接使用 union 进行合并,而是通常采用 union all 进行合并。
在 5.1 版本之前,MyISAM 是 MySQL 的默认存储引擎,MylSAM 并发性比较差,使
用的场景比较少主要特点是:
不支持事务操作,ACID 的特性也就不存在了,这一设计是为了性能和效率考虑的。
不支持外键操作,如果强行增加外键,MySQL 不会报错,只不过外键不起作用。
MyISAM 默认的锁粒度是表级锁,所以并发性能比较差,加锁比较快,锁冲突比较少,不太容易发生死锁的情况。
MyISAM 会在磁盘上存储三个文件,文件名和表名相同,扩展名分别是frm(存储表定义)
、MYD(MYData,存储数据)
、MYI(MyIndex,存储索引)
。这里需要特别注意的是 MyISAM 只缓存 索引文件,并不缓存数据文件。
MyISAM 支持的索引类型有全局索引(Full-Text)、B-Tree 索引、R-Tree 索引
数据库所在主机如果宕机,MyISAM 的数据文件容易损坏,而且难以恢复。
增删改查性能方面:SELECT 性能较高,适用于查询较多的情况
自从 MySQL5.1 之后,默认的存储引擎变成了 InnoDB 存储引擎,相对于MylSAM,InnoDB 存储引擎有了较大的改变,它的主要特点是:
支持事务操作,具有事务 ACID 隔离特性,默认的隔离级别是可重复读(repetable-read)、通过 MVCC(并发版本控制)来实现的。能够解决脏读
和不可重复读
的问题。 InnoDB 支持外键操作。
InnoDB 默认的锁粒度行级锁,并发性能比较好,会发生死锁的情况。
和 MyISAM 一样的是,InnoDB 存储引擎也有 frm 文件存储表结构定义,但是不同的是,InnoDB 的表数据与索引数据是存储在一起的,都位于 B+数的叶子节点上,而 MylSAM 的表数据和索引数据是分开的。
InnoDB 有安全的日志文件,这个日志文件用于恢复因数据库崩溃或其他情况导致的数据丢失问题,保证数据的一致性。
InnoDB 和 MylSAM 支持的索引类型相同,但具体实现因为文件结构的不同有很大差异。
增删改查性能方面,果执行大量的增删改操作,推荐使用 InnoDB 存储引擎,它在删除操作时是对行删除,不会重建表。
MyISAM:
(1) 不支持事务,但是每次查询都是原子的;
(2) 支持表级锁,即每次操作是对整个表加锁;
(3) 存储表的总行数;
(4) 一个 MYISAM 表有三个文件:索引文件、表结构文件、数据文件;
(5) 采用非聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性。
InnoDb:
(1) 支持 ACID 的事务,支持事务的四种隑离级别;
(2) 支持行级锁及外键约束:因此可以支持写并发;
(3) 不存储总行数:
(4) 一个 InnoDb 引擎存储在一个文件空间(共享表空间,表大小丌叐操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空间,表大小不受操作系统文件大小限制,一般为2G),受操作系统文件大小的限制;
(5) 主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。