当前位置: 首页 > news >正文

docker的前世今生

docker来自哪里?

从我们运维部署的历史来看,宿主机从最初的物理机到虚拟机,再到docker,一步步演进到现在。技术演进其实是为了解决当前技术的痛点,那我们来看看有哪些痛点以及如何克服痛点的。

物理机

一般来说,一台物理服务器的配置都很高,都会部署较多的服务以充分利用物理机的资源,这就会带来许多的问题

  1. 服务之间互相影响,如A服务占满了CPU或内存,就会把机器上所有服务都卡死
  2. 资源浪费,若服务对基础环境的要求不一致,如linux或java版本等,那么服务就得放在别的机器上
  3. 故障恢复困难,一旦系统出现故障或卡死,需要重启机器上的所有服务,经历过的都知道,时间漫长且困难

虚拟机

既然物理机有以上问题,那么怎么解决呢?从解决问题的思路上来说,就是隔离,把基础环境隔离,服务隔离,这就引入了虚拟机,通过虚拟化技术,将一台物理机隔离成多个独立的虚拟机,互不受影响,这样就能部分解决物理机的问题,所有的影响都限制到了单台虚拟机,而不是整台物理机。当然,引入虚拟机也有对应的问题,以及提出了更高的要求

  1. 虚拟机的管理复杂,存在管理虚机的消耗
  2. 虚机的依赖管理复杂,常常遇到测试环境没问题,而生产环境报错的情况

docker

那么,虚拟机上面的问题,该如何解决呢?再次隔离,把每个服务都进行隔离,互不影响,这样就将影响降低到最小。那么docker就应运而生,每个服务就是一个容器,互相隔离,环境版本都可以根据自己的需求进行配置,服务出现故障不会影响其他服务,也不会因为资源占用问题,夯死整台机器。此外容器的故障恢复速度非常快,基本是秒级。

关于解决虚拟机的问题,docker解决了虚机的依赖管理复杂的问题,这是毋庸置疑的。此外,docker是不需要虚拟机的,若直接部署在物理机上,那么就解决虚拟机管理复杂的问题(因为实际原因,docker可能是部署在虚机上的)。当然任何的新技术都会引入新的问题,关于docker的问题,后面介绍k8s的时候再介绍。

docker现在何处

docker的基本使用

docker可以简单分为两块,一个是镜像,一个是容器。镜像是静态的,容器是运行时。那么我们先看看镜像是如何创建的。

编写dockerfile
# 基础镜像
FROM dockerhub.kubekey.local/public/openjdk:8
# 对外暴露的端口
EXPOSE 8082
EXPOSE 9998
#启动时接受的参数
ARG profile
ENV profile ${profile}
RUN echo "profile: ${profile}"
# 将原路径复制到镜像内的目录下
COPY ./OaCenter-1.0-SNAPSHOT.jar /jar/OaCenter-1.0-SNAPSHOT.jar##统一内部时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone# 启动时执行命令
ENTRYPOINT java -server -Xms4g -Xmx4g -Xss512M -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/outOfMemory.hprof -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20m -Xloggc:/logs/gc.log -jar /jar/OaCenter-1.0-SNAPSHOT.jar --spring.profiles.active=${profile}
构建镜像
docker build --build-arg profile=uat -f Dockerfile -t $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BUILD_NUMBER .
运行镜像
docker run -d $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BUILD_NUMBER --name xxx

至此,容器就运行起来,对外提供服务了。

docker运维的常用命令

# 停止容器
docker stop xxx
# 重启容器
docker restart xxx
#进入容器
docker exec -it xxx bash
# 进入 shell启动的容器
docker exec -it xxx sh
# 查看所有容器
docker ps
# 查看所有容器,包括停止的
docker ps -a
# 容器的状态
docker stats
# 查看某个容器的详细信息
docker inspect xxx
# 复制容器的文件到宿主机
docker cp xxx:src_path dest_path
# 复制宿主机文件到容器
docker cp src_path xxx:dest_path
# 查看容器的日志
docker logs -f xxx
# 清理未使用的镜像、容器、卷和网络。
docker system prune

docker三板斧

一个容器服务突然报错,该如何处理?

第一步:docker ps

docker ps查看所有容器,若没看到,那么docker ps -a,如果docker ps -a也没看到,那么恭喜你,三板斧失败,你需要找到镜像文件,用启动命令或脚本去启动了。反之,如遇到下面三种情况,那么直接docker restart xxx,一般80%的情况下都能解决问题。

  1. docker ps没看到,docker ps -a看到了
  2. status为Exited
  3. status为UP,括号里unhealthy
