0%

关于本人

关于博客

  • why-how-what,理念-逻辑-知识,这就是知识的黄金圆环。

  • 博客建立只是为了监督个人学习,记录生活点滴,个人水平有限欢迎斧正。

  • 博客章节地址可能会发生变化,因为我也是在不断的归纳整理知识内容。

  • 2018年4月开始断断续续写内容,加油。

Read more »

最近在搭建测试环境,很久没接触nginx了。

阅读注意事项

  1. 需要一个域名,并且在国内备案过。
  2. 需要一台服务器(阿里ESC)这种,分配的公网ip。
  3. 申请或者购买一张证书。和域名做关联,域名解析到服务器的公网ip。注意开放服务区的80、443端口。
    1. 证书有两个文件
      1. domian.name.key domain.name.pem
    2. 吧证书上传到目标nginx服务器中。
    3. 检查服务器防火墙
1
2
3
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload

开始步骤

安装nginx

服务器使用的Centos服务器 使用yum安装nginx。

1
2
3
4
5
6
7
8
9
10
11
12
13
-> yum install -y nginx
#检查nginx安装位置信息
-> whereis nginx
nginx: /usr/sbin/nginx /usr/lib64/nginx /etc/nginx /usr/share/nginx /usr/share/man/man3/nginx.3pm.gz /usr/share/man/man8/nginx.8.gz
# 实际上是安装到/etc/nginx里面了
# 设置开机自动启动nginx
-> sudo systemctl enable nginx
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.
# 切换到nginx安装目录
-> cd /etc/nginx
# 启动nginx
-> nginx
# 测试nginx 在浏览器里面请求http://ip 就可以了看到nginx页面就算安装成功

配置证书

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
39
# 创建证书文件并且将证书复制近文件中
-> cd /etc/nginx
-> mkdir cert
-> mv domian.name.key /etc/nginx/cert/
-> mv domian.name.pem /etc/nginx/cert/
# 配置nginx.conf文件使证书生效,并将80端口转发到443端口上。
-> vim nginx.conf
#以下属性中以ssl开头的属性代表与证书配置有关,其他属性请根据自己的需要进行配置。
server {
listen 443; #配置HTTPS的默认访问端口号为443。此处如果未配置HTTPS的默认访问端口,可能会造成Nginx无法启动。Nginx 1.15.0以上版本请使用listen 443 ssl代替listen 443和ssl on。
server_name www.certificatestests.com; #将www.certificatestests.com修改为您证书绑定的域名,例如:www.example.com。如果您购买的是通配符域名证书,要修改为通配符域名,例如:*.aliyun.com。
root html;
index index.html index.htm;
ssl_certificate cert/domain name.pem; #将domain name.pem替换成您证书的文件名称。
ssl_certificate_key cert/domain name.key; #将domain name.key替换成您证书的密钥文件名称。
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #使用此加密套件。
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #使用该协议进行配置。
ssl_prefer_server_ciphers on;
location / {
root html; #站点目录。
index index.html index.htm;
}
}

server {
# listen 80 default_server;
# listen [::]:80 default_server;
# server_name _;
listen 80;
listen [::]:80;
server_name www.certificatestests.com;
return 301 https://$host$request_uri;
}
# 保存并推出、检查配置nginx配置文件是否合法、并重启nginx
-> nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
-> nginx -s reload

测试域名证书是否生效

1
2
https://domian
http://domian 检查是否安全链接即可。一般浏览器上面会出现一个安全的标志

参考文献

阿里云帮助文档-在Nginx上配置证书

CentOS 7 上安装最新版 Nginx

记录下使用docker-compose构建管理Redis、mysql。

准备工作

  1. 创建工作目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ mkdir -p docker docker/mysql docker/mysql/data docker/redis docker/redis/data
