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

Write-Ahead Log(PostgreSQL 14 Internals翻译版)

日志

如果发生停电、操作系统错误或数据库服务器崩溃等故障,RAM中的所有内容都将丢失;只有写入磁盘的数据才会被保留。要在故障后启动服务器,必须恢复数据一致性。如果磁盘本身已损坏,则必须通过备份恢复来解决相同的问题。

理论上,您可以始终保持磁盘上的数据一致性。但在实践中,这意味着服务器必须不断地将随机页面写入磁盘(尽管顺序写入更便宜),而且这种写入的顺序必须保证在任何特定时刻都不会损害一致性(这很难实现,特别是在处理复杂的索引结构时)。

就像大多数数据库系统一样,PostgreSQL使用了不同的方法。

当服务器运行时,一些当前数据仅在RAM中可用,将其写入永久存储器被推迟。因此,在服务器操作期间,存储在磁盘上的数据总是不一致的,因为页面永远不会一次刷新。但是在RAM中发生的每个更改(例如在缓冲区缓存中执行的页面更新)都被记录下来:PostgreSQL创建一个日志条目,其中包含在需要时重复此操作所需的所有基本信息

与页面修改相关的日志条目必须在被修改的页面本身之前写入磁盘。因此,该日志的名称为:预写日志(write-ahead log)或WAL。这个要求保证了在发生故障的情况下,PostgreSQL可以从磁盘读取WAL条目并重放它们以重复已经完成的操作,这些操作的结果仍然在RAM中,并且在崩溃之前没有将其保存到磁盘中。

保持预写日志通常比将随机页面写入磁盘更有效。WAL条目构成连续的数据流,甚至可以由HDD处理。此外,WAL条目通常小于页面大小。

在发生故障时,需要记录所有可能破坏数据一致性的操作。具体来说,在WAL中记录了以下动作:

  • 在缓冲缓存中执行的页面修改——因为写操作被延迟
  • 事务提交和回滚—因为状态更改发生在CLOG缓冲区中,而不会立即将其转移到磁盘
  • 文件操作(比如在添加或删除表时创建和删除文件和目录)——因为这些操作必须与数据更改同步

以下操作不被记录:

  • UNLOGGED表的操作
  • 对临时表的操作——因为它们的生命周期受到生成它们的会话的限制

在PostgreSQL 10之前,哈希索引也不被记录。它们的唯一目的是将哈希函数与不同的数据类型相匹配。

除了崩溃恢复之外,WAL还可以用于从备份和复制中进行时间点恢复

WAL结构

逻辑结构

谈到它的逻辑结构,我们可以将WAL描述为可变长度的日志条目流。每个条目包含一些关于特定操作的数据,前面有一个标准头。除其他事项外,头文件提供以下信息:

  • 和条目有关于的事务ID
  • 解释条目的资源管理器
  • 检测数据损坏的校验和
  • 条目长度
  • 对前一个WAL条目的引用

WAL通常是向前读取的,但是一些实用程序(如pg_rewind)可能会向后扫描它。

WAL数据本身可以具有不同的格式和含义。例如,它可以是一个页片段,它必须在指定的偏移量处替换页的某些部分。相应的资源管理器必须知道如何解释和重放特定的条目。表、各种索引类型、事务状态和其他实体都有单独的管理器。

WAL文件占用服务器共享内存中的特殊缓冲区。WAL使用的缓存大小由wal_buffers参数定义。默认情况下,自动选择该大小为总缓冲区缓存大小的1/32。

WAL缓存与缓冲区缓存非常相似,但它通常以环形缓冲区模式运行:新条目添加到其头部,而旧条目从尾部开始保存到磁盘。 如果WAL缓存太小,则执行磁盘同步的频率将超出必要的范围。

在低负载情况下,插入位置(缓冲区的头部)几乎总是与已经保存到磁盘的条目的位置(缓冲区的尾部)相同:

在这里插入图片描述
在PostgreSQL之前,所有函数名都包含XLOG缩写而不是WAL。

为了引用一个特定的条目,PostgreSQL使用一个特殊的数据类型:pg_lsn(日志序列号,LSN)。它表示从WAL开始到条目的64位字节偏移量。LSN表示为两个32位的十六进制数,中间用斜杠分隔。

