单核CPU, 1G内存,也能做JVM调优吗?
最近,笔者的技术群里有人问了一个有趣的技术话题:单核CPU, 1G内存的超低配机器,怎么做JVM调优?
这实际上是两个问题。单核CPU的超低配机器,怎么充分利用CPU?单核CPU, 1G内存的超低配机器,怎么做JVM调优?

怎么充分利用CPU?
这个问题不能一概而论,要结合具体场景。对于IO密集型和CPU密集型的应用调优的方法会截然不同。
IO密集型:有频繁外部设备访问的应用,如磁盘访问和网络访问等。由于CPU性能相对硬盘读写和网络访问要好很多,系统执行任务时,大部分的情况是CPU在等I/O (磁盘/网络) 的读/写操作,在发生I/O操作时cpu处于等待状态,这就可能导致cpu的利用率不高。
CPU密集型: 以计算为主,很少有磁盘和网络访问的应用。这种任务CPU一直在运行,CPU的利用率很高。
在给出CPU调优结论之前,先花两分钟熟悉一下I/O基础。
所谓的I/O(Input/Output)操作实际上就是输入输出的数据传输行为。程序员最关注的主要是磁盘IO和网络IO,因为这两个IO操作和应用程序的关系最直接最紧密。
磁盘IO:磁盘的输入输出,比如磁盘和内存之间的数据传输。
网络IO:不同系统间跨网络的数据传输,比如两个系统间的远程接口调用。
下面这张图展示了应用程序中发生IO的具体场景:

通过上图,我们可以了解到IO操作发生的具体场景。一个请求过程可能会发生很多次的IO操作:
1,页面请求到服务器会发生网络IO
2,服务之间远程调用会发生网络IO
3,应用程序访问数据库会发生网络IO
4,数据库查询或者写入数据会发生磁盘IO
下面是执行top命令查看CPU状况的截图:

从上图,我们可以看到:
CPU空闲率是0%(上图中红框id)
CPU使用率是22%(上图中红框 us 13% 加上 sy 9%,us可以理解成用户进程占用的CPU,sy可以理解成系统进程占用的CPU)
CPU 在等待磁盘IO操作上花费的时间占比是76.6% (上图中红框 wa)
不少人会这样理解,如果CPU空闲率是0%,就代表CPU已经在满负荷工作,没精力再处理其他任务了。真是这样的吗?
我们先看一下计算机是怎么管理磁盘IO操作的。计算机发展早期,磁盘和内存的数据传输是由CPU控制的,也就是说从磁盘读取数据到内存中,是需要CPU存储和转发的,期间CPU一直会被占用。我们知道磁盘的读写速度远远比不上CPU的运转速度。这样在传输数据时就会占用大量CPU资源,造成CPU资源严重浪费。
后来有人设计了一个IO控制器,专门控制磁盘IO。当发生磁盘和内存间的数据传输前,CPU会给IO控制器发送指令,让IO控制器负责数据传输操作,数据传输完IO控制器再通知CPU。因此,从磁盘读取数据到内存的过程就不再需要CPU参与了,CPU可以空出来处理其他事情,大大提高了CPU利用率。这个IO控制器就是“DMA”,即直接内存访问,Direct Memory Access。现在的计算机基本都采用这种DMA模式进行数据传输。

通过上面内容我们了解到,IO数据传输时,是不占用CPU的。当应用进程或线程发生IO等待时,CPU会及时释放相应的时间片资源并把时间片分配给其他进程或线程使用,从而使CPU资源得到充分利用。所以,假如CPU大部分消耗在IO等待(wa)上时,即便CPU空闲率(id)是0%,也并不意味着CPU资源完全耗尽了,如果有新的任务来了,CPU仍然有精力执行任务。如下图:

