SQL基础笔记(2)- 执行过程

笔记的部分内容来自于极客时间的《SQL必知必会》
下面聊聊SQL的执行过程,分Oracle和MySQL。DBMS的原理大致一样,但有些许区别。

Oracle中SQL的执行过程

在这里插入图片描述
SQL 语句在 Oracle 中经历了以下的几个步骤。

  1. 语法检查:检查 SQL 拼写是否正确,如果不正确,Oracle 会报语法错误。

  2. 语义检查:检查 SQL 中的访问对象是否存在。比如我们在写 SELECT 语句的时候,列名写错了,系统就会提示错误。语法检查和语义检查的作用是保证 SQL 语句没有错误。

  3. 权限检查:看用户是否具备访问该数据的权限。

  4. 共享池检查:共享池(Shared Pool)是一块内存池,最主要的作用是缓存 SQL 语句和该语句的执行计划。Oracle 通过检查共享池是否存在 SQL 语句的执行计划,来判断进行软解析,还是硬解析。那软解析和硬解析又该怎么理解呢?

    在共享池中,Oracle 首先对 SQL 语句进行 Hash 运算,然后根据 Hash 值在库缓存(Library Cache)中查找,如果存在 SQL 语句的执行计划,就直接拿来执行,直接进入“执行器”的环节,这就是软解析。

    如果没有找到 SQL 语句和执行计划,Oracle 就需要创建解析树进行解析,生成执行计划,进入“优化器”这个步骤,这就是硬解析。

  5. 优化器:优化器中就是要进行硬解析,也就是决定怎么做,比如创建解析树,生成执行计划。

  6. 执行器:当有了解析树和执行计划之后,就知道了 SQL 该怎么被执行,这样就可以在执行器中执行语句了。

    MySQL中SQL的执行过程

    MySQL 是典型的 C/S 架构,即 Client/Server 架构,整体的 MySQL 流程如下图:
    在这里插入图片描述
    SQL层的执行过程
    在这里插入图片描述

  7. 查询缓存:Server 如果在查询缓存中发现了这条 SQL 语句,就会直接将结果返回给客户端;如果没有,就进入到解析器阶段。需要说明的是,因为查询缓存往往效率不高,所以在 MySQL8.0 之后就抛弃了这个功能。

  8. 解析器:在解析器中对 SQL 语句进行语法分析、语义分析。

  9. 优化器:在优化器中会确定 SQL 语句的执行路径,比如是根据全表检索,还是根据索引来检索等。

  10. 执行器:在执行之前需要判断该用户是否具备权限,如果具备权限就执行 SQL 查询并返回结果。在 MySQL8.0 以下的版本,如果设置了查询缓存,这时会将查询结果进行缓存。

MySQL 和 Oracle 执行 SQL 的原理大致是一样的。

MySQL 的存储引擎

与 Oracle 不同的是,MySQL 的存储引擎采用了插件的形式,每个存储引擎都面向一种特定的数据库应用环境,只简说两种引擎。

  1. InnoDB 存储引擎:它是 MySQL 5.5 版本之后默认的存储引擎,最大的特点是支持事务、行级锁定、外键约束等。

  2. MyISAM 存储引擎:在 MySQL 5.5 版本之前是默认的存储引擎,不支持事务,也不支持外键,最大的特点是速度快,占用资源少。

    MySQL 中每个表的设计都可以采用不同的存储引擎

SQL基础笔记(3)- DBMS数据库管理系统相关查询语句

1. 开启SQL语句执行时间

如何知道 SQL 执行时所使用的资源情况,开启profiling

1.1 查询profiling

在这里插入图片描述

1
mysql> select @@profiling;

1.2 设置profiling

1
mysql> set profiling=1;

2. SQL执行历史查询

2.1 执行历史

在这里插入图片描述

1
mysql> show profiles;

2.2 执行过程的具体时间

1
mysql> show profile for query 4;

在这里插入图片描述

3. 查看DBMS MySQL的版本

在这里插入图片描述

1
mysql> select version();

SQL基础笔记(4)- 如何提升 SELECT 查询效率

基础查询

下图为表ums_admin表的数据例子
在这里插入图片描述

1. SELECT基础查询