# 使用 tree docker 查看目录结构 yum -y install tree 安装
$ tree docker
docker
├── mysql
│   └── data
└── redis
└── data
$ touch docker/mysql/my.cnf docker/redis/redis.conf
$ tree docker
docker
├── mysql
│   ├── data
│   └── my.cnf
└── redis
├── data
└── redis.conf
  1. 准备my.conf 、redis.conf
    1. my.conf
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
[client]
port = 3306
socket = /var/lib/mysql/data/mysql.sock
[mysqld]
# 针对5.7版本执行group by字句出错问题解决
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
# 一般配置选项
basedir = /var/lib/mysql
datadir = /var/lib/mysql/data
port = 3306
socket = /var/lib/mysql/data/mysql.sock
lc-messages-dir = /usr/share/mysql # 务必配置此项,否则执行sql出错时,只能显示错误代码而不显示具体错误消息
character-set-server=utf8
back_log = 300
max_connections = 3000
max_connect_errors = 50
table_open_cache = 4096
max_allowed_packet = 32M
#binlog_cache_size = 4M
max_heap_table_size = 128M
read_rnd_buffer_size = 16M
sort_buffer_size = 16M
join_buffer_size = 16M
thread_cache_size = 16
query_cache_size = 64M
query_cache_limit = 4M
ft_min_word_len = 8
thread_stack = 512K
transaction_isolation = REPEATABLE-READ
tmp_table_size = 64M
#log-bin=mysql-bin
long_query_time = 6
server_id=1
innodb_buffer_pool_size = 256M
innodb_thread_concurrency = 16
innodb_log_buffer_size = 16M
  1. redis.conf 可以去这里下载 官网
1
2
3
4
5
6
7
8
wget https://raw.githubusercontent.com/redis/redis/5.0/redis.conf

# 修改内容
daemonize no ## 若使用开机启动,生成pid,该项必须设置为诶yes,否则redis将不能够正常执行开机启动(systemctl start redis,执行后一直卡着,直到超时)
#bind 127.0.0.1 ##注释掉,允许所有其他ip访问,真实使用最好坐下限制,只允许某些主机访问
protected-mode no ## 允许其他机器上的客户端连接当前redis,配置文件设置该项,则开机启动处就可以去掉--protected no
requirepass root ##设置密码
# daemonize yes,他的作用是开启守护进程模式,在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。但是在后面的命令中“-d”选项也是开启daemonize,这是docker自家的方式。

编写Docker-Compose文件

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
39
40
41
42
version : '3'
services:
mysql:
image: mysql/mysql-server:5.7.18
container_name: mysql5.7.18-dev
networks:
- net-shop-db
ports:
- "3306:3306"
volumes:
- /opt/docker/mysql/my.cnf:/etc/my.cnf # 映射数据库配置文件
- /opt/docker/mysql/data:/var/lib/mysql/data # 映射数据库保存目录到宿主机,防止数据丢失
- /opt/docker/mysql/log:/var/log/mysql # 日志文件
- /etc/localtime:/etc/localtime:ro # 设置容器时区与宿主机保持一致
restart: always # 设置容器自启模式
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--lower_case_table_names=1',
'--default-time-zone=+8:00']
environment:
- TZ=Asia/Shanghai # 设置容器时区与宿主机保持一致
- MYSQL_ROOT_PASSWORD=root # 设置root密码
redis:
image: redis:5.0.5
container_name: redis5.0.6-dev
networks:
- net-shop-db
ports:
- "6379:6379"
volumes:
- /etc/localtime:/etc/localtime:ro # 设置容器时区与宿主机保持一致
- /opt/docker/redis/data:/data
- /opt/docker/redis/redis.conf:/etc/redis.conf
command: redis-server /etc/redis.conf # 启动redis命令
environment:
- TZ=Asia/Shanghai # 设置容器时区与宿主机保持一致
restart: always # 设置容器自启模式

networks:
net-shop-db:
driver: bridge

基本用法

注意需要提前启动docker

  1. 创建并启动容器 docker-compose -f docker-compose.yml up -d
  2. 批量停止容器并清除容器 docker-compose -f docker-compose.yml down
  3. 重新构建容器 docker-compose -f docker-compose.yml up --build -d
  4. 启动失败,可以查看容器日志信息获取帮助 docker logs 容器名词或容器ID # 既docker-compose.yml文件参数container_name指定的值

备注

  1. docker-compose 命令不存在、未找到命令。直接从github上下载,国内下载巨慢。
1
2
3
4
5
6
7
8
# 注意查看docker 版本 和 docker-compose 的版本
cd /usr/local/bin/

curl -L https://github.com/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
#。直接用国内的镜像
curl -L https://get.daocloud.io/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

chmod +x /usr/local/bin/docker-compose

参考

docker-compose 命令不存在、未找到命令

使用 docker-compose 在 Docker 中启动有密码的 Redis 容器

Docker-Compose搭建mysql、redis、zookeeper、rabbitmq、consul、elasticsearch环境

Docker Compose多容器部署

停止、删除所有的docker容器和镜像

宿主机连接docker中的mysql

Docker Compose 安装 on centos7

