【Docker】镜像与docker数据卷

news2024/12/24 2:19:43

文章目录

  • 一、镜像
    • 1、镜像
    • 2、镜像原理之联合文件系统
    • 3、镜像原理之分层
    • 4、commit镜像
  • 二、数据卷
    • 1、数据卷
    • 2、-v使用数据卷
    • 3、实战:MySQL 同步数据
    • 4、docker volume相关指令
    • 5、匿名和具名挂载
    • 6、数据卷之Dockerfile
    • 7、数据卷容器

一、镜像

1、镜像

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件。

所有应用,直接打包docker镜像,就可以直接跑起来!而镜像的获取,可以:

  • 从远程仓库下载
  • 拷贝或docker save -o 导出
  • 通过DockerFile自己制作

2、镜像原理之联合文件系统

UnionFs (联合文件系统):

Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下( unite several directories into a single virtual filesystem)。Union文件系统是 Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(基础镜像没有父镜像),可以制作各种具体的应用镜像。

Docker镜像加载原理:

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。

  • boots(boot file system):主要包含 bootloader和 Kernel, bootloader主要是引导加 kernel,Linux刚启动时会加bootfs文件系统,在 Docker镜像的最底层是 boots。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs转交给内核,此时系统也会卸载bootfs
  • rootfs(root file system):在 bootfs之上。包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等

在这里插入图片描述

平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?

在这里插入图片描述

对于个精简的OS,rootfs可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的Linux发行版, boots基本是一致的, rootfs会有差別,因此不同的发行版可以公用bootfs

3、镜像原理之分层

随便下载一个镜像,可以看到是在一层一层的下载:

在这里插入图片描述

Docker镜像采用这种分层结构,好处是可以资源共享。比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。

docker inspect redis
# 在镜像元数据信息中看到分层的相关数据
[
....
},
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:c2adabaecedbda0af72b153c6499a0555f3a769d52370469d8f6bd6328af9b13",
                "sha256:744315296a49be711c312dfa1b3a80516116f78c437367ff0bc678da1123e990",
                "sha256:379ef5d5cb402a5538413d7285b21aa58a560882d15f1f553f7868dc4b66afa8",
                "sha256:d00fd460effb7b066760f97447c071492d471c5176d05b8af1751806a1f905f8",
                "sha256:4d0c196331523cfed7bf5bafd616ecb3855256838d850b6f3d5fba911f6c4123",
                "sha256:98b4a6242af2536383425ba2d6de033a510e049d9ca07ff501b95052da76e894"
            ]
        },
        "Metadata": {
....
]

所有的 Docker镜像都起始于一个基础镜像层,当进行修改或加新的内容时,就会在当前镜像层之上,创建新的镜像层。

在这里插入图片描述

  • 基于 Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层
  • 如果在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层
  • 如果继续添加一个安全补丁,就会创健第三个镜像层该像当前已经包含3个镜像层

在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合。下图中,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。
在这里插入图片描述
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版。

在这里插入图片描述
这种更新情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。

下图展示了与系统显示相同的三层镜像。所有镜像层堆并合井,对外提供统一的视图。

在这里插入图片描述

Docker 镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部!

这一层就是我们通常说的容器层,容器之下的都叫镜像层!

在这里插入图片描述

4、commit镜像

指令:

docker commit 提交容器成为一个新的副本

# 命令和git原理类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[版本TAG]

示例:修改tomcat容器,并提交成一个镜像

# 1、启动一个默认的tomcat
[root@9527 ~] docker run -d -p 8080:8080 tomcat
de57d0ace5716d27d0e3a7341503d07ed4695ffc266aef78e0a855b270c4064e

# 2、发现这个默认的tomcat 是没有webapps应用,官方的镜像默认webapps下面是没有文件的!
[root@9527 ~] docker exec -it de57d0ace571 /bin/bash
root@de57d0ace571:/usr/local/tomcat# 

