MySQL 进阶 (1) -- MySQL 概念

文章目录

    • 1. 非关系型数据库和关系型数据库区别
    • 2. MySQL 的架构
    • 3. MySQL 常见存储引擎
      • 1. InnoDB 存储引擎
      • 2. MyISAM 存储引擎
      • 3. MEMORY 存储引擎
    • 4. MyISAM 与 InnoDB 的比较
    • 5. 临时表
    • 6. MySQL 元数据
    • 7. MySQL 常见索引类型
    • 8. SQL 语句执行顺序
    • 9. SQL 注入

1. 非关系型数据库和关系型数据库区别

非关系型数据库称为 NoSQL,也就是 Not Only SQL,不仅仅是 SQL。非关系型数据库不需要写一些复杂的 SQL 语句,其内部存储方式是以 key-value 的形式存在。可以把它想象成电话本的形式,每个人名(key)对应电话(value)。常见的非关系型数据库主要有 Hbase、Redis、MongoDB 等。非关系型数据库不需要经过 SQL 的重重解析,所以性能很高;非关系型数据库的可扩展性比较强,数据之间没有耦合性,遇见需要新加字段的需求,就直接增加一个 key-value 键值对即可。

关系型数据库以表格的形式存在,以行和列的形式存取数据,关系型数据库这一系列的行和列被称为表,无数张表组成了数据库,常见的关系型数据库有 Oracle、DB2、Microsoft SQL Server、MySQL等。关系型数据库能够支持复杂的 SQL 查询,能够体现出数据之间、表之间的关联关系;关系型数据库也支持事务,便于提交或者回滚。

它们之间的劣势都是基于对方的优势来满足的。

2. MySQL 的架构

MySQL 进阶 (1) -- MySQL 概念_第1张图片
大致上来说,MySQL 可以分为 Server层和 存储引擎层。

Server 层包括连接器、查询缓存、分析器、优化器、执行器,包括大多数 MySQL 中的核心功能,所有跨存储引擎的功能也在这一层实现,包括 存储过程、触发器、视图等。

存储引擎层包括 MySQL 常见的存储引擎,包括 MyISAM、InnoDB 和 Memory 等,最常用的是 InnoDB,也是现在 MySQL 的默认存储引擎。存储引擎也可以在创建表的时候手动指定,比如:CREATE TABLE t (i INT) ENGINE = ;

然后我们就可以探讨 MySQL 的执行过程了

  1. 连接器
    首先需要在 MySQL 客户端登陆才能使用,所以需要一个连接器来连接用户和 MySQL 数据库,我们一般是使用:mysql -u 用户名 -p 密码 来进行 MySQL 登陆,和服务端建立连接。在完成 TCP 握手后,连接器会根据你输入的用户名和密码验证你的登录身份。如果用户名或者密码错误,MySQL 就会提示 Access denied for user,来结束执行。如果登录成功后,MySQL 会根据权限表中的记录来判定你的权限。

  2. 查询缓存
    连接完成后,你就可以执行 SQL 语句了,这行逻辑就会来到第二步:查询缓存。
    .
    MySQL 在得到一个执行请求后,会首先去 查询缓存 中查找,是否执行过这条 SQL 语句,之前执行过的语句以及结果会以 key-value 对的形式,被直接放在内存中。key 是查询语句,value 是查询的结果。如果通过 key 能够查找到这条 SQL 语句,就直接返回 SQL 的执行结果。
    .
    如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果就会被放入查询缓存中。可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,效率会很高。
    MySQL 进阶 (1) -- MySQL 概念_第2张图片
    但是不建议使用查询缓存:为什么呢?因为只要在 MySQL 中对某一张表执行了更新操作,那么所有的查询缓存就会失效,对于更新频繁的数据库来说,查询缓存的命中率很低。

  3. 分析器
    如果没有命中查询,就开始执行真正的 SQL 语句。

    1. 首先,MySQL 会根据你写的 SQL 语句进行解析,分析器会先做 词法分析,你写的 SQL 就是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串是什么,代表什么。
    2. 然后进行 语法分析,根据词法分析的结果, 语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。如果 SQL 语句不正确,就会提示 You have an error in your SQL syntax
  4. 优化器
    经过分析器的词法分析和语法分析后,你这条 SQL 就合法了,MySQL 就知道你要做什么了。但是在执行前,还需要进行优化器的处理,优化器会判断你使用了哪种索引,使用了何种连接,优化器的作用就是确定效率最高的执行方案。生成执行计划。

  5. 执行器
    MySQL 通过分析器知道了你的 SQL 语句是否合法,你想要做什么操作,通过优化器知道了该怎么做效率最高,然后就进入了执行阶段,开始执行这条 SQL 语句。
    .
    在执行阶段,MySQL 首先会判断你有没有执行这条语句的权限,没有权限的话,就会返回没有权限的错误。如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。对于有索引的表,执行的逻辑也差不多。
    .
    至此,MySQL 对于一条语句的执行过程也就完成了。