创建一个表:
在这里插入图片描述
启动一个事务并记录WAL插入位置的LSN:
在这里插入图片描述
现在运行一些任意命令,例如,更新一行:
在这里插入图片描述
页面修改在RAM中的缓冲缓存中执行。此更改记录在WAL页面中,也记录在RAM中。因此,插入LSN被提前:
在这里插入图片描述
为了确保修改后的数据页严格在相应的WAL条目之后被刷新到磁盘,页头存储了与该页相关的最新WAL条目的LSN。你可以使用pageinspect查看LSN:

在这里插入图片描述
整个数据库集群只有一个WAL,并且不断向其添加新条目。由于这个原因,存储在页面中的LSN可能比前一段时间pg_current_wal_insert_lsn函数返回的LSN要小。但是如果系统中什么都没有发生,这些数字将是相同的。

现在提交事务:
在这里插入图片描述
提交操作也被记录,插入LSN再次更改:

在这里插入图片描述
提交更新CLOG页面中的事务状态,这些页面保存在它们自己的缓存中。CLOG缓存通常在共享内存中占用128页。为了确保在相应的WAL条目之前不会将CLOG页面刷新到磁盘,还必须跟踪CLOG页面的最新WAL条目的LSN。但是这些信息存储在RAM中,而不是页面本身。

在某个时刻,WAL条目将被写入磁盘;然后就可以从缓存中驱逐CLOG和数据页。如果必须更早地驱逐它们,则会发现它,并且会先将WAL条目强制写入磁盘。

如果您知道两个LSN位置,则可以通过简单地从一个位置减去另一个位置来计算它们之间的WAL条目的大小(以字节为单位)。你只需要将它们强制转换为pg_lsn类型:

在这里插入图片描述
在本例中,与UPDATE和COMMIT操作相关的WAL条目占用了大约100个字节。

您可以使用相同的方法来估计特定工作负载在单位时间内生成的WAL条目的数量。检查点设置将需要这些信息。

物理结构

在磁盘上,WAL作为单独的文件或段存储在PGDATA/pg_wal目录中。它们的大小由只读的wal_segment_size参数显示。

对于高负载系统,增加段大小是有意义的,因为它可以减少开销,但是这个设置只能在集群初始化期间修改(initdb --wal-segsize)。

WAL条目进入当前文件,直到空间用完;然后,PostgreSQL启动一个新文件。

我们可以知道一个特定的条目位于哪个文件中,以及从文件开始的偏移量:

在这里插入图片描述
文件名由两部分组成。最高的8个十六进制数字定义了用于从备份中恢复的时间线,而其余的数字表示最高的LSN位(最低的LSN位显示在file_offset字段中)。

要查看当前的WAL文件,可以调用以下函数:
在这里插入图片描述
在这里插入图片描述
现在,让我们使用pg_waldump实用程序查看新创建的WAL条目的头,该实用程序可以根据LSN范围(如本例中所示)和特定事务ID过滤WAL条目。

应该使用postgres操作系统用户启动pg_waldump实用程序,因为它需要访问磁盘上的WAL文件。

在这里插入图片描述
这里我们可以看到两个条目的标题。

第一个是由Heap资源管理器处理的HOT_UPDATE操作。blkref字段显示了更新后的堆页的文件名和页ID:

在这里插入图片描述

第二个条目是由事务资源管理器监督的COMMIT操作。

检查点

在失败后恢复数据一致性(即执行恢复),PostgreSQL必须往前重放WAL,并将代表丢失的更改的条目应用到相应的页面。为了找出丢失的内容,将存储在磁盘上的页面的LSN与WAL条目的LSN进行比较。但我们应该从什么时候开始复苏呢?如果我们开始得太晚,在此之前写入磁盘的页面将无法接收到所有更改,这将导致不可逆转的数据损坏。从头开始是不现实的:不可能存储如此庞大的潜在数据量,也不可能接受如此长的恢复时间。我们需要一个逐渐向前移动的检查点,这样可以安全地从该点开始恢复并删除所有以前的WAL条目。

创建检查点最直接的方法是定期挂起所有系统操作,并将所有脏页强制放到磁盘上。这种方法当然是不可接受的,因为系统将挂起一段不确定但相当重要的时间。