# 3、从webapps.dist拷贝文件进去webapp
root@de57d0ace571:/usr/local/tomcat: cp -r webapps.dist/* webapps
root@de57d0ace571:/usr/local/tomcat: cd webapps
root@de57d0ace571:/usr/local/tomcat/webapps: ls
ROOT  docs  examples  host-manager  manager

# 4、将操作过的容器通过commit调教为一个镜像!我们以后就使用我们修改过的镜像即可,而不需要每次都重新拷贝webapps.dist下的文件到webapps了,这就是我们自己的一个修改的镜像
[root@9527 ~] docker commit -a="9527code" -m="add webapps app" de57d0ace571 tomcat02:1.0
sha256:d5f28a0bb0d0b6522fdcb56f100d11298377b2b7c51b9a9e621379b01cf1487e

[root@9527 ~] docker images|grep -i tomcat
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
tomcat02              1.0               d5f28a0bb0d0        14 seconds ago      652MB
tomcat                latest              1b6b1fe7261e        5 days ago          647MB
# 看到镜像大小比官方的大一点,因为我加了几个文件

如果你想要保存当前容器的状态,就可以通过commit来提交,获得一个镜像,就好比我们我们使用虚拟机的快照。

二、数据卷

1、数据卷

如果数据存储在容器中,那删除容器,数据就会丢失,因此需要对数据进行持久化。基于此,Docker有数据卷技术,实现了:

  • Docker容器中产生的数据同步到本地
  • 容器之间可以数据共享

如下图中,将容器中的/usr/mysql目录映射到宿主机的/home/mysql目录:(其实就是挂载,把容器内的目录,挂载到宿主机上面)
在这里插入图片描述

Volume是外部默认的联合文件系统或者是存在于宿主文件系统中正常的文件或文件夹。

总结一句话:容器的持久化和同步操作!容器间也可以共享数据!

2、-v使用数据卷

docker run -it -v 主机目录:容器目录 ...
 
[root@9527 home] docker run -it -v /home/ceshi:/home  centos /bin/bash

使用docker inspect 查看容器元数据信息中的挂载信息:

在这里插入图片描述

此时,在容器内创建文件,宿主机目录中同步数据成功。在宿主机创建文件,容器中对应目录也同步。

在这里插入图片描述

停止容器 ⇒ 主机上修改文件 ⇒ 启动容器 ⇒ 容器内的数据依旧是同步的:

在这里插入图片描述

从此,对于上篇中的nginx、tomcat…,只需要修改对应的本地目录,容器内的数据也会自动同步更新。

3、实战:MySQL 同步数据

# 获取镜像
[root@9527 home] docker pull mysql:5.7
 
# 运行容器, 需要做数据挂载! 
# 安装启动mysql,需要配置密码(注意)
# 官方测试指令 docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
 
# 启动我们的
-d      # 后台运行
-p      # 端口映射
-v      # 卷挂载。可指定多个
-e      # 环境配置
--name  # 容器的名字
[root@9527 home] docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
9552bf4eb2b69a2ccd344b5ba5965da4d97b19f2e1a78626ac1f2f8d276fc2ba
 
# 启动成功之后,我们在本地使用navicat链接测试一下
# navicat链接到服务器的3310 ---> 3310和容器的3306映射,这个时候我们就可以连接上mysql了
 
# 在本地测试创建一个数据库,查看本地目录中,数据持久化同步成功。

此时彻底删除容器:

docker rm -f mysql01

本地目录中的数据不受影响,这就实现了容器数据持久化的功能!

4、docker volume相关指令

[root@9527 ~] docker volume help

Usage:	docker volume COMMAND

Manage volumes

Commands:
  create      Create a volume
  inspect     Display detailed information on one or more volumes
  ls          List volumes
  prune       Remove all unused local volumes
  rm          Remove one or more volumes

Run 'docker volume COMMAND --help' for more information on a command.
  • docker volume ls 列出所有的数据卷
# 只需要展示数据卷的名称,使用-q
[root@9527 ~] docker volume ls
DRIVER              VOLUME NAME
local               561b81a03506f31d45ada3f9fb7bd8d7c9b5e0f826c877221a17e45d4c80e096
  • docker volume create 创建一个数据卷
[root@9527 ~] docker volume create centos-volume
  • docker volume inspect 查看卷的信息
[root@9527 ~] docker volume inspect centos-volume
  • docker volume prune 移除未使用的数据卷
[root@9527 ~] docker volume prune
  • docker volume rm 移除一个或多个数据卷,不能移除被容器使用的数据卷
# -f, --force   Force the removal of one or more volumes
[root@9527 ~]# docker volume rm 8aee2f0aa880dc1892a01c211101f6360158ae64867b90e5ad3685d7a4ca9496
Error response from daemon: remove 8aee2f0aa880dc1892a01c211101f6360158ae64867b90e5ad3685d7a4ca9496: 
volume is in use - [857601f85a5adf1e1305f2f6a7c317431af6841d750b7dd947a03b0725e0ae49]

# 需要先移除该容器(只停止是不行的)

5、匿名和具名挂载

匿名挂载:-v 后面只写了容器内的路径

# 匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx     # -P 随机指定端口
 
# 查看所有volume的情况
[root@9527 ~] docker volume ls
DRIVER              VOLUME NAME
local               561b81a03506f31d45ada3f9fb7bd8d7c9b5e0f826c877221a17e45d4c80e096
local               36083fb6ca083005094cbd49572a0bffeec6daadfbc5ce772909bb00be760882
 
# 这里发现,这种情况就是匿名挂载,我们在-v 后面只写了容器内的路径,没有写容器外的路径!
 

具名挂载:-v后面给卷起个名字,注意没有/,更不是宿主机的本地目录路径。即-v 卷名:容器内的路径

# 具名挂载
[root@9527 ~] docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
26da1ec7d4994c76e80134d24d82403a254a4e1d84ec65d5f286000105c3da17
[root@9527 ~] docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
26da1ec7d499        nginx               "/docker-entrypoint.…"   3 seconds ago       Up 2 seconds        0.0.0.0:32769->80/tcp   nginx02
486de1da03cb        nginx               "/docker-entrypoint.…"   3 minutes ago       Up 3 minutes        0.0.0.0:32768->80/tcp   nginx01
[root@9527 ~] docker volume ls
DRIVER              VOLUME NAME
local               561b81a03506f31d45ada3f9fb7bd8d7c9b5e0f826c877221a17e45d4c80e096
local               juming-nginx
 
# 通过-v 卷名:容器内的路径
# 查看一下这个卷 
[root@9527 ~] docker volume inspect juming-nginx
[
  {
      "CreatedAt": "2020-08-12T18:15:21+08:00",
      "Driver": "local",
      "Labels": null,
      "Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
      "Name": "juming-nginx",
      "Options": null,
      "Scope": "local"
  }
]

所有docker容器内的卷,没有指定目录的情况下都是在 /var/lib/docker/volumes/xxxxx/_data

具名挂载可以方便的找到我们的一个卷,大多数情况下使用的是具名挂载。区别:

# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载!
-----------------
-v  容器内路径                   // 匿名挂载
-v  卷名:容器内路径               //具名挂载
-v /主机路径:容器内路径           // 指定路径挂载

关于权限ro、rw:

# 通过 -v 容器内容路径 ro rw 改变读写权限
ro  readonly    # 只读
rw  readwrite   # 可读可写
 
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
 
# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内容无法操作

6、数据卷之Dockerfile

Dockerfile就是用来构建docker镜像的构建文件(命令脚本),通过这个脚本可以生成镜像,镜像是一层一层的,脚本一个个的命令,每个命令都是一层。

# 创建一个dockerfile文件, 名字可以随机,但建议Dockerfile
# 文件的内容 指定(大写) 参数
 
FROM centos
 
VOLUME ["volume01", "volume02"]  # 代表容器内的两个目录,即匿名挂载
 
CMD echo "----end----"
CMD /bin/bash
 
# 这里的每一个命令都是镜像的一层!

开始构建:

在这里插入图片描述
拿构建的镜像启动容器:

在这里插入图片描述
这个卷和外部宿主机有一个同步的目录:

在这里插入图片描述
inspect查看容器元信息中数据卷的映射关系:

在这里插入图片描述

这种方式我们未来使用的十分多,假设构建镜像时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径!

7、数据卷容器

在这里插入图片描述

docker02挂载了docker01,docker01就叫数据卷容器。

# 使用上面自己构建的镜像启动两个容器docker01、docker02
docker run -it --name docker01 code-9527/centos:1.0
# docker02挂载docker01
docker run -it --name docker02 --volumes-from docker01 code-9527/centos:1.0

在这里插入图片描述
同样的,在docker02中创建文件,docker01中也能同步成功。即--volumes-from实现了容器间数据的共享。

# 也可以再挂一个docker03
docker run -it --name docker03 --volumes-from docker01 code-9527/centos:1.0

此时,docker02和docker03都挂载于docker01,删除docker01,02和03的数据还是能正常访问,这里是拷贝的关系!

在这里插入图片描述
基于以上的测试,就可以实现多个mysql容器数据的共享:

[root@9527 home] docker run -d -p 3344:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
 
[root@9527 home] docker run -d -p 3345:3306 
							-v /etc/mysql/conf.d -v /var/lib/mysql 
							-e MYSQL_ROOT_PASSWORD=123456 --name mysql02 
							--volumes-from mysql01 mysql:5.7

总结:

容器之间配置信息的传递, 数据卷容器的生命周期一直持续到没有容器使用为止(复制拷贝的关系)

且一旦你持久化到了本地,这个时候,本地的数据是不会删除的!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/490987.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s笔记25--k8s 跨主机网络flannel

k8s笔记25-- k8s 跨主机网络flannel 简介不同机器上网络设备区别flannel 网络常见三大后端模式UDPVXLANhost-gw 如何查看集群用哪种网络模式阿里云flannel容器网络 alloc参考文档 简介 在单机环境下,容器间可以通过 docker0 网桥来通信,但其无法实现不同…

QT QGridLayout网格布局控件

本文详细的介绍了QHBoxLayout控件的各种操作,例如:新建界面、控件布局、获取行、获取列、某行伸缩系数、某列伸缩系数、某列最小宽度、某行最小宽度、总单元格数、移除布局条目、移动布局条目、其它文章等操作。 实际开发中,一个界面上可能包…

【小程序】 键盘和表情同时存在时候,输入框上移问题

键盘和表情 效果图实现方法引入的js文件&#xff0c;文件名emoji.js&#xff0c;存放在untils路径下 效果图 实现过程&#xff0c;监听键盘高度的同时&#xff0c;判断是否获取到焦点样式上&#xff0c;swiper实现左右按页滑动效果 实现方法 <template><view class&…

IDEA常用配置及使用技巧

文章目录 下载插件JRebel and XRebelChinese Language PackMybatis XEasyCodeTranslationAuto filling Java call argumentsCodota AI Autocomplete for Java and JavaScriptAlibaba Java Coding GuidelinesEasyYapiGenerateAllSetterGit Commit TemplateGitToolBoxSQL Params …

基于YOLOv5的儿童睡眠检测

注意&#xff1a; 由于SOPHGO SE5微服务器的CPU是基于ARM架构&#xff0c;部分步骤将在 基于x86架构CPU的开发环境中完成 一、初始化开发环境(基于x86架构CPU的开发环境中完成) 二、模型转换 (基于x86架构CPU的开发环境中完成) 三、YOLOv5模型部署测试&#xff08;在SOPHGO SE5…

FT2000+ qemu kvm 64C64G 通过频繁设置CPU online 状态导致虚拟机openEuler 操作系统假死测试用例2

前文&#xff1a; https://hknaruto.blog.csdn.net/article/details/130408240 测试程序 /** tcti.cpp参考&#xff1a; https://www.cnblogs.com/organic/p/17321523.htmlg -stdc11 -lpthread trigger_cgroup_timer_inactive.cpp -o inactive_timer ./inactive_timer 100000…

linux内核网络子系统初探---概述

linux内核网络子系统初探—概述 一、网络模型 简单介绍 学习网络时&#xff0c;必定能在各种教材资料里见到以下三种网络模型&#xff1a; 三种模型间的差异&#xff1a; OSI七层模型是理论上的网络模型&#xff0c;从功能方面分成了相对独立的7个层次&#xff0c;由于太复…

如何通过 Rancher 轻松实现多云部署

“多云”通过不同的云厂商分发应用程序提高了弹性&#xff0c;能够帮助企业强化自身的竞争力。此外&#xff0c;多云还降低了被云厂商锁定的可能性&#xff0c;让企业避免过于依赖某个云厂商。 虽然多云的优势很多&#xff0c;但是管理多云 Kubernetes 的困难还是让人望而却步…

MCU自动化测量单元—峟思工程仪器仪表的智能助力

随着科技的不断发展&#xff0c;峟思工程仪器仪表在工程测量领域不断创新&#xff0c;其中MCU自动化测量单元作为一项颇具潜力的技术&#xff0c;正逐渐引起行业的广泛关注。MCU自动化测量单元作为峟思工程仪器仪表的重要组成部分&#xff0c;其应用带来了许多正面影响&#xf…

用右雅克比对旋转矩阵进行求导

考虑一个向量 a \bold{a} a对其进行旋转, 旋转用旋转矩阵 R \bold{R} R表示, 用朴素的倒数定义进行求导而不是用扰动模型, 我得到了这个过程与结果 和高博的新书结果 − R J r a ∧ -\bold{R}\bold{J}_{r}\bold{a}^{\wedge} −RJr​a∧结果不一样, 雅克比矩阵位置不同, 是不是…

神经网络结构搜索NAS

推荐课程&#xff1a;神经网络结构搜索 感谢博主ShusenWang提供的课程讲解&#xff01; 目录 1. 为什么要学习神经网络结构搜索NAS&#xff1f; 2. 什么是神经网络结构搜索NAS&#xff1f; &#xff08;1&#xff09;随机搜素Random Search 1. 为什么要学习神经网络结构搜…

位图的简单实现和使用

文章目录 1. 什么是位图2. 位图的简单实现3. 测试位图代码 1. 什么是位图 位图, 是一种非常常见的结构, 它使用每个二进制位来存放一个值的状态, 就类似于 Java 当中 HashSet 存储元素的功能. 在 Java 当中, 可以使用HashSet完成如下操作: add(T v): 添加一个元素到 HashSet…

算法:递归启蒙-汉诺塔

基本所有的讲递归的书和视频都会以汉诺塔作为开始&#xff0c;因为它足够经典 汉诺塔问题要求整个挪动的过程中都符合小压大的原则&#xff0c;就是如果同一个柱子上有超过1个的话&#xff0c;那必须下面是最大的&#xff0c;上面依次变小&#xff0c;不能出现大盘压小盘的情况…

Element Plus的Pagination 组件用法

5.2 Pagination 组件 分页组件通常与表格组件一同使用&#xff0c;在数据量很大的时候&#xff0c;通常不会在表格中一次性显示所有的数据&#xff0c;因为如果所有数据都展示在一个页面&#xff0c;数据量庞大&#xff0c;容易造成浏览器崩溃&#xff0c;就算数据可以完全展…

【CV2NLP】Chinese-Vicuna 中文小羊驼

学习一个短语&#xff01; gain proficiency in 熟练掌握 &#xff08;用我最爱的文心一言造个句子&#xff09; 最近羊驼家族百花齐放&#xff0c;赶紧学习一下 ChatBot 的背后细节。Chinese-Vicuna 中文小羊驼是基于 Vicuna 模型使用中文数据 LORA 方案来微调的一种中文对…

数值分析-牛顿-柯特斯公式的概念、推导与应用

目录 一、引言 二、牛顿-柯特斯公式的基本概念 三、牛顿-柯特斯公式的推导 四、牛顿-柯特斯公式的应用 五、牛顿-柯特斯公式的优缺点 六、总结 一、引言 数值分析是数学中的一个重要分支&#xff0c;它研究如何利用数值方法来解决实际问题。在数值分析中&#xff0c;牛顿…

Redux 学习系列(一) —— 基础概念入门篇

简介 Redux 是一个可预测的 JavaScript 应用状态管理容器&#xff0c;也可以说是一个应用数据流框架。 作用 Redux 主要是用作应用状态的管理。它抽离所有组件的状态&#xff0c;构造一个中心化的单独常量状态树&#xff08;对象&#xff09;来保存这一整个应用的状态。这棵…

Java经典笔试题—day02

Java经典笔试题—day02 &#x1f50e;选择题&#x1f50e;编程题&#x1f95d;排序子序列&#x1f95d;倒置字符串 &#x1f50e;结尾 &#x1f50e;选择题 (1)A 派生出子类 B &#xff0c; B 派生出子类 C &#xff0c;并且在 java 源代码有如下声明&#xff1a; A a0new A(…

HTTPS协议介绍

文章目录 一、HTTPS协议的认识二、常见的加密方式1.对称加密2.非对称加密 三、数据摘要四、HTTPS的工作过程探究1.只使用对称加密2.只使用非对称加密3.双方都使用非对称加密4.非对称加密对称加密5.中间人攻击6.引入证书7.非对称加密对称加密证书认证 一、HTTPS协议的认识 HTTP…

【数据库】面试高频问题汇总及详细解答

【C语言部分】面试高频问题汇总及详细解答 【操作系统(Linux)】面试高频问题汇总及详细解答 【计算机网络】面试高频问题汇总及详细解答 本文目录 1. SQL1.1 介绍一下数据库分页1.2 介绍一下SQL中的聚合函数1.3 表跟表是怎么关联的1.4 说一说你对外连接的了解1.5 说说SQL中怎么…