docker ps
CONTAINER ID   IMAGE          COMMAND                  PORTS                status    NAMES
09b93464c2f7   nginx:latest   "nginx -g 'daemon off"   80/tcp, 443/tcp       Up 16 hours (healthy)         myrunoob
96f7f14e99ab   mysql:5.6      "docker-entrypoint.sh"   0.0.0.0:3306->3306/tcp Exited(0) 16 hours ago       mymysql

Up:表示容器正在运行中。

Created:表示容器已经被创建,但还未启动。

Exited:表示容器已经停止运行

第二步:docker logs -f xxx

容器若重启失败,那么就得看容器的日志了,从容器的日志查看失败的原因,可能内存不足,配置缺失等等,如果找到服务失败的原因,并解决掉,然后docker restart xxx,应该也能解决问题。

第三步:docker inspect xxx

主要检查State,Env,HostConfig里面的信息,一般来说就能看到错误的原因,针对性的去处理即可。

docker inspect xxx
[{"Id": "d2f5e3f19a6a","Created": "2024-07-23T00:00:00Z","Path": "bash","Args": [],"State": {"Status": "running","Running": true,"Paused": false,"Restarting": false,"OOMKilled": false,"Dead": false,"Pid": 12345,"ExitCode": 0,"Error": "","StartedAt": "2024-07-23T00:00:00Z","FinishedAt": "0001-01-01T00:00:00Z"},"Image": "sha256:abc123","ResolvConfPath": "/var/lib/docker/containers/d2f5e3f19a6a/resolv.conf","HostnamePath": "/var/lib/docker/containers/d2f5e3f19a6a/hostname","HostsPath": "/var/lib/docker/containers/d2f5e3f19a6a/hosts","LogPath": "/var/lib/docker/containers/d2f5e3f19a6a/d2f5e3f19a6a-json.log","Name": "/my_container","RestartCount": 0,"Driver": "overlay2","Platform": "linux","MountLabel": "","ProcessLabel": "","AppArmorProfile": "","ExecIDs": null,"HostConfig": {"Binds": null,"ContainerIDFile": "","LogConfig": {"Type": "json-file","Config": {}},"NetworkMode": "default","PortBindings": {},"RestartPolicy": {"Name": "no","MaximumRetryCount": 0},"AutoRemove": false,"VolumeDriver": "","VolumesFrom": null,"CapAdd": null,"CapDrop": null,"Dns": [],"DnsOptions": [],"DnsSearch": [],"ExtraHosts": null,"GroupAdd": null,"IpcMode": "private","Cgroup": "","Links": null,"OomScoreAdj": 0,"PidMode": "","Privileged": false,"PublishAllPorts": false,"ReadonlyRootfs": false,"SecurityOpt": null,"UTSMode": "","UsernsMode": "","ShmSize": 67108864,"Runtime": "runc","ConsoleSize": [0,0],"Isolation": "","CpuShares": 0,"Memory": 0,"NanoCpus": 0,"CgroupParent": "","BlkioWeight": 0,"BlkioWeightDevice": null,"BlkioDeviceReadBps": null,"BlkioDeviceWriteBps": null,"BlkioDeviceReadIOps": null,"BlkioDeviceWriteIOps": null,"CpuPeriod": 0,"CpuQuota": 0,"CpuRealtimePeriod": 0,"CpuRealtimeRuntime": 0,"CpusetCpus": "","CpusetMems": "","Devices": null,"DiskQuota": 0,"KernelMemory": 0,"MemoryReservation": 0,"MemorySwap": 0,"MemorySwappiness": null,"OomKillDisable": false,"PidsLimit": null,"Ulimits": null,"CpuCount": 0,"CpuPercent": 0,"IOMaximumIOps": 0,"IOMaximumBandwidth": 0,"Mounts": [],"MaskedPaths": ["/proc/asound","/proc/acpi","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/proc/scsi","/sys/firmware"],"ReadonlyPaths": ["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"]},"GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/l/abc123/diff","MergedDir": "/var/lib/docker/overlay2/merged","UpperDir": "/var/lib/docker/overlay2/upper","WorkDir": "/var/lib/docker/overlay2/work"},"Name": "overlay2"},"Mounts": [],"Config": {"Hostname": "d2f5e3f19a6a","Domainname": "","User": "","AttachStdin": false,"AttachStdout": false,"AttachStderr": false,"ExposedPorts": {},"Tty": false,"OpenStdin": false,"StdinOnce": false,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["bash"],"Image": "ubuntu","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {}},"NetworkSettings": {"Bridge": "","SandboxID": "abc123","HairpinMode": false,"LinkLocalIPv6Address": "","LinkLocalIPv6PrefixLen": 0,"Ports": {},"SandboxKey": "/var/run/docker/netns/abc123","SecondaryIPAddresses": null,"SecondaryIPv6Addresses": null,"EndpointID": "abc123","Gateway": "172.17.0.1","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","MacAddress": "02:42:ac:11:00:02","Networks": {"bridge": {"IPAMConfig": null,"Links": null,"Aliases": null,"NetworkID": "abc123","EndpointID": "abc123","Gateway": "172.17.0.1","IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"MacAddress": "02:42:ac:11:00:02","DriverOpts": null}}}}
]