Docker-Compose搭建mysql、redis、zookeeper、rabbitmq、consul、elasticsearch环境

docker-compose一键安装redis+mysql+rabbitmq

Centos7的yum源中默认是没有mysql,因为现在已经用mariaDB代替mysql了。

安装前检查

检查linux系统版本

1
2
[root@localhost backup]# cat /etc/system-release
CentOS Linux release 7.4.1708 (Core)

检查是否已经安装mysql

1
[root@localhost backup]# rpm -qa | grep mysql
  • 如果存在Mysql可以考虑卸载

    1
    rpm -e --nodeps mysql-connector-odbc-x.x.x-x.el7.x86_64

    将/var/lib/mysql文件夹下的所有文件都删除干净。

检查是否安装mariaDB

Read more »

记录下自己在阿里云部署docker记录。

准备工作

  1. 准备一台ESC服务器,并可以使用控制台登录。
  2. 检查ESC是否安装了docker,可以选择是否卸载重装。
    1. 检查docker是否安装,docker 显示相关信息则表示安装
    2. 卸载docker,若不重装直接忽略,yum list installed | grep docker查看安装内容
1
2
3
4
5
6
7
8
9
10
11
12
13
$ yum list installed | grep docker
containerd.io.x86_64 1.3.7-3.1.el7 @docker-ce-stable
docker-ce.x86_64 3:19.03.13-3.el7 @docker-ce-stable
docker-ce-cli.x86_64 1:19.03.13-3.el7 @docker-ce-stable
# 依次卸载
$ yum -y remove docker-ce.x86_64
$ yum -y remove docker-ce-cli.x86_64
$ yum -y remove containerd.io.x86_64
# 检查卸载结果
$ yum list installed | grep docker
# 无输出内容
$ docker
# -bash: docekr: 未找到命令 则表示卸载干净了
  1. 安装docker
    1. 更新yum yum update
    2. 获取官方源wget -P /etc/yum.repos.d/ https://download.docker.com/linux/centos/docker-ce.repo
    3. 查看可以安装的版本yum list docker-ce --showduplicates | sort -r
    4. 选择安装 yum install -y docker-ce-18.03.0.ce-1.el7.centos
    5. 安装docker yum install docker -y
      1. 检查docker -v输出版本信息

设置docker相关信息

  1. 设置docker开机启动项systemctl enable docker.service
  2. 关闭docker开机启动项 systemctl stop docker.service
  3. 检查docker当前运行情况 systemctl status docker
  4. 若未启动则启动docker systemctl start docker ,若启动了可以重启systemctl restart docker
  5. 检查docker运行情况service docker status

参考文档

阿里云CentOS 7上安装配置Docker

How To Install and Use Docker Compose on CentOS 7

最近开始用mac来开发。记录下环境安装过程方便以后快速恢复熟悉的开发环境。

这里提前修改一下控制台前缀

1
2
3
4
5
[dev mc]$ sudo vim /etc/bashrc
#修改以下内容
PS1='\h:\W \u\$ '
#修改成
PS1='[dev \u]$ '

好用的工具

  • pages 同事推荐的软件暂时用来写周报。
  • brew 这是一个神器,和linux 上的yum 类似的工具。
  • sublime text 我之前在windows上都是使用notepad++。上App Store上没找到,该用这个了。

pages

  • 直接在app store安装就好了

brew

  • 安装过程使用终端安装。
1
2
#  这里并没有换行,复制的时候请注意。
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  • 基本操作方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 更新
brew update
# 安装软件
brew install xxx(软件名)

#常见命令整理如下,以下命令都可带上 cask 参数:

brew search name:联网搜索软件是否存在 brew 中
brew install name:安装软件
brew upgrade name:更新软件
brew uninstall name:卸载软件
brew reinstall name:重新安装软件
brew info name:查看软件安装地址
brew cleanup:清理缓存等
brew doctor:查看建议,例如升级等
Read more »

最近在编写爬虫程序,这里简单的做下笔记。

阅读注意事项

  1. 需要一台服务器(阿里ESC)这种,分配的公网ip。
  2. 这里使用的yum安装,所以环境配置都创建好了,不需要在手动修改。

准备工作