1
2
-- 查询用户名和邮箱
SELECT username,email FROM ums_admin;
1
2
-- 查询表中所有数据,在生产环境中不建议这样使用
SELECT * FROM ums_admin
1
2
-- 查询时定义别名
SELECT username AS user,password AS pwd,email FROM ums_admin;

在这里插入图片描述

2. DISTINCT的使用

  1. DISTINCT 需要放到所有列名的前面,如果写成SELECT username, DISTINCT password FROM ums_admin会报错。
  2. DISTINCT 其实是对后面所有列名的组合进行去重,所以SELECT DISTINCT username, iconSELECT DISTINCT icon,返回的结果是不一样
    1
    2
    -- 去除重复行
    SELECT DISTINCT username, icon FROM ums_admin;

3. ORDER BY排序

  1. 排序的列名:ORDER BY 后面可以有一个或多个列名,如果是多个列名进行排序,会按照后面第一个列先进行排序,当第一列的值相同的时候,再按照第二列进行排序,以此类推。
  2. 排序的顺序:ORDER BY 后面可以注明排序规则,ASC 代表递增排序,DESC 代表递减排序。如果没有注明排序规则,默认情况下是按照 ASC 递增排序。我们很容易理解 ORDER BY 对数值类型字段的排序规则,但如果排序字段类型为文本数据,就需要参考数据库的设置方式了,这样才能判断 A 是在 B 之前,还是在 B 之后。比如使用 MySQL 在创建字段的时候设置为 BINARY 属性,就代表区分大小写。
  3. 非选择列排序:ORDER BY 可以使用非选择列进行排序,所以即使在 SELECT 后面没有这个列名,你同样可以放到 ORDER BY 后面进行排序。
  4. ORDER BY 的位置:ORDER BY 通常位于 SELECT 语句的最后一条子句,否则会报错。
1
SELECT DISTINCT username, email FROM ums_admin ORDER BY username DESC ,email ASC;

4. LIMIT约束返回结果数量

  • MYSQL, 使用LIMIT 3
  • SQL Server 和 Access, 使用TOP 3
  • DB2,使用FETCH FIRST 3 ROWS ONLY
  • Oracle,使用ROWNUM <=3
1
SELECT DISTINCT username, email FROM ums_admin ORDER BY username DESC ,email ASC LIMIT 2;

5.SELECT 的执行顺序

Nginx配置404自定义页面

1. 修改nginx.conf http

nginx.conf 文件http 区域添加 fastcgi_intercept_errors on;

1
2
3
http {
.......
fastcgi_intercept_errors on;

2. 配置conf server

配置error_page 404

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name www.zhangguoye.com;
index index.html index.htm;
root /home/wwwroot;

error_page 404 /404.html;
location = /404.html {
root /home/wwwroot/; # 在此目录下添加自定义的404.html
}

3. 重启nginx

使用restart 或者 reload 重启nginx,使配置生效。

Nginx配置大文件上传

nginx问题

遇到的问题:

  • Nginx: 413 – Request Entity Too Large Error and Solution
  • TIMEOUT

解决方法

解决方法:在nginx的配置文件下(通常为xxx.conf),加上以下配置:

1
2
3
4
5
6
client_max_body_size     50m;  # 限制请求体的大小,若超过所设定的大小,返回413错误,默认1m
client_header_timeout 1m; # 读取请求头的超时时间,若超过所设定的大小,返回408错误
client_body_timeout 1m; # 读取请求实体的超时时间,若超过所设定的大小,返回413错误
proxy_connect_timeout 60s; # http请求无法立即被容器(tomcat, netty等)处理,被放在nginx的待处理池中等待被处理。此参数为等待的最长时间,默认为60秒,官方推荐最长不要超过75秒
proxy_read_timeout 1m; # http请求被容器(tomcat, netty等)处理后,nginx会等待处理结果,也就是容器返回的response。此参数即为服务器响应时间,默认60秒
proxy_send_timeout 1m; # http请求被服务器处理完后,把数据传返回给Nginx的用时,默认60秒
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 80;
server_name localhost;

client_max_body_size 10m;
client_header_timeout 5m;
client_body_timeout 5m;
proxy_connect_timeout 6000s;
proxy_read_timeout 5m;
proxy_send_timeout 5m;

location / {
# ...
}
}