3. MySQL 常见存储引擎

MySQL 常见的存储引擎,可以使用 SHOW ENGINES 命令,来列出所有的存储引擎
MySQL 进阶 (1) -- MySQL 概念_第3张图片

1. InnoDB 存储引擎

自从 MySQL 5.1 之后,默认的存储引擎变成了 InnoDB 存储引擎,相对于 MyISAM,InnoDB 存储引擎有了较大的改变,它的主要特点是

  1. 支持事务操作,具有事务 ACID 隔离特性,默认的隔离级别是可重复读(repetable-read)、通过MVCC(并发版本控制)来实现的。能够解决脏读和不可重复读的问题。
  2. InnoDB 支持外键操作。
  3. InnoDB 默认的锁粒度行级锁,并发性能比较好,会发生死锁的情况。
  4. 和 MyISAM 一样的是,InnoDB 存储引擎也有 .frm文件存储表结构 定义,但是不同的是,InnoDB 的表数据与索引数据是存储在一起的,都位于 B+ 数的叶子节点上,而 MyISAM 的表数据和索引数据是分开的。
  5. InnoDB 有安全的日志文件,这个日志文件用于恢复因数据库崩溃或其他情况导致的数据丢失问题,保证数据的一致性。
  6. InnoDB 和 MyISAM 支持的索引类型相同,但具体实现因为文件结构的不同有很大差异。
  7. 增删改查性能方面,如果执行大量的增删改操作,推荐使用 InnoDB 存储引擎,它在删除操作时是对行删除,不会重建表。

2. MyISAM 存储引擎

在 5.1 版本之前,MyISAM 是 MySQL 的默认存储引擎,MyISAM 并发性比较差,使用的场景比较少,主要特点是

  1. 不支持事务操作,ACID 的特性也就不存在了,这一设计是为了性能和效率考虑的。
  2. 不支持外键操作,如果强行增加外键,MySQL 不会报错,只不过外键不起作用。
  3. MyISAM 默认的锁粒度是表级锁,所以并发性能比较差,加锁比较快,锁冲突比较少,不太容易发生死锁的情况。
  4. MyISAM 会在磁盘上存储三个文件,文件名和表名相同,扩展名分别是 .frm(存储表定义)、.MYD(MYData,存储数据)、MYI(MyIndex,存储索引)。这里需要特别注意的是 MyISAM 只缓存索引文件,并不缓存数据文件。
  5. MyISAM 支持的索引类型有 全局索引(Full-Text)、B-Tree 索引、R-Tree 索引
    1. Full-Text 索引:它的出现是为了解决针对文本的模糊查询效率较低的问题。
    2. B-Tree 索引:所有的索引节点都按照平衡树的数据结构来存储,所有的索引数据节点都在叶节点
    3. R-Tree索引:它的存储方式和 B-Tree 索引有一些区别,主要设计用于存储空间和多维数据的字段做索引,目前的 MySQL 版本仅支持 geometry 类型的字段作索引,相对于 BTREE,RTREE 的优势在于范围查找。
  6. 数据库所在主机如果宕机,MyISAM 的数据文件容易损坏,而且难以恢复。
  7. 增删改查性能方面:SELECT 性能较高,适用于查询较多的情况

3. MEMORY 存储引擎

MEMORY 是 MySQL 中一类特殊的存储引擎,它使用存储在内容中的内容来创建表,而且数据全部放在内存中。每个基于 MEMORY 存储引擎的表实际对应一个磁盘文件。该文件的文件名与表名相同,类型为 frm 类型。而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。MEMORY 用到的很少,因为它是把数据存到内存中,如果内存出现异常就会影响数据。如果重启或者关机,所有数据都会消失。因此,基于 MEMORY 的表的生命周期很短,一般是一次性的。

4. MyISAM 与 InnoDB 的比较

  1. InnoDB 支持事务,是事务型数据库的首选存储引擎,支持行级锁,支持外键,支持在线热备份,表空间大,可以缓存索引也可以缓存数据,对内存要求较高,内存的大小对其性能有决定性影响,MySQL 5.5.8 版本开始的默认存储引擎,MySQL 5.6.4 开始,innodb也开始支持全文索引(innodb 1.2.x 版本开始支持全文索引),关注点是事务,默认安装
  2. MyISAM 不支持事务,只支持表级锁,不支持外键,表空间小,只缓存索引,数据需要依赖操作系统缓存,提供了很多特性,像全文索引,压缩表,空间数据索引等等,关注点是性能,其查询速度很快,默认安装