安装Java、maven、git、selenium运行环境

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
# 使用的机器是centos7.4
# 系统环境一个一个来安装。
# 1.检查是否安装jdk
-> yum install java-1.8.0-openjdk-devel.x86_64
-> java -version
openjdk version "1.8.0_262"
OpenJDK Runtime Environment (build 1.8.0_262-b10)
OpenJDK 64-Bit Server VM (build 25.262-b10, mixed mode)
-> yum install maven
-> mvn -version
Java version: 1.8.0_262, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/jre
Default locale: zh_CN, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-862.el7.x86_64", arch: "amd64", family: "unix"
-> yum install git
git version 1.8.3.1
# 查看是否生成过证书
-> ls -al ~/.ssh
# 这里配置下ssh公钥方便git拉去代码
-> ssh-keygen -t rsa -C “you email@gamil.com”
# 查看证书
-> cat ~/.ssh/id_rsa.pub
# 开始配置 selenium 运行环境
-> wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
-> yum localinstall google-chrome-stable_current_x86_64.rpm
# 检查版本
-> google-chrome --version
Google Chrome 85.0.4183.83
# 安装 chromedriver
-> yum install chromedriver
-> chromedriver -version
ChromeDriver 84.0.4147.89
# 这里注意 chromedriver 可能与chrome的版本不一致,去官方网站下载后解压复制到/usr/bin中
# 下载地址 https://chromedriver.chromium.org/
  • 注意 google-chrome 和 chromedriver版本是需要对应的具体可以到官方查看。
  • 在centos上运行需要配置下关键参数
1
2
3
4
5
6
7
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--headless");
chromeOptions.addArguments("--disable-gpu"); // 不配置一定报错
chromeOptions.addArguments("--no-sandbox");
// 创建无界面浏览器对象
chromeOptions.setHeadless(true);
return new ChromeDriver(chromeOptions);
  • 启动代码的需要指定环境变量
1
2
System.setProperty("webdriver.chrome.chromedriver", "/usr/bin/chromedriver");
# 具体可以通过 whereis chromedriver查看。

参考文献

CentOS使用yum安装jdk

本章是整理知识内容,为强化知识长期更新。

Mysql基础架构图

mysql基础架构图

简单介绍上图相关模块功能

  • 连接器:管理连接,权限验证

  • 查询缓存:命中缓存则直接返回结果

  • 语法解析:词法分析,语法分析

  • 查询优化:执行计划生成,索引选择

  • 执行器:操作引擎,返回结果

  • 存储引擎:存储数据,提供读写结构。

连接器

我们需要连接到mysql服务端才能进行各种操作。

1
2
# 使用密码登录到时候我们会使用这种格式。
mysql -h$ip -P$port -u$user -p

执行上述命令会提示输入密码。输入完密码后就进入了mysql。

  • 如果用户名或者密码输入错误,会提示一个Access denied for user到错误,然后客户端程序结束执行。

  • 如果用户名和密码认证通过,连接器会去权限表中查询该用户拥有的所有权限。此后该连接里面的权限逻辑判断都会依赖认证成功时候读取到的权限。

因此当修改用户的权限之后,如果被修改用户的在登录状态是不会修改已经连接的权限。需要让被修改权限用户从新登录。

  • 实际上连接的方式有多种,上面通过密码的方式只是其中的一种方式。当客户端与服务端mysql进程进程建立连接,服务器的进程就会创建一个单独的线程来专门处理与这个客户端的交互,当该客户断开与服务端连接时,服务端并不会马上销毁对应的交互线程,而是缓存起来。当另一个新的客户端连接进来的时候,在把这个缓存的线程分配给新的客户端。这样就避免的频繁创建和修改线程,节省系统的开销。但是连接线程多了也会影响服务端,所以也有默认的参数限制客户端的连接数量。
1
2
# 这里是查询服务端存在的连接。
SHOW PROCESSLIST;

我使用的阿里云的数据库,这里展示下结果集。这里需要注意,登录的账号需要有PROCESS的权限否则只能看到自己的连接信息(线程),我这里采用的root所以看到全部到。

连接完成后如果没有执行后续操作则 Command会显示Sleep的状态,长时间Sleep会导致连接自动断开。默认的时间是8小时,也就是参数 wait_timeout。当客户端连接断开后,若客户端再次发出请求就会提示一个

Lost connection to MySQL server during query,此时就只能重新建立连接了。

半双工
  • MySQL客户端/服务端通信协议是“半双工”的:在任一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时发生。一旦一端开始发送消息,另一端要接收完整个消息才能响应它,所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。
  • 服务器响应给用户的数据通常会很多,由多个数据包组成。但是当服务器响应客户端请求时,客户端必须完整的接收整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。因而在实际开发中,尽量保持查询简单且只返回必需的数据,减小通信间数据包的大小和数量是一个非常好的习惯,这也是查询中尽量避免使用SELECT *以及加上LIMIT限制的原因之一。