在DMA模式下执行IO操作是不占用CPU的,所以CPU IO等待(上图的wa)实际上属于CPU空闲率的一部分。所以我们执行top命令时,除了要关注CPU空闲率,CPU使用率(us,sy),还要关注IO Wait(wa)。注意,wa只代表磁盘IO Wait,不包括网络IO Wait。
了解完IO的基础知识,我们看看在单核CPU的超低配机器上,怎么充分利用CPU?
对于IO密集型应用。CPU会有很多时间花在IO等待上,发生IO时虽然CPU空闲率(上图的id)受到影响,但是实际上cpu并没有干活。这时就需要较多的线程数量,当一部分线程因为IO问题被阻塞时,其他空闲线程还能继续接收并执行其他请求任务。这样cpu利用率就会更高。同时还要考虑线程间上下文切换带来的性能开销,线程数量不能太高。对于单核CPU,要根据IO的密集程度设置线程数。由于CPU只有一核,资源有限,所以除了对线程数的优化外,主要还是要优化IO操作,减少IO操作频率,缩短IO操作时间。IO操作优化之后,线程数可以设置成更少,线程切的换频率和性能开销也会随之降低。
对于CPU密集型应用。线程数应该尽可能少一些,在没有任何IO操作的情况下,为了减少线程切换带来的性能开销,理论上最佳的线程数量应该设置成CPU的核数。不过实际场景中,绝大多数应用或多或少都会有一定的IO操作(比如记录Log,访问数据库或者跨网络的远程调用等),这样线程数就需要适当调大。至于设置成多少,就没有定论了,需要我们多次调整验证(取性能测试的最优结果)。对于单核CPU,为了减少线程切换带来的性能开销,一两个线程基本就够了。

怎么做JVM调优?
选择合适的垃圾收集器
CMS和G1是目前最炙手可热的两个垃圾回收器,基本上所有公司都在使用CMS或G1。不过,在单核CPU,内存只有1G的机器上,CMS和G1就不太合适了。
以CMS回收过程为例,在耗时较长的并发标记和并发清除阶段,垃圾收集线程和用户线程是同时并行工作的,也就是说并发阶段不会导致用户线程停顿。不过CMS对CPU资源非常敏感。 其实,所有高并发的应用对CPU资源都很敏感。在CMS并发阶段(并发标记和并发清除阶段),虽然不会导致用户线程停顿,但是垃圾收集线程会占用一部分CPU资源,进而导致应用程序变慢,吞吐量降低。CMS默认启动的垃圾收集线程数是(CPU核数+3)/4,当CPU核数在4个以上时,并发回收阶段垃圾收集线程不少于25%的CPU资源(CPU核数)。但是当CPU核数不足4个时,比如CPU核数为2个,CMS对用户程序的影响就可能变得很大,此时需要分配1个核的资源去执行垃圾收集任务,如果本来CPU负载就比较大,还要分出一半的计算能力去执行垃圾收集任务,就可能导致应用程序的执行速度大幅下降,甚至忽然降低50%以上,着实让人无法接受。
在单核CPU环境下,并发标记和并发清除阶段是无法真正做到并发的,当垃圾收集线程执行标记和清除任务时,单核CPU唯一的核就无法执行用户线程,这样就会造成严重的用户线程阻塞问题,导致应用程序响应超慢。
说到这有人可能会问:换成其他垃圾收集器,在单核CPU环境下,不一样会有这种因为线程阻塞导致的应用程序执行变慢的问题吗?
没错,换成其他垃圾收集器,在单核CPU环境下,一样会有同样的问题。不过情况应该会比使用CMS或者G1要好!CMS是响应速度优先的老年代垃圾收集器,是一种以降低GC全局停顿时间(Stop The World)为目标的收集器。为了实现这一目标,CMS把垃圾回收分成了初始标记,并发标记,重新标记和并发清除4个阶段。其中初始标记和重新标记两个阶段会停止所有用户线程(发生STW),不过耗时很短。并发标记和并发清除两个阶段耗时最长,但是这两个阶段垃圾收集线程可以和用户线程一起工作,不会停止用户线程。CMS的这种设计虽然缩短了STW的时间,但是整个GC过程(四个阶段加在一起的总时间)更长了。如果在单核CPU环境下,并发标记和并发清除两个阶段就无法做到真正的并发,因为单核的问题,垃圾收集线程和用户线程不可能同时占用唯一的CPU资源,所以在垃圾收集线程运行时所有用户线程都会被停止,相当于发生了STW。基本上可以这样理解,在单核CPU环境下,CMS的四个阶段都会发生Stop The World。也就是说,在单核CPU环境下,CMS的Stop The World时间比传统的老年代收集器Serial Old和Parallel Old还要长。所以在单核CPU环境下,绝对不能选择CMS和G1这种对CPU特别敏感的收集器。考虑到Parallel Old是一款多线程并发收集器,主要为了利用多核CPU来提高垃圾回收效率,不适合单核环境。所以,基本上最古老的Serial Old收集器就成了单核CPU的最佳选择啦。
另外,1G的内存空间太小,也不适合CMS和G1。数年前,在CMS和G1还没诞生之前,很多互联网系统使用Serial Old和Parallel Old做为老年代收集器,这样会带来一个严重问题,堆内存越大垃圾回收时STW(Stop The World)时间就越长,在互联网系统中,堆内存往往会超过4G,每次Full GC时STW时间会很长,可能会达到几秒钟甚至更长,也就是说JVM在这几秒钟内无法处理任何用户请求。这在高并发的互联网系统中是无法接受的。后来随着CMS和G1先后应运而生,解决了较大堆内存GC时STW时间过长的问题。所以说CMS和G1只是为了大内存场景设计的,不适合小内存场景,在小内存场景下不能发挥自己的优势。如果内存只有1G,单核CPU下为了提高吞吐量可以选择Serial Old。多核CPU下,为了充分发挥多核作用提高垃圾收集效率,可以选择多线程并发收集器Parallel Old。
降低GC频次
在给出具体降低GC频次方案之前,我们以Java官方的HotSpot JVM为例,先了解一下堆内存分布以及对象的分配和流转过程。