注意:

  1. 锁粒度方面:由于锁粒度不同,InnoDB 比 MyISAM 支持更高的并发;InnoDB 的锁粒度为行锁、MyISAM 的锁粒度为表锁、行锁需要对每一行进行加锁,所以锁的开销更大,但是能解决脏读和不可重复读的问题,相对来说也更容易发生死锁
  2. 可恢复性上:由于 InnoDB 是有事务日志的,所以在产生由于数据库崩溃等条件后,可以根据日志文件进行恢复。而 MyISAM 则没有事务日志。
  3. 查询性能上:MyISAM 要优于 InnoDB,因为 InnoDB 在查询过程中,是需要维护数据缓存,而且查询过程是先定位到行所在的数据块,然后在从数据块中定位到要查找的行;而 MyISAM 可以直接定位到数据所在的内存地址,可以直接找到数据。
  4. 表结构文件上:MyISAM 的表结构文件包括:.frm(表结构定义),.MYI(索引),.MYD(数据);而 InnoDB 的表数据文件为:.ibd和.frm(表结构定义);

5. 临时表

什么是临时表?MySQL 在执行 SQL 语句的过程中,通常会临时创建一些存储中间结果集的表,临时表只对当前连接可见,在连接关闭时,临时表会被删除并释放所有表空间。

临时表分为两种:一种是内存临时表,一种是磁盘临时表,什么区别呢?内存临时表使用的是 MEMORY 存储引擎,而磁盘临时表采用的是 MyISAM 存储引擎。

MySQL 会在下面这几种情况产生临时表:

  1. 使用 UNION 查询:UNION 有两种,一种是UNION ,一种是 UNION ALL ,它们都用于联合查询;区别是 使用 UNION 会去掉两个表中的重复数据,相当于对结果集做了一下去重(distinct)。使用 UNION ALL,则不会排重,返回所有的行。使用 UNION 查询会产生临时表。
  2. 使用 TEMPTABLE 算法或者是 UNION 查询中的视图。TEMPTABLE 算法是一种创建临时表的算法,它是将结果放置到临时表中,意味这要 MySQL 要先创建好一个临时表,然后将结果放到临时表中去,然后再使用这个临时表进行相应的查询。
  3. ORDER BY 和 GROUP BY 的子句不一样时也会产生临时表。
  4. DISTINCT 查询并且加上 ORDER BY 时;
  5. SQL 用到 SQL_SMALL_RESULT 选项时;如果查询结果比较小的时候,可以加上 SQL_SMALL_RESULT 来优化,产生临时表
  6. FROM 中的子查询;
  7. EXPLAIN 查看执行计划结果的 Extra 列中,如果使用 Using Temporary 就表示会用到临时表。

6. MySQL 元数据

一些获取服务器元数据的命令
MySQL 进阶 (1) -- MySQL 概念_第4张图片

7. MySQL 常见索引类型

索引是存储在一张表中特定列上的数据结构,索引是在列上创建的。并且,索引是一种数据结构。

在 MySQL 中,主要有下面这几种索引:

  1. 全局索引(FULLTEXT):全局索引,目前只有 MyISAM 引擎支持全局索引,它的出现是为了解决针对文本的模糊查询效率较低的问题。
  2. 哈希索引(HASH):哈希索引是 MySQL 中用到的唯一 key-value 键值对的数据结构,很适合作为索引。HASH 索引具有一次定位的好处,不需要像树那样逐个节点查找,但是这种查找适合应用于查找单个键的情况,对于范围查找,HASH 索引的性能就会很低。
  3. B-Tree 索引:B 就是 Balance 的意思,BTree 是一种平衡树,它有很多变种,最常见的就是 B+ Tree,它被 MySQL 广泛使用。
  4. R-Tree 索引:R-Tree 在 MySQL 很少使用,仅支持 geometry 数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种,相对于 B-Tree 来说,R-Tree 的优势在于范围查找。

8. SQL 语句执行顺序

from -> on -> join -> where -> group by( 开始使用select中的别名,后面的语句中都可以使用 ) -> avg,sum… -> having -> select -> distinct -> order by

案例:

SELECT DISTINCT
    < select_list >
FROM
    < left_table > < join_type >
JOIN < right_table > ON < join_condition >
WHERE
    < where_condition >
GROUP BY
    < group_by_list >
HAVING
    < having_condition >