长连接与短连接
  • 在数据库里面,长连接是指数据库建立连接,如果客户端有持续的请求则使用同一个连接,短连接是指每次执行几次短的查询就断开连接,下次查询再次创建连接。由于创建的过程复杂,一般来说尽量减少创建连接的动作,尽可能使用长连接。特别是在开发的时候出现大伙突然连接不上测试库,此时就检查下连接是不是太多了。因为连接多了会导致内存消耗特别快。

    • mysql的执行过程中临时使用的内存管理是在连接线程对象里面,服务端断开的时候才会释放,为了避免内存消耗过大,每次执行较大的操作之后,可以通过mysql_reset_connection来重新初始化连接资源。这个操作是不需要重新做权限,只是恢复到创建的初始状态。
  • MySQL mysql_reset_connection 官方文档

查询缓存

一个不被建议使用的功能,在新版的8.0中已经被删除了。总结就是弊大于利。

  • 建立完成后,假设执行一次SELECT语句,执行逻辑就会到第二部查询缓存。mysql拿到一个查询之后,会先到缓存中寻找释放有完全对应的,因为之前执行的查询语句和结果集会直接被缓存起来,以Keys-value的形式。keys是查询语句-value是查询结果集。如果能命中这个key则直接返回value。咋一看挺有用的,但实际上查询缓存的实效的太频繁。并且keys的命中条件太不聪明了,如果两次查询语句在任何的字符上存在不通(空格、大小写)都不会缓存命中。为什么会频繁失效,因为如果对查询语句中的表进行更新,就会导致缓存失效。比如INSERT、UPDATE 、DELETE 、TRUNCATE TABLE 、ALTER TABLE 等等就会导致缓存失效。

语法解析

如果没有命中查询缓存,就要开始执行sql语句了。mysql需要做什么,因此需要对sql进行解析。MySQL通过关键字将SQL语句进行解析,并生成一颗对应的解析树。这个过程解析器主要通过语法规则来验证和解析。比如SQL中是否使用了错误的关键字或者关键字的顺序是否正确等等。预处理则会根据MySQL规则进一步检查解析树是否合法。比如检查要查询的数据表和数据列是否存在等等。

  • 本质上发送过来的是一个文本信息,这里就需要对该文本信息进行编译,涉及到词法解析语法解析语义解析等阶段。需要注意的是,这里会对sql语句进行一些检查,比如设计的相关表、表字段。这里检查通过的才会进入查询优化模块。

查询优化