JVM将堆内存分为了三部分:新生代(Young Generation),老年代(Old Generation),永久代(Permanent Generation)。其中新生代又分为三部分:伊甸园区(Eden),和两个幸存区S0和S1。
注:JDK1.8之后,Java官方的HotSpot JVM去掉了永久代,取而代之的是元数据区Metaspace。Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大小只与本地内存的大小有关。因此JDK1.8之后,就见不到java.lang.OutOfMemoryError: PermGen space这种由于永久代空间不足导致的内存溢出的问题了。
堆内存中对象的分配和流转过程

新创建的对象会先被分配到到Eden区。JVM刚启动时,Eden区对象数量较少,两个Survivor区S0、S1几乎是空的。

随着时间的推移,Eden区的对象越来越多。当Eden区放不下时(占用空间达到容量阈值),新生代就会发生垃圾回收,我们称之为Minor GC或者Young GC。

发生GC时,第一步会通过可达性分析算法找到可达对象。如上图,蓝色为可达对象,其他紫色为不可达对象。第二步,被标示的可达对象会被转移到S0(此时S0是From Survivor),此时存活对象年龄加1,三个对象年龄都变为1。第三步,清除Eden区所有对象。

GC后各区域对象占用情况,如上图所示。

程序继续运行,Eden区再次达到容量阈值时,会再次发生GC。这时S0(From Survivor)已经有了对象。还是同样的步骤,通过可达性分析算法找到可达对象,然后再将Eden和S0中的可达对象转移到S1(To Survivor),各存活对象年龄加1。最后将Eden和S0中的所有对象清除。

GC后S0区域被清空。如上图所示。S0和S1发生了互换,S1变成了From Survivor,S0变成了To Survivor。
注意,To Survivor区永远都为空。这实际上是垃圾回收算法-复制算法在年轻代的实际应用。把年轻代分为Eden,S0,S1三个区域,每次垃圾回收时把可达对象复制到S0或S1,然后再清除掉Eden和(S1或S0)中的所有对象。由于每次GC时,新生代的可达对象非常少(绝大部分对象要被回收掉),一般不会超过新生代总体空间的10%,所以搜寻可达对象以及复制对象的成本都会非常低。而且这种复制的方式还能避免产生堆内存碎片,提高内存利用率。很多年轻代垃圾收集器都采用复制算法,如ParNew。

在程序运行过程中,新生代GC会反复发生,长寿对象会在S0和S1之间反复交换,年龄也会越来越大,当对象达到年龄上限时,会被晋升到老年代。这个年龄上限默认是15,可以通过参数-XX:MaxTenuringThreshold设置。如下图,有些年轻代对象年龄达到了上限15,被转移到了老年代。