ORDER BY
    < order_by_condition >
LIMIT < limit_number >
  1. FROM 连接
    首先,对 SELECT 语句执行查询时,对 FROM 关键字两边的表执行连接,会形成笛卡尔积,这时候会产生一个虚表VT1(virtual table)
    .

    虚表:在 MySQL 中,有三种类型的表,一种是永久表,永久表就是创建以后用来长期保存数据的表;一种是临时表,临时表也有两类,一种是,只保存临时数据,但和永久表一样,能够长久存在的;还有一种是临时创建的,SQL 语句执行完成就会删除。一种是虚表,虚表其实就是视图,数据可能会来自多张表的执行结果。

  2. ON 过滤
    然后对 FROM 连接的结果进行 ON 筛选,创建虚表 VT2,把符合记录的条件存在 VT2 中。

  3. JOIN 连接
    第三步,如果是 OUTER JOIN(left join、right join) ,那么这一步就将添加外部行,如果是 left join 就把 ON 过滤条件的左表添加进来,如果是 right join ,就把右表添加进来,从而生成新的虚拟表 VT3。

  4. WHERE 过滤
    第四步,是执行 WHERE 过滤器,对上一步生产的虚拟表引用 WHERE 筛选,生成虚拟表 VT4。

    WHERE 和 ON 的区别

    1. 如果有外部列,ON 针对过滤的是关联表,主表(保留表)会返回所有的列;
    2. 如果没有添加外部列,两者的效果是一样的;
      .

    应用

    1. 对主表的过滤应该使用 WHERE;
    2. 对于关联表,先条件查询后连接则用 ON,先连接后条件查询则用 WHERE;
  5. GROUP BY
    根据 group by 字句中的列,会对 VT4 中的记录进行分组操作,产生虚拟机表 VT5。如果应用了group by,那么后面的所有步骤都只能得到的 VT5 的列或者是聚合函数(count、sum、avg等)。

  6. HAVING
    紧跟着 GROUP BY 字句后面的是 HAVING,使用 HAVING 过滤,会把符合条件的放在 VT6

  7. SELECT
    第七步才会执行 SELECT 语句,将 VT6 中的结果按照 SELECT 进行刷选,生成 VT7

  8. DISTINCT
    在第八步中,会对 TV7 生成的记录进行去重操作,生成 VT8。事实上如果应用了 group by 子句那么 distinct 是多余的,原因同样在于,分组的时候是将列中唯一的值分成一组,同时只为每一组返回一行记录,那么所以的记录都将是不相同的。

  9. ORDER BY
    应用 order by 子句。按照 order_by_condition 排序 VT8,此时返回的一个游标,而不是虚拟表。sql 是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。
    .
    SQL 语句执行的过程如下:
    MySQL 进阶 (1) -- MySQL 概念_第5张图片

9. SQL 注入

所谓SQL注入,就是通过把SQL命令插入到 Web 表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的 SQL 命令。

我们永远不要信任用户的输入,我们必须认定用户输入的数据都是不安全的,我们都需要对用户输入的数据进行过滤处理。

以下实例中,输入的用户名必须为字母、数字及下划线的组合,且用户名长度为 8 到 20 个字符之间:

if (preg_match("/^\w{8,20}$/", $_GET['username'], $matches))
{
   $result = mysql_query("SELECT * FROM users 
                          WHERE username=$matches[0]");
}
 else 
{
   echo "username 输入异常";
}

让我们看下在没有过滤特殊字符时,出现的SQL情况:

// 设定$name 中插入了我们不需要的SQL语句
$name = "Qadir'; DELETE FROM users;";
mysql_query("SELECT * FROM users WHERE name='{$name}'");

以上的注入语句中,我们没有对 $name 的变量进行过滤,$name 中插入了我们不需要的SQL语句,将删除 users 表中的所有数据。

在PHP中的 mysql_query() 是不允许执行多个SQL语句的,但是在 SQLite 和 PostgreSQL 是可以同时执行多条SQL语句的,所以我们对这些用户的数据需要进行严格的验证。

防止SQL注入,我们需要注意以下几个要点:

  1. 永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。
  2. 永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
  3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
  4. 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
  5. 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
  6. sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻击等。

防止SQL注入

  1. 可以对用户输入的数据进行转义从而来防止SQL注入。
  2. like查询时,如果用户输入的值有"“和”%",则会出现这种情况:用户本来只是想查询abcd_,查询结果中却有"abcd"、“abcde”、“abcdf"等等;用户要查询"30%”(注:百分之三十)时也会出现问题。可以使用转义符\解决。

你可能感兴趣的:(#,MySQL,数据库)