通过之前的语法解析,基本可以判定语法树是合法的,此时mysql就知道文本内容要做什么。

  • mysql会对查询语句进行优化,优化的结果就是生成一个执行计划,这个计划会表明使用了那些查询索引,表之间的连接是什么样子的。有时候出现多种执行方式,只是效率不通,优化器会决定使用哪一个执行方案,这个时候就涉及到EXPLAIN语句,该语句可以查看sql到执行计划,这将涉及到查询优化。
  • MySQL的查询优化器是一个非常复杂的部件,它使用了非常多的优化策略来生成一个最优的执行计划:
    • 重新定义表的关联顺序(多张表关联查询时,并不一定按照SQL中指定的顺序进行,但有一些技巧可以指定关联顺序)
    • 优化函数
    • 提前终止查询(比如:使用Limit时,查找到满足数量的结果集后会立即终止查询)
    • 优化排序(在老版本MySQL会使用两次传输排序,即先读取行指针和需要排序的字段在内存中对其排序,然后再根据排序结果去读取数据行,而新版本采用的是单次传输排序,也就是一次读取所有的数据行,然后根据给定的列排序。对于I/O密集型应用,效率会高很多。

执行器

进入到执行sql到环节。

  • 通过之前到查询优化,进入执行阶段,此时会先判断该连接用户是否有相关表的权限,如果没有会返回权限错误。如果有权限就会打开表,执行器会根据表定义的引擎去使用这个引擎提供的接口完成流程。

存储引擎

关于存储引擎,这里明确提醒。不是三种也不是四种。而是多种因为随时可能出现新的引擎。

  • 我们常用的就是InnoDBMyISAM,其它的不常见就不做过多介绍。mysql现在默认的引擎是InnoDB所以主要也是了解InnoDB
1
2
# 查看当前服务支持的存储引擎
SHOW ENGINES;

Read more »

本章是整理知识内容,为强化知识长期更新。

Redis 简介

Redis 是完全开源免费的,遵守 BSD 协议,是一个高性能的 key - value 数据库

  • Redis 与 其他 key - value 缓存产品有以下三个特点:
    • Redis 支持数据持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
    • Redis 不仅仅支持简单的 key - value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储
    • Redis 支持数据的备份,即 master - slave 模式的数据备份。
  • Redis是内存数据库,所有操作都在内存上完成,内存的访问速度本身就很快,读的速度是 110000 次 /s, 写的速度是 81000 次 /s。另一方面是因为它的数据结构。键值对是按一定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操作,所以高效的数据结构是 Redis 快速处理数据的基础。

Redis单线程

  • 我们通常说,Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。Redis 的单线程设计机制以及多路复用机制
  • Linux 中的 IO 多路复用机制
    • Linux 中的 IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。

Redis单线程处理IO请求性能瓶颈

  • 任意一个请求在server中一旦发生耗时,都会影响整个server的性能,也就是说后面的请求都要等前面这个耗时请求处理完成,自己才能被处理到。耗时的操作包括以下几种:
    • 操作bigkey:写入一个bigkey在分配内存时需要消耗更多的时间,同样,删除bigkey释放内存同样会产生耗时;
    • 使用复杂度过高的命令:例如SORT/SUNION/ZUNIONSTORE,或者O(N)命令,但是N很大,例如lrange key 0 -1一次查询全量数据;
    • 大量key集中过期:Redis的过期机制也是在主线程中执行的,大量key集中过期会导致处理一个请求时,耗时都在删除过期key,耗时变长;
    • 淘汰策略:淘汰策略也是在主线程执行的,当内存超过Redis内存上限后,每次写入都需要淘汰一些key,也会造成耗时变长;
    • AOF刷盘开启always机制:每次写入都需要把这个操作刷到磁盘,写磁盘的速度远比写内存慢,会拖慢Redis的性能;
    • 主从全量同步生成RDB:虽然采用fork子进程生成数据快照,但fork这一瞬间也是会阻塞整个线程的,实例越大,阻塞时间越久;
  • 并发量非常大时,单线程读写客户端IO数据存在性能瓶颈,虽然采用IO多路复用机制,但是读写客户端数据依旧是同步IO,只能单线程依次读取客户端的数据,无法利用到CPU多核。
  • Redis在4.0推出了lazy-free机制,把bigkey释放内存的耗时操作放在了异步线程中执行,降低对主线程的影响。
  • Redis在6.0推出了多线程,可以在高并发场景下利用CPU多核多线程读写客户端数据,进一步提升server性能,当然,只是针对客户端的读写是并行的,每个命令的真正操作依旧是单线程的。

Redis持久化机制

  • Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。
    • 实现:单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程内存释放。
  • RDB是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)
  • AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

AOF日志

  • 写后日志,“写后”的意思是 Redis 是先执行命令,把数据写入内存,然后才记录日志。
  • AOF 也有两个潜在的风险
    • 如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有丢失的风险。如果此时 Redis 是用作缓存,还可以从后端数据库重新读入数据进行恢复,但是,如果 Redis 是直接用作数据库的话,此时,因为命令没有记入日志,所以就无法用日志进行恢复了。
    • AOF 虽然避免了对当前命令的阻塞,但可能会给下一个操作带来阻塞风险。这是因为,AOF 日志也是在主线程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了。
  • AOF三种写回策略
    • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
      • 同步写回可以做到基本不丢数据,但是它在每一个写命令后都有一个慢速的落盘操作,不可避免地会影响主线程性能。
    • Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
      • 每秒写回采用一秒写回一次的频率,避免了“同步写回”的性能开销,虽然减少了对系统性能的影响,但是如果发生宕机,上一秒内未落盘的命令操作仍然会丢失。所以,这只能算是,在避免影响主线程性能和避免数据丢失两者间取了个折中。
    • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
      • 操作系统控制的写回在写完缓冲区后,就可以继续执行后续的命令,但是落盘的时机已经不在 Redis 手中了,只要 AOF 记录没有写回磁盘,一旦宕机对应的数据就丢失了。