通过上面的图文内容,我们了解了堆内存中对象的分配和流转过程。那么可以基于这些知识来做一些JVM调优的工作。
所谓降低GC频次,主要指的是降低Major GC(老年代GC)次数。内存只有1G,为了减少Major GC,最简单的做法是适当调大老年代比例,但是老年代空间总有个上限,需要在老年代和年轻代之间找一个平衡点。还可以适当调大MaxTenuringThreshold,来提高年轻代幸存区s0和s1的交换次数,进而减少对象晋升到老年代的几率。另外调大幸存区比例,也可以减少基于动态对象年龄判定导致对象晋升老年代的几率。不管是哪种优化手段,都需要反复调整和验证(可以做性能测试验证调整结果)。
再补充一个基础知识点。Full GC,Major GC,Minor GC之间是什么关系?
当前绝大部分垃圾收集器都采用分代回收的策略,年轻代和老年代的GC分别独立进行。一般情况下,老年代Major GC是由年轻代Minor GC触发的,Minor GC会导致部分存活时间较长的对象晋升到老年代,在晋升过程中如果老年代使用空间达到阈值就会发生Major GC。这种由Minor GC触发Major GC引发整个堆内存GC的情况,我们一般称之为Full GC。还有一些情况也会触发Major GC,比如大对象初始化时会跨过年轻代直接分配到老年代,这种情况触发的Major GC和Minor GC就没半点关系了。可以通过-XX:PretenureSizeThreshold参数设置大对象的大小,如果参数被设置成5MB,超过5MB的大对象会直接分配到老年代。
缩短GC时间
缩短GC时间和降低GC频次,两者是鱼和熊掌的关系,不可兼得。如上面所说,在1G内存单核CPU的场景下,响应时间优先的CMS和G1都不适合。在垃圾收集器没有太多选择的情况下,如果想缩短Major GC时间,基本上只能减小老年代的比例了,老年代空间越小,每次Major GC需要处理的对象就越少,GC时间也就越短。老年代空间越小,GC的频次自然也会更高,内存空间就那么多,所以我们需要反复试验,在GC频次和GC时间上找到最佳平衡点来满足业务系统的要求。
结语
JVM调优没有什么可以拿来即用的固定模板或规范,每个应用都有自己的独特场景。不同的应用并发程度不一样,对响应时间和吞吐量要求也不一样,堆内存对象规模、对象生命周期、对象大小等等都不会完全一样,这些因素都会影响到JVM的性能。所以,JVM调优是一个循序渐进的过程,必然需要经历多次迭代,最终才能得到一个较好的折中方案。
相关文章:

单核CPU, 1G内存,也能做JVM调优吗?
最近,笔者的技术群里有人问了一个有趣的技术话题:单核CPU, 1G内存的超低配机器,怎么做JVM调优?这实际上是两个问题。单核CPU的超低配机器,怎么充分利用CPU?单核CPU, 1G内存的超低配机器,怎么做J…...

《计算机应用研究》投稿经历和时间节点
记录四川计算机研究院《计算机应用研究》期刊投稿经历和时间节点。 日期状态周期2022.11.09上传稿件当天显示编辑部已接收稿件,开始初审2022.11.09 – 2022.11.15初审6天2022.11.15 – 2022.12.21外审36天2022.12.21收到退修意见(邮件形式)编…...

mars3d获取视窗的范围
期望效果 :1.我现在想获取到当前视窗的地图范围,请问有什么⽅法可以拿到吗 2.⽐如当前视窗地图范围的边界点,每个边界点的经纬度 回复:1.mars3d的API⽂档中有相关的⽅法 2.具体使⽤可以参考⽂档地址:http://mars3d.cn/api/Map.htm…...

《高性能MySQL》读书笔记(上)
目录 MySQL的架构 MySQL中的锁 MySQL中的事务 事务特性 隔离级别 事务日志 多版本并发控制MVCC 影响MySQL性能的物理因素 InnoDB缓冲池 MySQL常用的数据类型以及优化 字符串类型 日期和时间类型 数据标识符 MySQL的架构 默认情况下,每个客户端连接都…...

05-代理模式
代理模式 代理模式使用代理对象来代替真实对象的访问,在不修改原有对象的前提下,提供额外的操作,扩展目标对象的功能。代理模式分为静态代理和动态代理。 静态代理 手动为目标对象中的方法进行增强,通过实现相同接口重写方法进…...