一般来说,三板斧能解决95%以上的问题。当然还有一些情况,比如我近期遇到过修改宿主机iptables,导致容器挂掉的情况,重启也会失败,这种情况,需要重启宿主机就能解决此问题。

docker的原理和优势

原理

主要是利用了Linux内核的特性:namespacecgrouprootfs

  1. namespace将文件,网络,进程和权限进行隔离。
  2. cgroup限制namespace可使用的资源,如cpu,内存等
  3. rootfs用以创建新的根文件系统
优势
  1. 独立性,每个容器完全隔离,不会互相影响
  2. 启动快,容器因为简单和资源少,启动速度在秒级
  3. 运行环境一致,不会出现测试环境可以,生产环境失败的情况
  4. 迁移快,更换机器或者扩展节点,只需保存容器,在新机器上启动即可

docker去向何方

docker已经很方便了,为何现在k8s更如火如荼呢?这时候就得说到docker的缺点了。在大型系统中,采用微服务+docker部署方式之后,同时可能存在几百上千的容器在同时运行,同时产生的就是管理问题。我怎么把上千的容器部署到对应的节点上,怎么对容器进行部署重启,总不能一台机器一台机器上去运维部署,这显然不符合技术自动化发展的趋势。在这种情况下,k8s应运而生(其实不仅仅有k8s,还有docker swam等,最后k8s脱颖而出)。k8s简单来说就是容器编排工具,那么我们看看k8s的架构图。

在这里插入图片描述

Controller Manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;

API Server 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制;

Scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上;

Kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡;

etcd 保存了整个集群的状态;

Kubelet 负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的管理;

总结

k8s为使用大规模容器服务提供了极大的便利,对于持续部署和自动化运维几乎都是一键操作。但是在最新的k8s版本已经舍弃了docker,后面可以继续聊聊这个的原因以及k8s的详细介绍和运维

相关文章:

docker的前世今生

docker来自哪里? 从我们运维部署的历史来看,宿主机从最初的物理机到虚拟机,再到docker,一步步演进到现在。技术演进其实是为了解决当前技术的痛点,那我们来看看有哪些痛点以及如何克服痛点的。 物理机 一般来说&…...

python实现施瓦茨-克里斯托费尔【全网首个】根据用户输入推测函数

上代码&#xff1a; from sympy import symbols, integrate, simplify from sympy.plotting import plotn int(input("n:")) if n < 2:print("Error: Must n > 2") i 0 a [] aef [] A [] x, y symbols(x y) z, w symbols(z w)while i < n…...

c语言中的数组(上)

数组的概念 数组是⼀组相同类型元素的集合&#xff1b; 数组中存放的是1个或者多个数据&#xff0c;但是数组元素个数不能为0。 数组中存放的多个数据&#xff0c;类型是相同的。 数组分为⼀维数组和多维数组&#xff0c;多维数组⼀般⽐较多⻅的是⼆维数组。 数组创建 在C语言…...

Unity3D仿星露谷物语开发25之创建时钟界面

1、目标 在时钟界面显示当前时钟信息&#xff0c;同时设置特殊按钮可以快速推进时间用于测试。 2、创建GameClock.cs脚本 在Assets -> Scripts -> TimeSystem目录下创建GameClock.cs脚本。 代码如下&#xff1a; using System.Collections; using System.Collections…...

数据结构测试题1

一、选择题: 1&#xff0e;若长度为n的钱性表采用顺序存储结构&#xff0c;删除它的第i数据元素之前&#xff0c;需要先依次向前移动( )个数据元素。( C ) A .n-i B.ni C.n-i-1 D.n-i1 2.在单链表中&#xff0c;已知q指的结点是p指的结点的直接前驱结点&am…...

android wifi AsyncChannel(WifiManager和WifiP2pManager)