由于这个原因,检查点随着时间的推移而分散,实际上构成了一个间隔。检查点的执行是由一个叫做checkpointer的特殊后台进程执行的。

  • 检查点开始。 checkpointer进程将可以立即写入的所有内容刷新到磁盘:CLOG事务状态、子事务的元数据和一些其他结构。

  • 检查点执行。 检查点执行的大部分时间都花在将脏页刷新到磁盘上。首先,在检查点开始时已脏的所有缓冲区的标头中设置一个特殊标记。由于不涉及I/O操作,因此它发生得非常快。然后checkpointer进程遍历所有缓冲区并将标记的缓冲区写入磁盘。它们的页面不会从缓存中被驱逐:它们只是被写下来,因此使用量和pin计数可以忽略。页面按照其ID的顺序进行处理,以尽可能避免随机写入。为了更好地实现负载平衡,PostgreSQL 在不同的表空间之间进行切换 (因为它们可能位于不同的物理设备上)。后端也可以将带标签的缓冲区写入磁盘——如果它们先到达的话。在任何情况下,缓冲区标记都在此阶段被删除,因此出于检查点的目的,每个缓冲区将只被写入一次。当然,当检查点正在进行时,仍然可以在缓冲缓存中修改页面。但是由于新的脏缓冲区没有标记,checkpointer将忽略它们。

  • 检查点完成。 当检查点开始时所有脏的缓冲区都被写入磁盘时,就认为检查点完成了。从现在开始(但不是更早!),检查点的起点将被用作恢复的新起点。不再需要在此之前编写的所有WAL条目。

在这里插入图片描述
最后,checkpointer创建一个与检查点完成相对应的WAL条目,指定检查点的起始LSN。 由于检查点在启动时不记录任何日志,因此该LSN可以属于任何类型的WAL条目。

PGDATA/global/pg_control文件也得到更新,以引用最新完成的检查点。 (在此过程结束之前,pg_control将保留前一个检查点。)

在这里插入图片描述
为了一劳永逸地弄清楚哪些点在哪里,让我们看一个简单的例子。我们将使几个缓存页面变脏:
在这里插入图片描述
现在让我们手动完成检查点。所有脏页都将刷新到磁盘;由于系统中没有发生任何事情,因此不会出现新的脏页:

在这里插入图片描述
让我们看看检查点是如何反映在WAL中的:
在这里插入图片描述
最新的WAL条目与检查点完成(CHECKPOINT_ONLINE)有关。 这个检查点的起始LSN在redo后面指定;这个位置对应于检查点开始时最近插入的WAL条目。

同样的信息也可以在pg_control文件中找到:

在这里插入图片描述

恢复

在服务器启动时启动的第一个进程是postmaster。反过来,postmaster生成startup进程,在发生故障时负责数据恢复。

为了确定是否需要恢复,启动进程读取pg_control文件并检查集群状态。pg_controldata实用程序使我们能够查看这个文件的内容:

在这里插入图片描述
正常停止的服务器处于“shut down”状态;非运行服务器的“in production”状态表示故障。在这种情况下,启动进程将自动从同一个pg_control文件中找到的最近完成的检查点的起始LSN启动恢复。

如果PGDATA目录包含与备份相关的backup_label文件,则从该文件获取起始LSN位置。
启动进程从定义的位置开始逐条读取WAL条目,如果页面的LSN小于WAL条目的LSN,则将其应用于数据页。如果页面包含较大的LSN,则不应用WAL;事实上,它不能被应用,因为它的条目被设计成严格按顺序重放。

然而,一些WAL条目构成了一个完整的页面映像(FPI)。这种类型的条目可以应用于页面的任何状态,因为无论如何都会擦除所有页面内容。这样的修改称为幂等的。幂等操作的另一个例子是注册事务状态更改:每个事务状态在CLOG中由某些位定义,这些位的设置与它们以前的值无关,因此不需要在CLOG页面中保留最新更改的LSN。

WAL条目应用于缓冲缓存中的页面,就像在正常操作期间的常规页面更新一样。

以类似的方式从WAL中恢复文件:例如,如果WAL条目显示该文件必须退出,但由于某种原因丢失,则将重新创建该文件。

一旦恢复结束,所有未记录的关系将被相应的初始化分支覆盖。

最后,执行检查点以保护磁盘上恢复的状态。

启动过程的工作现在已经完成。