RocketMQ源码分析之消费队列、Index索引文件存储结构与存储机制-上篇
RocketMQ 存储基础回顾: 源码分析RocketMQ之CommitLog消息存储机制 本文主要从源码的角度分析 Rocketmq 消费队列 ConsumeQueue 物理文件的构建与存储结构,同时分析 RocketMQ 索引文件IndexFile 文件的存储原理、存储格式以及检索方式。RocketMQ 的存储…...

基于Java的浏览器的设计与实现毕业设计
技术:Java等摘要:当今世界是一个以计算机网络为核心的信息时代,互联网为人们快速获取、发布和传递信息提供了便捷,而浏览器作为互联网上查找信息的重要工具,给人们提供了巨大而又宝贵的信息财富,受到了大家…...

手把手教你使用vite打包自己的js代码包并推送到npm
准备 要有npm账号,没有的铁子去npm官网注册一个,又不要钱。 使用vite创建项目 一行代码搞定 npm create vite viet-demo框架选择Others 模板选择library 选择ts 这样项目就创建完了 这个项目默认有一个函数,用来记录按钮的点击次数并…...

Tomcat源码分析-关于tomcat热加载的一些思考
在前面的文章中,我们分析了 tomcat 类加载器的相关源码,也了解了 tomcat 支持类的热加载,意味着 tomcat 要涉及类的重复卸装/装载过程,这个过程是很敏感的,一旦处理不当,可能会引起内存泄露 卸载类 我们知…...

DataWhale 大数据处理技术组队学习task4
五、分布式并行编程模型MapReduce 1. 概述 1.1 分布式并行编程 背景:摩尔定律已经开始逐渐失效,提升数据处理计算能力刻不容缓。传统的程序开发与分布式并行编程 传统的程序开发:以单指令、单数据流的方式顺序执行,虽然这种方式…...

Oracle 12C以上统计信息收集CDB、PDB执行时间不一致问题
文章目录前言一、统计信息窗口期调查二、时区调查三、查询alert记录四、why Database Statistic Collection Job is running two times inside a Maintenance Window?五、Default Scheduler Timezone Value In PDB$SEED Different Than CDB六、总结前言 在实际工作中发现一个…...

用Python获取弹幕的两种方式(一种简单但量少,另一量大管饱)
前言 弹幕可以给观众一种“实时互动”的错觉,虽然不同弹幕的发送时间有所区别,但是其只会在视频中特定的一个时间点出现,因此在相同时刻发送的弹幕基本上也具有相同的主题,在参与评论时就会有与其他观众同时评论的错觉。 在国内…...

算法训练营 day55 动态规划 买卖股票问题系列3
算法训练营 day55 动态规划 买卖股票问题系列3 最佳买卖股票时机含冷冻期 309. 最佳买卖股票时机含冷冻期 - 力扣(LeetCode) 给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。 设计一个算法计算出最大利润。在满足以下…...

电商共享购模式,消费增值返利,app开发
在当今以市场需求为主导的数字经济时代,消费者需求呈现出精细化管理和多元化的特性,目标市场日渐完善,另外在大数据技术迅速进步和运用的驱动下,总体行业的发展节奏感也在不断加速。因而,企业需要建立一套灵活多变的经…...

机房信息牌系统
产品特色: 无线低功耗安装简单,快速布置易于维护墨水屏显示,清晰,更环保信息后台推送,远程管理多模版样式随意制作多尺寸:4.2寸,7.5寸,10.2寸4.2寸7.5寸10.2寸标签特性:…...

金测评 手感更细腻的游戏手柄,双模加持兼容更出色,雷柏V600S上手
很多朋友周末都喜欢玩玩游戏放松一下,在家玩游戏的时候,PC是大家常用的平台,当然了,玩游戏的时候用键鼠的话,手感难免差点意思,还是要手柄才能获得更好的体验。我现在用的是雷柏V600S,这是一款支…...

