
雄关漫道真如铁而今迈步从头越。经过了漫长的休息期后其实也就休息了一周左右我终于再次开始了学习。今天先大致复习一下过往学习的内容对在笔试面试中遇到的问题进行一个梳理和总结。C字符串的内存分区是怎样的我们都知道如果是字符串常量那么会存储在常量区当然如果你是局部变量就在栈上是动态分配的内存就在堆上然而其实还有一个考虑我们会把较短的字符串放在栈上把较长的字符串放在堆上。它能显著提升性能避免对短字符串进行昂贵的堆内存分配和释放操作减少内存碎片同时提高CPU缓存命中率使内存访问速度更快。一些常见的字符串操作函数我们在这里介绍一下strlen(),strcpy(),strcmp(),strcat()。strlen 用于计算字符串长度不包括结尾的\0strcpy 将源字符串包括\0复制到目标空间strcat 将源字符串连接到目标字符串的末尾覆盖目标串的\0strcmp 按ASCII码值逐字符比较两个字符串返回零表示相等非零值表示大小关系。使用时务必确保目标空间足够大且可写并且源字符串必须以\0结尾否则可能导致缓冲区溢出或未定义行为。如何理解string中的\0std::string是一个更为现代的字符串类型它不依赖\0来判断字符串的结束。它通过一个内部的成员变量通常是size或length来明确知道字符串的长度。因此std::string甚至可以包含中间带\0的字符序列而不会被截断。然而为了与大量现有的 C 语言库函数如strcpy,printf等进行交互这些函数都要求传入的字符数组必须以\0结尾std::string提供了c_str()和data()(C11 起) 方法。这两个方法返回的指针指向一个保证以\0结尾的字符数组。这就是它内部存储末尾需要那个\0的主要原因。new分配内存的大小总是等同于sizeof的结果吗new分配内存的大小确实是基于sizeof的返回值的但是事实上一般new会比sizeof的返回值稍微大一些主要是因为内存分配器需要额外的空间来存储管理这块内存所需的元信息例如分配块的大小并且为了满足系统的内存对齐要求分配器也可能略微多分配一些空间。这些额外的开销对程序员是透明的但确保了内存管理的正确性和高效性。什么是WebSocketWebSocket 是一种网络通信协议它通过在单个TCP连接上建立持久化的全双工通道实现了服务器与客户端之间的真正双向实时通信。这使得服务器可以主动、即时地向客户端推送数据彻底摆脱了传统HTTP协议“请求-响应”模式带来的延迟和高开销非常适合构建聊天应用、实时数据看板、在线游戏等需要高实时性的场景。PSWebSocket是应用层协议。有哪些常见的I/O方式这其中第四种IO信号驱动IO的应用场景有限我们主要针对其他四种IO进行介绍这四者的差异基本体现在数据准备阶段和数据拷贝阶段。红黑树是什么具有哪些性质红黑树Red-Black Tree是一种自平衡的二叉查找树Binary Search Tree。它通过在节点中增加一个存储位表示颜色红或黑和遵循特定的规则来确保树在插入和删除元素后能通过旋转和变色快速恢复平衡从而保证查找、插入、删除等操作在最坏情况下仍能保持较高的效率时间复杂度为 O(log n)。对于有n个节点的红黑树对于有 n 个节点的红黑树其高度 h ≤ 2log₂(n1)大于理论最小值 log₂n。红黑树在插入和删除操作时所需的旋转次数更少插入最多2次旋转删除最多3次旋转且调整频率较低。这是因为红黑树的平衡要求更为宽松允许暂时的“近似平衡”。AVL树为了维持其严格的平衡左右子树高度差不超过1在插入和删除操作后可能需要进行更多的旋转操作并需要维护从被删节点到根节点路径上所有节点的平衡因此在这些写操作上开销通常更大。因此红黑树在插入和删除写操作上通常比AVL树更高效。什么是图的最小生成树有哪些相关算法最小生成树是指在一个带权的、连通的、无向图中找到一个边的子集使得这个子集能够连接图中的所有顶点并且不形成任何环同时满足所有边的权重之和最小。求解最小生成树的两个经典算法是 Kruskal算法 和 Prim算法它们都基于贪心算法的策略即在每一步选择中都采取当前看来最优的选择。Kruskal算法采用 “加边法”全局地将所有边按权值从小到大排序然后依次选择不会形成环的边加入生成树常用并查集判环适合稀疏图边少Prim算法采用 “加点法”从某顶点开始逐步选择与当前生成树相连的最小权值边来扩展生成树常用优先队列维护候选边适合稠密图边多。当图中存在权重相同的边时最小生成树MST的结果可能不唯一这意味着你可能会得到多棵结构不同但总权重相同的生成树。C中支持对nullptr调用delete吗在 C 中使用delete或delete[]释放一个nullptr是完全安全的。语言标准明确规定了这一行为它不会执行任何操作也不会导致运行时错误。delete操作符的内部实现包含了对空指针的检查因此如果ptr是nullptr检查条件失败不会进行任何操作。可以访问union中的非活跃成员吗在union中所有成员共享同一块内存空间。在任意时刻只有一个成员是“活跃”的即其值是被最后写入的。通过其他非活跃成员来读取这块内存属于未定义行为。可以分别介绍一下工厂模式装饰器模式观察者模式和单例模式吗单例模式确保一个类只有一个实例并提供全局访问点用于控制资源共享工厂模式将对象创建逻辑封装起来使客户端无需关心具体实现实现了创建与使用的分离装饰器模式通过包装原有对象来动态添加新功能提供了比继承更灵活的扩展方式观察者模式则定义了一种一对多的依赖关系当一个对象状态改变时所有依赖它的对象都会自动得到通知和更新。可以用shared_ptr来管理this指针吗如何解决问题std::shared_ptr的机制是为其管理的每个对象创建一个控制块存储引用计数等信息。如果你直接使用this指针创建新的shared_ptr即使对象已被另一个shared_ptr管理新创建的shared_ptr无法感知到已有的控制块它会为自己创建一个全新的、独立的控制块。要安全地解决这个问题应让你的类公有继承std::enable_shared_from_thisT其中T是你的类名然后在成员函数中通过shared_from_this()方法来获取指向自身的shared_ptr。关键在于std::enable_shared_from_this内部维护了一个std::weak_ptr它会在对象被首个shared_ptr管理时例如通过std::make_sharedT()或std::shared_ptrT(new T)自动关联到该shared_ptr的控制块此后调用shared_from_this()会通过这个weak_ptr安全地升级为一个新的shared_ptr并与所有现有shared_ptr共享同一个控制块和引用计数从而避免重复释放。UnityUnity有哪些IO操作Unity主要提供了针对文件资源和网络的IO操作。如何理解C#中的Attribute特性C# 中的 Attribute特性是一种强大的声明性标签用于为代码元素如类、方法、属性、参数等附加元数据metadata。这些元数据可以在编译时或运行时被读取从而影响程序的编译过程、运行时行为或提供额外的信息。常见的 Attribute 及其用途如下C#的unsafe是什么有何作用C# 中的unsafe上下文就像一把打开底层操作权限的“钥匙”。通过在代码块或方法外使用unsafe关键字你可以在其内部直接使用指针进行内存地址的读写和算术运算从而绕过 CLR 的安全检查来提升性能或与非托管代码交互。但这不仅仅是“能用指针”它还解锁了fixed固定托管对象防GC移动、stackalloc在栈上分配临时内存等关键操作。然而这是一把双刃剑它要求开发者自行承担内存安全的责任如避免越界访问和内存泄漏因此应谨慎使用并优先考虑SpanT等安全替代方案。如何理解Unity中的Sprite和Texture在 Unity 中Texture纹理 和 Sprite精灵 是处理图像资源的两个核心概念它们既有区别又紧密联系。Texture 是原始的图像数据本质上是存储在 GPU 内存中的一张图片它主要负责定义物体表面的颜色和细节信息通常作为 3D 模型的贴图需要通过与材质球Material关联并赋予网格模型来使用。Sprite 则可以理解为基于 Texture 的、专为 2D 和 UI 设计的一种资源类型它不仅包含了纹理数据还是一个可定位、可移动的 2D 对象拥有位置、旋转、缩放等属性可以直接被 Unity 的 UI 系统如Image组件或 2D 渲染系统如Sprite Renderer使用并支持动画、碰撞检测等功能。它们的联系在于Sprite 通常是从 Texture 中“切割”或定义出来的一个部分或者直接使用整张 Texture一个 Texture 可以通过 Sprite Editor 设置为多个 Sprite形成图集Atlas从而优化渲染性能。区别则在于用途和功能Texture 是基础的数据层而 Sprite 是基于 Texture 的应用层对象专为 2D 交互和显示设计。简单来说所有 Sprite 都依赖 Texture 提供图像源但并非所有 Texture 都需要用作 Sprite例如有些 Texture 仅作为 3D 模型的贴图。Unity的材质球是什么可以将 材质球 (Material Asset) 理解为一张“设计图纸”而 材质实例 (Material Instance) 则是根据这张图纸批量生产出的“产品”。修改图纸所有产品都会变但修改某一个产品不会影响图纸和其他产品。在 Unity 中你首先需要自定义一个着色器使用 Shader Graph 或编写 ShaderLab/CG/HLSL 代码这定义了物体表面的渲染逻辑和可调参数。接着在编辑器里创建一个材质球.mat 文件并将你写好的着色器赋给它此时材质球就成了一个可配置的着色器实例。然后将这个材质球拖拽或指定给场景中的模型Mesh模型的渲染器Renderer会使用它。默认情况下所有使用该材质球的模型会共享同一份材质数据但当你通过代码 (renderer.material) 访问或修改其属性时Unity 会自动为该模型创建一个独立的材质实例后续的所有修改都只影响这个实例而不会影响原始的材质球资源。关于Resources,AB包和AddressableUnity 中的 Resources、AssetBundles (AB 包) 和 Addressables 是三种主流的资源管理方式它们在存储、加载和卸载行为上各有特点。AssetBundleAB 包和 Addressables 的核心区别在于自动化程度和易用性。AB 包是一个相对底层的系统需要开发者手动处理依赖关系如通过AssetBundleManifest递归加载依赖包、自行实现引用计数机制来管理内存释放并完全手动编写热更新流程包括版本对比、差分下载和资源替换。而 Addressables 是在 AB 包之上构建的高阶抽象层它通过一个中心的 catalog文件自动管理所有依赖加载资源时开发者无需关心内部依赖链它内置了基于引用计数的内存管理通过Release方法自动跟踪和卸载不再使用的资源极大降低了内存泄漏风险其热更新流程也更高效通过对比catalog文件可实现增量更新仅下载变化的资源包。简而言之Addressables 通过自动化解决了 AB 包在依赖、内存和更新三大方面的繁琐管理问题显著提升了开发效率和可靠性。Unity中的层级系统有哪些Hierarchy 窗口是 Unity 编辑器中的一个核心面板它以树形结构直观地展示了当前场景中的所有游戏对象 (GameObject)Layer 是分配给单个游戏对象的一种标签或分类主要用于过滤Sorting Layer这套系统专门用于控制 2D 精灵 (Sprites) 和 UI 元素的渲染顺序谁在前谁在后。父类的transform变化可见性都会影响子类同时父类和子类之间的组件可以互相获取。Unity提供的PlayerPrefs是什么有何作用Unity 中的PlayerPrefs是一个用于在玩家设备本地存储和读取简单数据的键值对存储系统。它本质上是 Unity 引擎提供的一个静态类核心作用是实现数据的持久化让你能够在游戏会话之间保存和加载一些基础信息。它的工作原理很简单你给每份数据一个唯一的键Key字符串类型并为其赋值Value仅支持三种基本类型。这些数据会被写入设备本地特定的存储位置如 Windows 的注册表、Android 的 XML 文件等。.Net和Mono的关系是什么.NET 是一个由微软主导的跨语言、跨平台的开发者平台和生态标准它定义了一套通用的API规范.NET Standard和运行环境CLR而 Mono 则是 .NET 标准的一个开源、跨平台的具体实现它最早由社区开发旨在让 .NET 应用程序尤其是使用 C# 编写的程序能够运行在 Linux、macOS、Android 等非 Windows 操作系统上并成为 Unity 游戏引擎长期使用的脚本运行时环境。Unity协程的底层原理是什么Unity 协程的底层原理是C# 编译器将你的协程方法编译成一个状态机Unity 引擎的调度器在主线程的每一帧中根据条件驱动这些状态机的执行。它提供了一种在单线程内优雅处理异步和延时操作的强大模式。Unity 协程的底层原理是 C# 编译器将包含yield return的协程方法编译成一个实现了IEnumerator接口的状态机类这个类通过一个状态变量记录执行位置并通过MoveNext()方法推进Unity 引擎的 C 层则提供了一个协程调度器它在主线程每帧的特定阶段如 Update 后遍历所有活跃协程检查其yield return返回的条件如等待时间是否结束、条件是否满足若条件满足则调用该状态机的MoveNext()方法使协程从上次暂停的位置继续执行直到协程结束或再次被挂起。整个过程完全在主线程中进行本质是分帧的条件调度而非多线程并发。Unity实现跨平台的原理简而言之Unity 就像一位全能的“翻译官”和“总指挥”。它先让你的代码变成一种通用语言IL然后根据要去的“国家”目标平台要么派出一位即时翻译Mono虚拟机要么直接生成一份当地语言的剧本IL2CPP生成原生码。同时它还把不同地方的硬件和系统功能如图形、输入、文件系统都包装成统一的工具给你用平台抽象层只有在遇到极其特殊的地方法规时才需要请本地专家原生适配模块来处理。物理什么是SAT分离轴定理SAT的核心思想基于一个直观的几何观察若两个凸图形在所有可能的轴上投影均重叠则它们相交反之若存在一条轴使其投影不重叠则它们分离。GJK算法又是什么GJK算法通过迭代方式构建一个称为单纯形的几何结构并判断该结构是否包含原点从而确定两个凸体是否相交。UGUI我们来聊聊UGUI的底层原理当你点击屏幕时EventSystem 中的 Input Module如StandaloneInputModule会捕获输入信息并生成一个 PointerEventData 数据结构然后由 Raycaster如GraphicRaycaster用于 UI从点击位置发射射线检测其路径上与所有启用了Raycast Target的 UI 元素的矩形区域是否发生碰撞并通过深度排序确定最终命中的最上层元素最后通过 ExecuteEvents 这个“调度中心”将事件如IPointerClickHandler派发给目标对象上实现了相应接口的组件如Button来触发响应。UGUI的运作流程如何优化呢UGUI通过为每个UI元素创建网格和材质球来渲染但为了优化性能降低drawcall它会将同层级、同材质的元素合并成一个大网格并使用同一张图集共享材质球。这种合并在UI静止时效率极高但任何元素的移动或属性改变都会触发网格的拆分与重建消耗大量CPU资源。因此优化UGUI性能的关键在于减少此类动态变化以最小化网格的拆分合并操作。什么是material和shared material有何区别首先会有一个纹理和一个着色器然后可以根据这两个东西生成一个材质球然后在场景中具体某个物体是由网格和材质组成的这里的材质就是我们去材质球处获取的材质material和shared material是两种从材质球处获取材质的方式 前者是获取一个材质实例是一个独立副本而后者是直接获取原始材质球的材质如果修改前者不会影响原材质球而后者会。如何在不同分辨率下保持UI的一致性有两种方法一种是针对UI的RectTransform另一种是Canvas Scaler中的Scale With Screen Size这两种方法都可以帮助我们在不同分辨率下实现一致的UI。Canvas上有哪些渲染模式overlay是无视所有摄像机而直接将UI绘制在屏幕最上面camera space则是会在摄像机前方绘制world space则是把UI作为世界中一个物体来处理。Graphic Raycaster是什么有何作用Graphic Raycaster 是 Unity UGUI 系统中一个处理 UI 交互的核心组件。它的主要作用是向 UI 元素发射射线检测点击、触摸等输入事件并判断这些事件是否落在了某个 UI 元素上。什么是合批什么是静态合批和动态合批有何差异合批Batching是 Unity 中的一项核心渲染优化技术其根本目的是通过减少 CPU 向 GPU 发送的绘制命令Draw Call次数来提升性能。它主要分为两种方式静态合批Static Batching 和 动态合批Dynamic Batching。静态合批在运行前如打包时进行它将场景中标记为 Static、使用相同材质的静态物体的网格数据预先合并成一个大的网格。其优点是运行时几乎没有CPU开销并能利用视锥剔除提升GPU效率但代价是会增加包体大小和内存占用因为合并后的网格需要常驻内存。而动态合批则在运行时每帧进行它自动将使用相同材质、且满足特定条件如顶点数通常不超过900个顶点属性的移动物体的顶点数据进行动态合并。其优点是不会增加包体和内存负担物体可以移动但缺点是会给CPU带来持续的计算开销且条件苛刻容易因顶点数超限、材质实例或属性不同等原因导致合批失败。简而言之两者最核心的差异在于静态合批是“以空间换时间”通过预消耗内存来避免运行时开销适用于静止物体动态合批是“以计算换效率”通过CPU的实时计算来减少Draw Call适用于小型可移动物体。在实际项目中它们常根据物体的静态属性和性能需求结合使用。如何理解UI的动静分离UGUI的渲染基于网格Mesh。一个Canvas下的所有UI元素最终会被合并成一个或几个大网格提交给GPU渲染这个过程称为合批Batching目的是减少Draw Call。然而一旦某个UI元素的属性如位置、颜色、纹理等发生变化UGUI就必须重新计算该Canvas内所有UI元素的顶点数据并重新合批网格这个过程称为网格重构Rebuild。如果动静UI混杂在同一个Canvas中一个血条的微小移动就可能触发整个界面包括复杂的静态背景的网格重构造成巨大的CPU浪费。UI动静分离是UGUI性能优化中最核心、最有效的策略之一。它的核心思想就是将频繁变化的UI元素与静态UI元素放置在不同的Canvas下以限制网格重构的范围从而降低CPU开销。如何拆分过大的UI在 Unity UGUI 中一个复杂的 UI 界面如 RPG 游戏界面如果不进行合理拆分任何一个部分的微小变化如血条更新都会导致整个 Canvas 的网格重建Rebuild造成巨大的 CPU 性能开销。将频繁变化的“动态”UI元素如血条、技能图标、计时器和永不变化的“静态”UI元素如背景、边框分离开放置到不同的 Canvas 中。这样做可以严格限制重建操作的影响范围动态UI的变化只会触发其所在Canvas的重建而不会波及到静态UI所在的Canvas。同时在实际项目中需要权衡利弊虽然拆分 Canvas 可能会轻微增加 Draw Call但因此带来的重建开销的显著降低其收益远大于代价是提升UI性能的关键实践。为什么要预加载UI如何预加载UGUI 界面实例化时涉及 Prefab 实例化、Mesh 合并、组件初始化、渲染初始化等操作CPU 开销集中。若在玩家点击打开界面的瞬间进行这些操作极易引起明显卡顿破坏游戏体验。预加载的核心思想就是“提前做功”将这部分消耗转移到玩家不敏感的时间段如游戏启动时、过场加载时。具体如何预加载对于使用Resources目录的资源可以使用异步加载在后台进行。对于更复杂的项目通常使用 AssetBundle 或 Addressables 资源管理系统进行异步加载原理类似。如果还是太慢的话在加载场景或空闲时实例化UI并完成初始化然后立即禁用SetActive(false)。需要显示时只需启用它几乎无开销。Sprite Atlas是什么有何作用Sprite Atlas 是 Unity 优化 2D 和 UI 渲染性能的利器。它通过将小纹理合并成大纹理来减少 Draw Call优化内存使用并提升渲染效率。要有效使用它你需要合理地规划和划分图集内容、根据项目类型UGUI 或 2D Sprite配置合适的打包参数、以及在运行时选择正确的加载方式如 AssetBundle 或后期绑定。Mask是什么有何作用对性能有何影响在 Unity UGUI 中Mask遮罩组件是一种利用 GPU 的模板缓冲区Stencil Buffer 来限制其子元素可见区域的工具。它的作用是只允许子元素在 Mask 定义的形状区域内显示区域外的部分会被裁剪掉常用于实现圆形头像、滚动视图或复杂 UI 的局部显示。然而Mask 会带来显著的性能开销它需要两次渲染操作先写模板缓冲再渲染子元素会增加 Draw Call。它会中断 UI 元素的合批Batching导致更多的绘制调用。频繁的模板缓冲操作会增加 GPU 负载尤其在低端设备上可能引发卡顿。因此对于矩形裁剪需求应优先使用性能更优的RectMask2D组件它基于屏幕坐标裁剪无模板缓冲开销并尽量避免多层 Mask 嵌套以减轻性能负担。为什么RectMask2D组件性能更优RectMask2D 性能更优的核心在于其简单、前置的裁剪机制。它在 CPU 端就直接对子元素的顶点数据进行计算和修改完全丢弃超出矩形范围的顶点使得传递给 GPU 渲染的数据本身就是“纯净”且可见的。这避免了像传统 Mask 那样需要将大量最终不可见的像素送入 GPU 渲染管线并在模板测试阶段才丢弃所造成的 GPU 填充率浪费和额外的 Draw Call。因此对于矩形裁剪需求RectMask2D 是一种开销更小、效率更高的解决方案。LuaProtoBuf这里专门开一个专题来介绍ProtoBufProtoBuf是什么ProtoBufProtocol Buffers是 Google 开发的一种语言无关、平台无关、可扩展的结构化数据序列化机制。它通过预定义的.proto文件描述数据结构并利用专用编译器生成不同编程语言的源代码实现高效、紧凑的数据序列化与反序列化特别适用于高性能网络通信如 gRPC和数据存储场景。ProtoBuf相比起其他序列化方法来说有哪些优点ProtoBuf比起其他协议的优点主要体现在高效的数据序列化跨语言跨平台的特性以及优秀的向后向前兼容性。具体来说ProtoBuf序列化后的数据体积更小支持多种语言和平台使用且允许删除旧字段或者添加新字段。如何实现高效的数据序列化要实现高效的数据序列化TLVTag-Length-Value结构、Varints 编码和 Zigzag 编码正是其中的核心技术。它们通过紧凑的数据组织、灵活的整数编码和高效的负数处理共同构成了像 Protocol Buffers (Protobuf) 这样的高效序列化方案的基石。采用 TLVTag-Length-Value 结构存储数据其中 Tag 包含了字段的唯一编号和数据类型信息这使得序列化时无需传输字段名极大减少了数据体积。对整数类型它使用 Varints 编码动态根据整数大小分配存储字节让小整数占用空间更少。同时为高效处理负数引入了 Zigzag 编码先将有符号整数映射为无符号整数再进行 Varints 编码避免了直接编码负数时效率低下的问题。此外Protocol Buffers 的序列化过程还会利用预编译的代码和零拷贝等技术来优化性能。其实就是我们每个字段都会包含一个Tag这个Tag中包含编号和数据类型然后可以根据数据类型来最小化字段所需要的空间体积比如一个int一般都要占据4个字节但是我们在确定具体传输的内容后比如有一个int值为1那我们只用一个字节就可以存储这个1而不是传统的存储int用4个字节。然后在这个基础之上我还想更广泛地学习一些其实比较常见但是目前还没有掌握的知识。多线程什么是自旋锁自旋锁SpinLock是一种用于保护共享资源的轻量级同步机制主要用于多线程/多处理器环境。它的核心特点是当线程尝试获取锁失败时不会进入睡眠状态而是会在一个循环中“忙等”自旋不断检查锁是否被释放直到成功获取锁。原子操作可以避免部分锁的开销吗原子操作通过利用硬件指令在用户态完成同步避免了传统锁机制中陷入内核、线程挂起和上下文切换的主要开销。因此在保护简单变量、短临界区且低竞争的场景下原子操作的性能通常远高于互斥锁。Linux相关select和epoll是什么select和epoll都是Linux系统中用于I/O多路复用的机制它们允许单个进程或线程同时监视多个文件描述符如网络连接并在这些描述符可读、可写或出现异常时通知应用程序进行处理从而高效地管理大量并发连接。它们的核心区别在于select 采用轮询方式每次调用都需要将全部监控的文件描述符从用户空间拷贝到内核空间并由内核线性遍历所有描述符时间复杂度 O(n)且存在连接数限制通常 1024而 epoll 采用事件驱动机制通过回调函数只处理活跃的描述符时间复杂度 O(1)无连接数限制且通过内核与用户空间共享内存避免了重复拷贝因此能高效处理海量并发连接但缺点是仅限 Linux 平台使用。数据库什么是关系型数据库关系型数据库是一种基于关系模型来组织和管理数据的数据库系统。它使用二维表格称为“关系”来存储数据表格由行和列组成能够清晰地表示数据之间的关系。一般关系型数据库的组成部分如下关系型数据库包含以下特性采用二维表格来结构化地存储数据通过主键、外键等约束维护数据的完整性和表之间的关联并严格遵循 ACID 原则原子性、一致性、隔离性、持久性来保证事务的可靠性同时使用标准化的SQL语言作为数据操作和查询的统一接口从而确保了数据的一致性、安全性和强大的复杂查询能力。如何理解遵循范式数据库设计中的“遵循范式”指的是在构建关系型数据库时遵守一系列预先定义的规范化设计原则。这些原则旨在通过减少数据冗余、避免数据操作异常如插入、更新和删除异常来提升数据的完整性和一致性。