在经典形式中,恢复过程包括两个阶段。在前滚阶段,重放WAL条目,重复丢失的操作。在回滚阶段,服务器中止在发生故障时尚未提交的事务。
在PostgreSQL中,第二阶段是不需要的。在恢复之后,CLOG将既不包含未完成事务的提交位,也不包含未完成事务的abort位(技术上表示活动事务),但是由于可以确定事务不再运行,因此将认为它已终止。

我们可以通过强制服务器在immediate模式下停止来模拟故障:

在这里插入图片描述

当我们启动服务器时,启动进程看到发生了故障,并进入恢复模式:

在这里插入图片描述
如果服务器正常停止,postmaster断开所有客户机的连接,然后执行最后一个检查点,将所有脏页刷新到磁盘。

注意当前的WAL位置:

在这里插入图片描述
现在让我们正确地停止服务器:

在这里插入图片描述
下面是新的集群状态:

在这里插入图片描述
在WAL的末尾,我们可以看到CHECKPOINT_SHUTDOWN条目,它表示
最后一个检查点:

在这里插入图片描述
最新的pg_waldump消息表明该实用程序已将WAL读取到最后。

让我们重新启动实例:

在这里插入图片描述

后台写

如果后端需要从缓冲区中驱逐脏页,则必须将该页写入磁盘。这种情况是不希望出现的,因为它会导致等待——在后台异步执行写入要好得多。

这个任务部分由checkpointer来完成,但是这还不够。

因此,PostgreSQL提供了另一个名为bgwriter的进程,专门用于后台写操作。它依赖于与驱逐相同的缓冲区搜索算法,除了两个主要区别:

  • bgwriter进程使用自己的时钟指针,它从不落后于驱逐进程,而且通常会超过驱逐进程。
  • 在遍历缓冲区时,使用计数不会减少。

如果缓冲区没有固定且使用计数为零,则将脏页刷新到磁盘。因此,bgwriter在驱逐之前运行,并主动将那些极有可能很快被驱逐的页面写入磁盘。

它提高了被选中用于驱逐的缓冲区是干净的可能性。

相关文章:

Write-Ahead Log(PostgreSQL 14 Internals翻译版)

日志 如果发生停电、操作系统错误或数据库服务器崩溃等故障,RAM中的所有内容都将丢失;只有写入磁盘的数据才会被保留。要在故障后启动服务器,必须恢复数据一致性。如果磁盘本身已损坏,则必须通过备份恢复来解决相同的问题。 理论…...

CUDA 学习记录

1.关于volatile: 对于文章中这个函数, __global__ void reduceUnrollWarps8 (int *g_idata, int *g_odata, unsigned int n) {// set thread IDunsigned int tid threadIdx.x;unsigned int idx blockIdx.x * blockDim.x * 8 threadIdx.x;// convert…...

【Java 进阶篇】深入了解 Bootstrap 按钮和图标

按钮和图标在网页设计中扮演着重要的角色,它们是用户与网站或应用程序交互的关键元素之一。Bootstrap 是一个流行的前端框架,提供了丰富的按钮样式和图标库,使开发者能够轻松创建吸引人的界面。在本文中,我们将深入探讨 Bootstrap…...

基于Java的人事管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding) 代码参考数据库参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...

代码随想录算法训练营第五十九天| 647. 回文子串 516.最长回文子序列

今日学习的文章链接和视频链接 回文子串 https://programmercarl.com/0647.%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2.html 516.最长回文子序列 https://programmercarl.com/0516.%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E5%BA%8F%E5%88%97.html 动态规划总结篇 https:…...

uniapp 小程序优惠劵样式

先看效果图 上代码 <view class"coupon"><view class"tickets" v-for"(item,index) in 10" :key"item"><view class"l-tickets"><view class"name">10元优惠劵</view><view cl…...

元梦之星内测上线,如何在B站打响声量?

元梦之星是腾讯天美工作室群研发的超开星乐园派对手游&#xff0c;于2023年1月17日通过审批。该游戏风格可爱软萌&#xff0c;带有社交属性&#xff0c;又是一款开黑聚会的手游&#xff0c;备受年轻人关注。 飞瓜数据&#xff08;B站版&#xff09;显示&#xff0c;元梦之星在…...

Python---循环---while循环