Windows10 下测试 Intel SGX 功能
文章目录参考文献系统要求一、安装Open Enclave SDK 环境(一)什么是Open Enclave SDK(二)启动SGX功能方法一: BIOS启动方法二:软件方式启动(三)安装必要环境(1࿰…...

Tina_Linux_功耗管理_开发指南
Tina Linux 功耗管理开发指南 1 概述 1.1 编写目的 简要介绍tina 平台功耗管理机制,为关注功耗的开发者,维护者和测试者提供使用和配置参考。 1.2 适用范围 表1-1: 适用产品列表产品名称内核版本休眠类型参与功耗管理的协处理器R328Linux-4.9NormalS…...

golang编译dll失败问题解决
执行go build -buildmodec-shared -o exportgo.dll exportgo.go报类似如下错误/usr/lib/gcc/x86_64-pc-msys/9.1.0/../../../../x86_64-pc-msys/bin/ld: 找不到 -lmingwex/usr/lib/gcc/x86_64-pc-msys/9.1.0/../../../../x86_64-pc-msys/bin/ld: 找不到 -lmingw32安装tdm gcc m…...

Convolutional Neural Networks for Sentence Classification
摘要 We report on a series of experiments with convolutional neural networks (CNN) trained on top of pre-trained word vectors for sentence-level classification tasks. We show that a simple CNN with little hyperparameter tuning and static vectors achieves e…...

基于SpringBoot的共享汽车管理系统
文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏…...

TCP三次握手
参考:4.1 TCP 三次握手与四次挥手面试题 | 小林coding TCP 头格式 我们先来看看 TCP 头的格式,标注颜色的表示与本文关联比较大的字段,其他字段不做详细阐述。 序列号:在建立连接时由计算机生成的随机数作为其初始值,…...

未来土地利用模拟FLUS模型
未来土地利用模拟(FutureLand-Use Simulation, FLUS)模型1 模型简介1.1 基于ANN 的适宜性概率计算1.2 基于自适应惯性机制的元胞自动机1.3 模拟精度评价参考流域 径流变化是 自然因素和 人为因素共同作用的结果,其中人为因素最为直接的方式就…...

压力传感器MPX5700D/MPX5700GP/MPX5700AP产品概述、特征
MPX5700系列压阻式换能器是最先进的单片硅压力传感器,可广泛用于各种应用,特别是采用A/D输入微控制器或微处理器的应用。这一获得专利的单元件传感器集合了高级微加工技术、薄膜金属化、双极工艺,能够提供精确的、与所施加压力成正比的高电平…...

taobao.trades.sold.query( 根据收件人信息查询交易单号 )
¥开放平台免费API必须用户授权聚石塔内调用 根据收件人信息查询交易单号。 公共参数 请求地址: HTTP地址 公共请求参数: 公共响应参数: 请求参数 请求示例 TaobaoClient client new DefaultTaobaoClient(url, appkey, secret); TradesSoldQueryRequest req new…...

【JavaWeb】JSON、AJAX(305-317)
305.JSON-什么是JSON JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON 采用完全独立于语言的文本格式,而且很多语言都提供了对 json 的支持(包括 C, C, C#, Java, JavaScript, Perl,…...

AI入场,搜索这个“营销枢纽”有新故事吗?
哪里有内容,哪里就有搜索。 以前,互联网离我们生活很远,传统搜索与用户的距离分割,只有当用户想要了解什么,才会去使用。 如今,互联网与真实世界密不可分,加之新技术、新平台的不断涌现…...

字节在职5年,一个测试工程师的坎坷之路
几年前进入到IT行业,现在发现学习软件测试的人越来越多,今天我想根据自己的行业经验给大家提一些建议。 跟其他行业相比,做软件测试的岗位确实算是高薪职业,我们那个时候起步的工资并不高,而看现在很多毕业的学生薪资都…...

什么是web框架?
什么是web框架? 我们解释一个概念的时候,通常会用到其他更多的概念去解释它,如果听的人不理解解释它的概念,那么这个解释是失败的,因此首先要回答一下解释web框架中所用到的概念。 回答这个问题前,首先需…...

说一说关系数据库中的范式建模
面试中可能会被问到,来回顾总结一下,参考《数据库系统第五版》(王珊/萨师煊) 范式(normal form),我的理解是用来规范关系数据库中实体如何划分以及实体间如何建立联系来保持数据完整性的一种指导思想,目的就…...