AynscChannel的讲解 [Android]AsyncChannel介绍-CSDN博客 WifiP2pManager里的channel的使用理解 WifiP2pManager.java public void createGroup(Channel c, ActionListener listener) {checkChannel(c);c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.NETWORK_ID_PE…...

【Image Captioning】DynRefer

DynRefer是由中国科学院大学于2024年提出的用于1种用于区域级多模态任务的模型。DynRefer 通过模拟人类视觉认知过程&#xff0c;显著提升了区域级多模态识别能力。通过引入人眼的动态分辨率机制&#xff0c; 能够以同时完成区域识别、区域属性检测和区域字幕生成任务。 文章链…...

Midjourney基础-常用修饰词+权重的用法大全

用好修饰词很关键 Midjourney要用除了掌握好提示词的写法&#xff0c;按照上一篇《做Midjourney最好图文教程-提示词公式以及高级参数讲解》画面主体 场景氛围 主体行为 构图方式 艺术风格 图像质量。 要画出有质感的内容我们必须要掌握好“修饰词”&#xff0c;这些修饰…...

没有屋檐的房子-023粪堆旁边的舞蹈

爱美是天性&#xff0c;贫苦的农村人也一样&#xff0c;贫苦的时代也一样。 本世纪&#xff0c;广场舞在华夏大地遍地开花&#xff0c;甚至都传到了外面。但是广场舞这种舞蹈形式并不是互联网时代的特产&#xff0c;也不是电声设备日益高级和普及时代的特产&#xff0c;更不是大…...

基于Docker的Kafka分布式集群

目录 1. 说明 2. 服务器规划 3. docker-compose文件 kafka{i}.yaml kafka-ui.yaml 4. kafka-ui配置集群监控 5. 参数表 6. 测试脚本 生产者-异步生产: AsyncKafkaProducer1.py 消费者-异步消费: AsyncKafkaConsumer1.py 7. 参考 1. 说明 创建一个本地开发环境所需的k…...

【博客之星】年度总结:在云影与墨香中探寻成长的足迹

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、年度回顾 1、创作历程 2、个人成长 3、个人生活与博客事业 二、技术总结 1、赛道选择 2、技术工具 3、实战项目 三、前景与展望 1、云原生未来…...

SpringBoot的Swagger配置

一、Swagger配置 1.添加依赖 <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</version> </dependency> 2.修改WebMvcConfig Slf4j Configurat…...

machine learning knn算法之使用KNN对鸢尾花数据集进行分类

通过导入必要的scikit-learn导入必要的库&#xff0c;加载给定的数据&#xff0c;划分测试集和训练集之后训练预测和评估即可 具体代码如下&#xff1a; import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split f…...

C语言练习(16)

猴子吃桃问题。猴子第一天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xff0c;又多吃了一个。第二天早上又将剩下的桃子吃掉一半&#xff0c;又多吃了一个。以后每天早上都吃了前一天剩下的一半加一个。到第10天早上想再吃时&#xff0c;见只剩一个桃子了…...

SOAFEE 技术研讨会:汽车软件定义与自动驾驶技术探讨

在本次技术研讨会上&#xff0c;来自汽车与科技领域的专家们围绕汽车软件定义及自动驾驶技术展开了深入交流与探讨。从 SOAFEE 蓝图计划的创新性理念&#xff0c;到 Autoware 开源项目及 Open AD Kit 在实际应用中的探索&#xff0c;再到 Edge Workload Abstraction and Orches…...

R语言学习笔记之开发环境配置

一、概要 整个安装过程及遇到的问题记录 操作步骤备注&#xff08;包含遇到的问题&#xff09;1下载安装R语言2下载安装RStudio3离线安装pacman提示需要安装Rtools4安装Rtoolspacman、tidyfst均离线安装完成5加载tidyfst报错 提示需要安装依赖&#xff0c;试错逐步下载并安装…...

多版本并发控制:MVCC的作用和基本原理

多版本并发控制&#xff1a;MVCC的作用和基本原理 1、MVCC简介1.1 快照读与当前读的区别1.1.1 快照读1.1.2 当前读 1.2 数据库的读写问题1.3 MVCC的作用 2、MVCC实现原理之ReadView2.1 什么是ReadView2.2 ReadView的设计思路2.3 MVCC整体操作流程 1、MVCC简介 1.1 快照读与当前…...

ubuntu18.04安装nvm管理本机node和npm

ubuntu18.04安装nvm管理本机node和npm nvm的使用方法1. 安装nvm2. 加载nvm3. 安装执行版本4. 设置默认版本(可选)5. 检查:6. 将配置加入到shell配置文件中(默认已经加入) 如果系统全局的 Node.js 存在&#xff0c;但被 nvm 覆盖了&#xff0c;可以通过禁用或卸载 nvm 恢复到系统…...

【数据结构进阶】红黑树超详解 + 实现(附源码)

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;数据结构 目录 前言 一、红黑树介绍 二、红黑树原理详解 三、红黑树的实现 1. 节点定义 2. 红黑树类型定义及接口声明 3. 红黑树的插入&#xff08;重点&a…...

leetcode_3092. 最高频率的 ID

https://leetcode.cn/problems/most-frequent-ids/description/ 看到这个数据范围 最极端情况 如果nums全为一个数 并且数量取到最大 那么范围是10的10次方 需要longlong储存 这题主要运用了哈希表配合multiset实现 哈希表主要用作存储某个数的出现次数 mst则用于记录出现次…...

鸿蒙仓颉环境配置(仓颉SDK下载,仓颉VsCode开发环境配置,仓颉DevEco开发环境配置)

目录 ​1&#xff09;仓颉的SDK下载 1--进入仓颉的官网 2--点击图片中的下载按钮 3--在新跳转的页面点击即刻下载 4--下载 5--找到你们自己下载好的地方 6--解压软件 2&#xff09;仓颉编程环境配置 1--找到自己的根目录 2--进入命令行窗口 3--输入 envsetup.bat 4--验证是否安…...

数据统计–图形报表(day11)

Apache ECharts 介绍 Apache ECharts 介绍 Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。 官网地址&#xff1a;Apache ECharts 入门案例 Apache Echarts官方…...

源码分析之Openlayers样式篇CircleStyle类

访问Openlayers网站(https://jinuss.github.io/Openlayers_map_pages/&#xff0c;网站是基于Vue3 Openlayers&#xff0c;里面有大量的实践和案例。觉得还不错&#xff0c;可以 给个小星星Star&#xff0c;鼓励一波 https://github.com/Jinuss/OpenlayersMap哦~ 概述 在 Ope…...

解决CentOS9系统下Zabbix 7.2图形中文字符乱码问题

操作系统&#xff1a;CentOS 9 Zabbix版本&#xff1a;Zabbix7.2 问题描述&#xff1a;主机图形中文字符乱码 解决方案&#xff1a; # 安装字体配置和中文语言包 sudo yum install -y fontconfig langpacks-zh_CN.noarch # 检查是否已有中文字体&#xff1a; fc-list :lan…...

AF3 FourierEmbedding类源码解读

FourierEmbedding 是一个用于扩散条件的傅里叶嵌入类,其核心是将输入的时间步噪声强度或控制参数(timestep)转换为高维的周期性特征。 源代码: class FourierEmbedding(nn.Module):"""Fourier embedding for diffusion conditioning."""de…...

vsftpd虚拟用户部署

vsftpd虚拟用户部署 案例提供两个用户如下,使用centos7验证可行。 test *AO9ih&7 ftp DTx4zp_shell脚本运行一键安装vsftp #!/bin/bash yum -y install vsftpd ftp >/etc/vsftpd/vsftpd.conf cat <<EOL >> /etc/vsftpd/vsftpd.conf anonymous_enableNO l…...

MySQL 容器已经停止(但仍然存在),但希望重新启动它,并使它的 3306 端口映射到宿主机的 3306 端口是不可行的

重新启动容器并映射端口是不行的 由于你已经有一个名为 mysql-container 的 MySQL 容器&#xff0c;你可以使用 docker start 启动它。想要让3306 端口映射到宿主机是不行的&#xff0c;实际上&#xff0c;端口映射是在容器启动时指定的。你无法在容器已经创建的情况下直接修改…...

汇编实验·顺序程序设计

一、实验目的: 1.能够熟练的进行顺序程序的编写,掌握基本的汇编语言指令的用法 2.通过程序设计理解掌握不同类型的数据混合运算的基本规则 3.熟练掌握各种寻址方式,深入理解逻辑地址和物理地址的相关概念 二、实验内容 有三个长度分别为1、2、4个字节的数据,编写程序求…...

AIGC视频扩散模型新星:Video 版本的SD模型

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍慕尼黑大学携手 NVIDIA 等共同推出视频生成模型 Video LDMs。NVIDIA 在 AI 领域的卓越成就家喻户晓&#xff0c;而慕尼黑大学同样不容小觑&#xff0c;…...

HarmonyOS:通过(SQLite)关系型数据库实现数据持久化

一、场景介绍 关系型数据库基于SQLite组件&#xff0c;适用于存储包含复杂关系数据的场景&#xff0c;比如一个班级的学生信息&#xff0c;需要包括姓名、学号、各科成绩等&#xff0c;又或者公司的雇员信息&#xff0c;需要包括姓名、工号、职位等&#xff0c;由于数据之间有较…...