Python中的循环 包括 while循环与for循环&#xff0c;本文以while循环为主。 Python中所有的知识点&#xff0c;都是为了解决某个问题诞生的&#xff0c;就好比中文的汉字&#xff0c;每个汉字都是为了解决某种意思表达而诞生的。 1、什么是循环 现实生活中&#xff0c;也有…...

面试知识点--基础篇

文章目录 前言一、排序1. 冒泡排序2. 选择排序3. 插入排序4. 快速单边循环排序5. 快速双边循环排序6. 二分查找 二、集合1.List2.Map 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、排序 1. 冒泡排序 冒泡排序就是把小的元素往前调或者把大…...

FIFO设计16*8,verilog,源码和视频

名称&#xff1a;FIFO设计16*8&#xff0c;数据显示在数码管 软件&#xff1a;Quartus 语言&#xff1a;Verilog 代码功能&#xff1a; 使用verilog语言设计一个16*8的FIFO&#xff0c;深度16&#xff0c;宽度为8。可对FIFO进行写和读&#xff0c;并将FIFO读出的数据显示到…...

#力扣:2769. 找出最大的可达成数字@FDDLC

2769. 找出最大的可达成数字 - 力扣&#xff08;LeetCode&#xff09; 一、Java class Solution {public int theMaximumAchievableX(int num, int t) {return num 2*t;} }...

Juniper防火墙SSG-140 session 过高问题

1.SSG-140性能参数 2.问题截图 3.解决方法 &#xff08;1&#xff09;通过telnet 或 consol的方法登录到防火墙&#xff1b; &#xff08;2&#xff09;使用get session 查看总的session会话数&#xff0c;如果大于300 一般属于不正常情况 &#xff08;3&#xff09;使用get…...

Spring Boot 3.2四个新特点提升运行性能

随着 Spring Framework 6.1 和 Spring Boot 3.2 普遍可用性的临近&#xff0c;我们想分享一下 Spring 团队为让开发人员优化其应用程序的运行时效率而做出的几项努力的概述。 我们将介绍以下技术和用例&#xff1a; Spring MVC 将使用 基于JDK 21 虚拟线程 Web 堆栈使用 Spri…...

一阶系统阶跃响应实现规划方波目标值

一阶系统单位阶跃响应 一阶系统传递函数&#xff0c;实质是一阶惯性环节&#xff0c;T为一阶系统时间常数。 输入信号为单位阶跃函数&#xff0c;数学表达式 单位阶跃函数拉氏变换 输出一阶系统单位阶跃响应 拉普拉斯反变换 使用前向差分法对一阶系统离散化 将z变换写成差分方…...

项目经理如何去拆分复杂项目?

代码的横向分层&#xff0c;维度是根据复杂度来的&#xff0c;可保证代码便于开发和维护 1、因为强类型的原因&#xff0c;把变动大的分到数据库来解决&#xff0c;这是一种后端分离。 2、因为发布难的原因&#xff0c;所以用稳定的引擎来解决问题&#xff0c;然后用数据库配置…...

python二次开发Solidworks:修改实体尺寸

立方体原始尺寸&#xff1a;100mm100mm100mm 修改后尺寸&#xff1a;10mm100mm100mm import win32com.client as win32 import pythoncomdef bin_width(width):myDimension Part.Parameter("D1草图1")myDimension.SystemValue width def bin_length(length):myDime…...

【C++】:类和对象(中)之类的默认成员函数——构造函数and析构函数

1.类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器会生成…...

sqlserver系统存储过程添加用户学习

sqlserver有一个系统存储过程sp_adduser&#xff1b;从名字看是添加用户的&#xff1b;操作一下&#xff0c; 从错误提示看还需要先添加一个登录名&#xff0c;再执行一个系统过程sp_addlogin看一下&#xff0c; 执行完之后看一下&#xff0c;安全性-登录名下面有了rabbit&…...

Monocle 3 | 太牛了!单细胞必学R包!~(一)(预处理与降维聚类)

1写在前面 忙碌的一周结束了&#xff0c;终于迎来周末了。&#x1fae0; 这周的手术真的是做到崩溃&#xff0c;2天的手术都过点了。&#x1fae0; 真的希望有时间静下来思考一下。&#x1fae0; 最近的教程可能会陆续写一下Monocle 3&#xff0c;炙手可热啊&#xff0c;欢迎大…...

基于VCO的OTA稳定性分析的零交叉时差模型研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...