重启nginx

设置完成后,需要使用 reload 或者reload重启nginx

如何正确设置npm start PORT端口(windows/mac)

正确设置PORT端口(windows/mac)

在跑lesson5的时候,有部分同学会遇到以下错误,这是由于不同系统,所执行的命令代码有所差异。
查看图片:​​
https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy85NDE4MjkyLTVmMDYxYjRlYzI1NWJkY2YucG5n

MAC/linux环境:

1
$ PORT=8081 npm start

使用上面命令每次都需要重新设置

如果想设置一次永久生效,使用下面的命令。

1
$ export PORT=8081
1
$ npm start

Window系统环境,按照顺序这样进行:

1
$ set PORT=8081
1
$ npm start

关闭命令行窗口后,端口的配置会失效

发布npm包,删除npm包

发布npm包

注册并在本机添加npm用户(已注册可忽略)

完成了上面的步骤之后,我们接下来要在www.npmjs.com注册一个账号,这个账号会被添加到npm本地的配置中,下面命令行将会使用到。

1
2
3
4
5
//前提已完成npm用户的注册
$ npm adduser
Username: your name
Password: your password
Email: yourmail@gmail.com

如果出现以下错误,可能是你的npm版本太低,通过sudo npm install -g npm升级一下。

1
2
3
4
5
6
7
8
9
npm WARN adduser Incorrect username or password
npm WARN adduser You can reset your account by visiting:
npm WARN adduser
npm WARN adduser http://admin.npmjs.org/reset
npm WARN adduser
npm ERR! Error: forbidden may not mix password_sha and pbkdf2
npm ERR! You may need to upgrade your version of npm:
npm ERR! npm install npm -g
npm ERR! Note that this may need to be run as root/admin (sudo, etc.)

成功之后,npm会把认证信息存储在~/.npmrc中,并且可以通过以下命令查看npm当前使用的用户:

1
$ npm whoami

以上完成之后,我们终于可以发布自己包了。

发布

1
$ npm publish

删除

1
$ npm unpublish --force //强制删除
1
$ npm unpublish guitest@1.0.1 //指定版本号
1
$ npm deprecate //某些情况

如何正确的使用Promise

promise用法

对比传统回调函数与Pormise的写法

传统回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 声明函数
function run(callback) {
let parmas = 0;
if (callback) callback(parmas);
};
function fnStep1(callback) {
let parmas = 123;
if (callback) callback(parmas);
};
function fnStep2(callback) {
let parmas = 456;
if (callback) callback(parmas);
};
function fnStep3(callback) {
let parmas = 789;
if (callback) callback(parmas);
};
// fnStep4 ...

// 传统使用回调的写法
run(function (parmas) {
// parmas = 0
console.log(parmas);
fnStep1(function (parmas1) {
// parmas = 123
console.log(parmas1);
fnStep2(function (parmas2) {
// parmas = 456
console.log(parmas2);
fnStep3(function (parmas3) {
// ...
// 一直嵌套
});
});
});
});

Promise的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

let p = new Promise((resolve, reject) => {
// ?异步操作,最终调用:
//
const parmas = 0;
resolve(parmas); // fulfilled
// ?或
// reject("failure reason"); // rejected
})

p
.then(
(parmas) => {
// parmas,resolve返回的值
console.log(parmas);
// 你的代码块 code...
return 123; //返回值给下一个then
}
)
.then(
(parmas) => {
// parmas,上一个then返回的值
console.log(parmas);
// 你的代码块 code...
return 456; //返回值给下一个then
}
)
.then(
(parmas) => {
// parmas,上一个then返回的值
console.log(parmas);
// 你的代码块 code...
return 789; //返回值给下一个then
}
)

Promise要比传统回调函数更简洁直观,可读性更强。

那如何使用Promise进行异步回调? 如何捕获错误?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 声明函数
function asyncFn(a) {

return new Promise((resolve, reject) => {
a += 1;
setTimeout(function () {
// 使用resolve则返回a
resolve(a);
// 使用reject则返回错误,并结束then的继续向下执行,并会跳到catch
// reject(new Error("fail"));
}, 2000);
});

}