RDB快照

  • Redis 的数据都在内存中,为了提供所有数据的可靠性保证,它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中。全量数据越多,RDB 文件就越大,往磁盘上写数据的时间开销就越大。
  • 对于 Redis 而言,它的单线程模型就决定了,我们要尽量避免所有会阻塞主线程的操作,所以,针对任何操作,我们都会提一个灵魂之问:“它会阻塞主线程吗?”RDB 文件的生成是否会阻塞主线程,这就关系到是否会降低 Redis 的性能。
  • Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave。
    • save:在主线程中执行,会导致阻塞;
    • bgsave:创建一个子进程,专门用于写入 RDB 文件,避免了主线程的阻塞,这也是 Redis RDB 文件生成的默认配置。
Read more »

本章属于持续学习、长期更修。

数据库的三范式是什么

  • 第一范式:列不可再分

  • 第二范式:行可以唯一区分,主键约束

  • 第三范式:表的非主属性不能依赖与其他表的非主属性 外键约束

  • 且三大范式是一级一级依赖的,第二范式建立在第一范式上,第三范式建立第一第二范式上。

  • 第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。

  • 第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。

  • 第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如 果存在”A → B → C”的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非关键字段 x → 非关键字段y

说下mysql有几种数据库引擎

不清楚,因为每个版本发布都会新增一些。但是我最熟悉是的innodb。

说下mysql Master slave 的原理

  • :binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;

  • :io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进 自己的relay log中;

  • :sql执行线程——执行relay log中的语句;

MySQL中myisam与innodb的区别

  • InnoDB支持事物,而MyISAM不支持事物
  • InnoDB支持行级锁,而MyISAM支持表级锁
  • InnoDB支持MVCC, 而MyISAM不支持
  • InnoDB支持外键,而MyISAM不支持
  • InnoDB不支持全文索引,而MyISAM支持。

innodb引擎的4大特性

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

什么是事务?

  • 事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。

覆盖索引和回表

  • 覆盖索引指的是在一次查询中,如果一个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,而不再需要回表查询。而要确定一个查询是否是覆盖索引,我们只需要explain sql语句看Extra的结果是否是“Using index”即可。

MySQL 事务四大特性

一说到 MySQL 事务,你肯定能想起来四大特性:原子性一致性隔离性持久性。什么是 MySQL 中的事务?

事务是一组操作,组成这组操作的各个单元,要不全都成功要不全都失败,这个特性就是事务。

在 MySQL 中,事务是在引擎层实现的,只有使用 innodb 引擎的数据库或表才支持事务。

  • 原子性(Atomicity): 原子性指的就是 MySQL 中的包含事务的操作要么全部成功、要么全部失败回滚,因此事务的操作如果成功就必须要全部应用到数据库,如果操作失败则不能对数据库有任何影响。
  • 一致性(Consistency):一致性指的是一个事务在执行前后其状态一致。比如 A 和 B 加起来的钱一共是 1000 元,那么不管 A 和 B 之间如何转账,转多少次,事务结束后两个用户的钱加起来还得是 1000,这就是事务的一致性。
  • 持久性(Durability): 持久性指的是一旦事务提交,那么发生的改变就是永久性的,即使数据库遇到特殊情况比如故障的时候也不会产生干扰。
  • 隔离性(Isolation):当多个事务同时进行时,就有可能出现脏读(dirty read)不可重复读(non-repeatable read)幻读(phantom read) 的情况,为了解决这些并发问题,提出了隔离性的概念。
    • 重点说下隔离性它们分别是读未提交(read uncommitted)读已提交(read committed)可重复读(repetable read)串行化(serializable)。下面分别来解释一下。
    • 读未提交:读未提交指的是一个事务在提交之前,它所做的修改就能够被其他事务所看到。
    • 读已提交:读已提交指的是一个事务在提交之后,它所做的变更才能够让其他事务看到。
    • 可重复读:可重复读指的是一个事务在执行的过程中,看到的数据是和启动时看到的数据是一致的。未提交的变更对其他事务不可见。
    • 串行化:顾名思义是对于同一行记录,会加写锁会加读锁。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

SQL语句的执行顺序

我们在编写一个查询语句的时候

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)

首先先来解释一下什么是笛卡尔积

现在我们有两个集合 A = {0,1} , B = {2,3,4}

那么,集合 A * B 得到的结果就是

A * B = {(0,2)、(1,2)、(0,3)、(1,3)、(0,4)、(1,4)};

B * A = {(2,0)、{2,1}、{3,0}、{3,1}、{4,0}、(4,1)};

上面 A * B 和 B * A 的结果就可以称为两个集合相乘的 笛卡尔积