// 执行
asyncFn(1).then(
(a) => {
// 过了2秒后接收到a值 => 2
console.log(a);

const newVal = 5;
// const newVal = {a: 5};
// const newVal = new Promise((resolve, reject) =>{});
// 返回值可以是数字,字串,对象或者是 Promise
return newVal;
}
).then(
(newVal) => {
// newVal 获得上一个then返回的值 或 返回的Promise的返回值

}
).catch(
(err)=>{
// 如用到reject,则会直接跳到此处
console.log(err)
}
);

SQL基础笔记(1)- 约定

SQL 语言功能划分

SQL 语言按照功能划分成以下的 4 个部分

  1. DDL,Data Definition Language,也就是数据定义语言

定义数据库对象,包括数据库、数据表和列。
通过使用 DDL,可以创建,删除和修改数据库和表结构。(CUD)

2. DML,Data Manipulation Language,数据操作语言
操作和数据库相关的记录。
增加、删除、修改数据表中的记录。

3. DCL,Data Control Language,数据控制语言
定义访问权限和安全级别。
4. DQL,Data Query Language,数据查询语言
语言的重中之重。
用它查询想要的记录。

SQL 大小写约定

MySQL在默认情况下是否区分大小写,也和操作系统有关。
在Linux下,MySQL对表名和数据库名是区分大小写的。
在Windows下,MySQL默认情况是不区分大小写的
Oracle中,SQL语句是不区分大小写,如果查询中有字符,是区分大小写的

关于 SQL 大小写的问题,约定下面两点:

  1. 表名、表别名、字段名、字段别名等都小写;
  2. SQL 保留字、函数名、绑定变量等都大写。

UTF8字符集

在这里插入图片描述

  • 字符集是 utf8,排序规则是utf8_general_ci,代表对大小写不敏感,如果设置为utf8_bin,代表对大小写敏感
  • 数据表的存储规则默认采用 InnoDB

是否使用外键

具体看看以下的观点,是否符合你当前的项目场景。

  • 首先,外键本身是为了实现强一致性,所以如果需要正确性>性能的话,还是建议使用外键,它可以让我们在数据库的层面保证数据的完整性和一致性。

  • 当然不用外键,你也可以在业务层进行实现。不过,这样做也同样存在一定的风险,因为这样,就会让业务逻辑会与数据具备一定的耦合性。也就是业务逻辑和数据必须同时修改。而且在工作中,业务层可能会经常发生变化。

  • 当然,很多互联网的公司,尤其是超大型的数据应用场景,大量的插入,更新和删除在外键的约束下会降低性能,同时数据库在水平拆分和分库的情况下,数据库端也做不到执行外键约束。另外,在高并发的情况下,外键的存在也会造成额外的开销。因为每次更新数据,都需要检查另外一张表的数据,也容易造成死锁。
    所以在这种情况下,尤其是大型项目中后期,可以采用业务层来实现,取消外键提高效率。

  • 不过在SQL学习之初,包括在系统最初设计的时候,还是建议你采用规范的数据库设计,也就是采用外键来对数据表进行约束。因为这样可以建立一个强一致性,可靠性高的数据库结构,也不需要在业务层来实现过多的检查。

  • 当然在项目后期,业务量增大的情况下,你需要更多考虑到数据库性能问题,可以取消外键的约束,转移到业务层来实现。而且在大型互联网项目中,考虑到分库分表的情况,也会降低外键的使用。

  • 不过在SQL学习,以及项目早期,还是建议你使用外键。在项目后期,你可以分析有哪些外键造成了过多的性能消耗。一般遵循2/8原则,会有20%的外键造成80%的资源效率,你可以只把这20%的外键进行开放,采用业务层逻辑来进行实现,当然你需要保证业务层的实现没有错误。不同阶段,考虑的问题不同。当用户和业务量增大的时候,对于大型互联网应用,也会通过减少外键的使用,来减低死锁发生的概率,提高并发处理能力。

  • 《阿里巴巴 Java 开发手册》28页第6条。SQL语句中 【强制】不得使用外键与级联,一切外键概念必须在应用层解决。 外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻 塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。

DDL思维图

在这里插入图片描述