我们可以得出结论,A 集合和 B 集合相乘,包含了集合 A 中的元素和集合 B 中元素之和,也就是 A 元素的个数 * B 元素的个数

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

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

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

  • WHERE 和 ON 的区别

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

    • 对主表的过滤应该使用 WHERE;
    • 对于关联表,先条件查询后连接则用 ON,先连接后条件查询则用 WHERE;
  1. GROUP BY
  • 根据 group by 字句中的列,会对 VT4 中的记录进行分组操作,产生虚拟机表 VT5。果应用了group by,那么后面的所有步骤都只能得到的 VT5 的列或者是聚合函数(count、sum、avg等)。
  1. HAVING
  • 紧跟着 GROUP BY 字句后面的是 HAVING,使用 HAVING 过滤,会把符合条件的放在 VT6
  1. SELECT
  • 第七步才会执行 SELECT 语句,将 VT6 中的结果按照 SELECT 进行刷选,生成 VT7
  1. DISTINCT
  • 在第八步中,会对 TV7 生成的记录进行去重操作,生成 VT8。事实上如果应用了 group by 子句那么 distinct 是多余的,原因同样在于,分组的时候是将列中唯一的值分成一组,同时只为每一组返回一行记录,那么所以的记录都将是不相同的。
  1. ORDER BY
  • 应用 order by 子句。按照 order_by_condition 排序 VT8,此时返回的一个游标,而不是虚拟表。sql 是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。

什么是临时表,何时删除临时表

1
2
3
4
在 MySQL 中,有三种类型的表
一种是`永久表`,永久表就是创建以后用来长期保存数据的表
一种是`临时表`,临时表也有两类,一种是和永久表一样,只保存临时数据,但是能够长久存在的;还有一种是临时创建的,SQL 语句执行完成就会删除。
一种是`虚表`,虚表其实就是`视图`,数据可能会来自多张表的执行结果。
  • MySQL 会在下面这几种情况产生临时表
    • 使用 UNION 查询:UNION 有两种,一种是UNION ,一种是 UNION ALL ,它们都用于联合查询;区别是 使用 UNION 会去掉两个表中的重复数据,相当于对结果集做了一下去重(distinct)。使用 UNION ALL,则不会排重,返回所有的行。使用 UNION 查询会产生临时表。
    • 使用 TEMPTABLE 算法或者是 UNION 查询中的视图。TEMPTABLE 算法是一种创建临时表的算法,它是将结果放置到临时表中,意味这要 MySQL 要先创建好一个临时表,然后将结果放到临时表中去,然后再使用这个临时表进行相应的查询。
    • ORDER BY 和 GROUP BY 的子句不一样时也会产生临时表。
    • DISTINCT 查询并且加上 ORDER BY 时;
    • SQL 用到 SQL_SMALL_RESULT 选项时;如果查询结果比较小的时候,可以加上 SQL_SMALL_RESULT 来优化,产生临时表
    • FROM 中的子查询;
    • EXPLAIN 查看执行计划结果的 Extra 列中,如果使用 Using Temporary 就表示会用到临时表。

介绍下InnoDB存储引擎的B+树索引

  • 每个索引都对应一棵B+树,B+树分为好多层,最下边一层是叶子节点,其余的是内节点。所有用户记录都存储在B+树的叶子节点,所有目录项记录都存储在内节点。
  • InnoDB存储引擎会自动为主键(如果没有它会自动帮我们添加)建立聚簇索引,聚簇索引的叶子节点包含完整的用户记录。
  • 我们可以为自己感兴趣的列建立二级索引,二级索引的叶子节点包含的用户记录由索引列 + 主键组成,所以如果想通过二级索引来查找完整的用户记录的话,需要通过回表操作,也就是在通过二级索引找到主键值之后再到聚簇索引中查找完整的用户记录。
  • B+树中每层节点都是按照索引列值从小到大的顺序排序而组成了双向链表,而且每个页内的记录(不论是用户记录还是目录项记录)都是按照索引列的值从小到大的顺序而形成了一个单链表。如果是联合索引的话,则页面和记录先按照联合索引前边的列排序,如果该列值相同,再按照联合索引后边的列排序。
  • 通过索引查找记录是从B+树的根节点开始,一层一层向下搜索。由于每个页面都按照索引列的值建立了Page Directory(页目录),所以在这些页面中的查找非常快。
  • 二者SELECT COUNT(*)哪个更快,为什么
    • myisam更快,因为myisam内部维护了一个计数器,可以直接调取。
Read more »