OceanBase v4.3特性解析:新功能“租户克隆”的场景与应用指南

 

熟悉或曾用过OceanBase的朋友,对于“多租户”这一理念定不陌生。OceanBase的租户概念,与我们熟知的传统数据库实例颇为相似。举例来说,OceanBase的租户支持MySQL兼容模式,对于用户而言,选用一个MySQL兼容模式的租户,就如同用一个MySQL数据库实例。

OceanBase 4.3 版本在已有的多租户架构基础上,新增了功能——租户克隆,本文将向大家介绍租户克隆的应用场景和使用方法。

租户克隆功能是什么?

当我们需要使用 OceanBase 数据库时,会在一台或多台机器上拉起 OceanBase 进程,这些进程会组成 OceanBase 集群。在这个集群中,我们可以创建出需要的租户。单个 OceanBase 集群中可以创建出多个租户,租户间互相隔离。

如下图所示,在 OceanBase 的系统租户下执行该克隆语句,就可以对指定的源租户快速克隆出一个新租户。新克隆租户在初始时刻包含的数据,就是源租户在语句执行时刻的一个快照。

1715306009

租户克隆操作可以快速把源租户的元数据信息,拷贝一份,给到新克隆租户来使用。初始时刻,新克隆租户访问的是和源租户完全相同的物理宏块。宏块,就是上图中画的 Macro Block,是 OceanBase 中基础的数据存储单元。

因为克隆过程只拷贝了元数据,而不是拷贝数据,克隆操作可以快速完成。

该新克隆租户与源租户是两个独立的租户,他们之间保持了 OceanBase 一直以来的租户隔离特性,存在严格的数据隔离和资源隔离。新克隆租户的任何数据改动,都不会影响到源租户;源租户的数据改动,也不会影响到新克隆租户。新克隆租户与源租户具有自己独占的 CPU、内存、IOPS 资源,不相互抢占。

租户克隆功能可以做什么?

如果大家在使用OceanBase的过程中遇到以下六种场景,租户克隆功能就会发挥它的价值。

1715306018

场景一:报表任务。

业务正在大促,卖得非常好,数据库运行平稳,但已处于满载状态。老板非常高兴,走到值班室,问技术团队要一个当前整体销售情况的报表。这时候,是选择冒着被“广进”的风险跑这个报表任务呢?还是选择对满脸期待的老板进行下预期管理呢?如果最终决定运行这个报表任务,当前这种情况下,又该开多大的并发度来跑大查询呢?

这时就可以利用租户克隆技术,快速基于生产租户克隆出一个新的报表租户,用新克隆出来的报表租户执行在线数据分析任务。

从 OceanBase 4.3 版本开始,支持行列混合存储,如果源租户是行列混存结构,克隆得到的新租户也是行列混存结构,可以良好支持 AP 负载。

场景二:快弹只读备租户。

生产租户压力很大,同时内部运营系统,还会发送不那么确定的复杂查询负载。我们希望将这些来自内部的复杂查询负载,从生产租户上快速移走。

这时就可以利用租户克隆技术,快速基于生产租户克隆出一个新租户,让这个新租户作为生产租户的备租户。新租户会主动去生产租户上拉取日志,提供一个准实时的弱读服务。

如此一来,我们就可以将内部运营系统的复杂查询负载,快速转移到这个新的只读备租户中。

场景三:应用版本发布。

不少应用的版本发布流程,都是先备份数据库,再更新应用版本。若这次应用版本发布出现问题,就用发布前的那个数据库备份,来还原数据库,最终实现整体版本发布的回退。备份数据库及用备份来恢复数据库,在数据量增大后都变得特别慢,导致整个版本发布过程执行时间特别长。

为了最小化版本发布对生产经营的影响,版本发布大多选择在凌晨进行,同时为了应对各类突发情况,整个版本发布过程时常需要相关同学现场值守。发版时间太长,一方面对业务生产经营产生影响,另一方面会让参与同学倍感疲倦。

租户克隆技术就可以在一定程度上解决这个问题。首先在版本正式发布前,可以基于生产租户克隆出一个预发测试租户,用这个预发测试租户来做最后的版本预发验证。版本发布的第一个流程,利用租户克隆技术,对生产租户克隆出一个小规格的临时备份租户。如果版本发布出现问题,就可以用这个临时备份租户,克隆出新的生产租户,来将数据库还原到版本发布之前。

因为租户克隆过程执行得很快,可以大幅度减少版本发布需要的执行时间。

场景四:数据库变更。

这是 DBA 同学经常遇到的场景。比如研发同学找到你帮忙调整一个索引,并且告诉你这个变更在测试环境已经验证了很久了,绝对没问题。结果你一执行完索引变更,数据库 CPU 直接就飙到100%,同时你发现,如果要将之前的索引重新构建出来,至少要6个小时。

这时,租户克隆机制就可以帮你解决这个问题,具体做法和应用版本发布的操作类似。

场景五:快速构建开发测试环境。

当前我们的代码已经可以通过像 git 这样的工具被分支化管理,比如:你要做一个 bugfix,你可以快速拉出一个 bugfix 分支并在分支上编写代码。但是整个研发过程,不光是编码,还有测试验证。测试验证时常需要基于主干测试库,构建出一个分支测试库。主干测试库可能很大,进而构建这个分支测试库耗时很长,最终导致编码 5 分钟,搭建测试环境 5 个小时的情况。

租户克隆技术就可以解决这个问题,快速从主干测试租户中克隆一个分支测试租户,以支持分支的测试验证。

场景六:数据导出。

部分时候,我们需要将数据库中大量的表数据按 csv 格式,导出到对象存储中,以供下游的大数据分析系统使用。由于担心影响线上生产服务,不敢开太大的导出并发,导致导出速度缓慢。

这时可以利用克隆功能,克隆出一个新的导出租户,用这个新的导出租户来执行数据导出任务,尽可能地减少对生产租户的影响。

租户克隆如何实现?

租户克隆的过程大致分为两个步骤。第一个步骤是对源租户创建一个 crash-consistent snapshot;第二个步骤是基于 crash-consistent snapshot 拉起新克隆租户。可以看到,整体的克隆流程都是围绕着这个 snapshot 进行的。

下面依次介绍两个步骤的执行。

如何在一个 shared-nothing 的分布式数据库中,创建 crash-consistent snapshot?如下图所示,在分布式数据库基础模型中,单个数据库具有多个完全对等的节点。每个节点都有自己的内存缓存,持久化的日志文件及持久化的数据文件。正常更新的时候,数据修改都是在内存中进行,并在必要时,将日志持久化到日志文件中。后台会有定时的checkpoint 任务,将内存中修改过的数据,刷写到数据文件中,同时记录下宕机重启时,回放日志的起始位点,即 checkpoint 点。多个节点间,通常会有一个高可靠高可用的全局时间戳服务,用来保证分布式数据库整体的外部一致性。

1715306034

基于分布式数据库中已有的这些组件和机制,快照创建的过程,大致分为如下三个步骤:

第一步,记录下各个节点当前的 checkpoint 位点。

第二步,备份各个节点的数据文件的元数据,通过元数据,找到需要的数据。

第三步,通过全局时间戳服务,确定最终的一致性快照位点。

上述步骤执行完成后,各个节点保留下来的数据文件,加上各个节点从 checkpoint 位点到 snapshot 位点的日志文件,就构成了一个 crash-consistent snapshot。后续我们就可以基于这个 crash-consistent snapshot,拉起新的克隆租户。

新克隆租户的各个节点,会通过之前保留下来的数据文件,获得到初始数据,同时每个节点会继续回放从 checkpoint 位点到 snapshot 位点的日志,这样所有节点在克隆执行完成之后,都会将自身的状态准确的更新到 snapshot 位点。

整个租户克隆的步骤都是非阻塞的,不会阻塞源租户正常的SQL 执行。

下图描述了租户克隆在 OceanBase 中的具体实现。

1715306043

日志部分:我们利用了 OceanBase 已有的归档日志机制,来实现克隆过程中日志数据的保留和管理。新克隆租户的各个节点拉起之后,会访问源租户的归档日志,读取并回放从 checkpoint 位点到 snapshot 位点的日志数据,从而将各个节点的状态统一更新到 snapshot 位点。

数据部分:从 OceanBase 4.0 版本开始,数据以 Tablet 的形式来组织,Tablet 会引用自己需要的物理宏块,数据最终存储在各个物理宏块中。克隆过程会拷贝源租户各个 Tablet 的元数据,生成新克隆租户的元数据,并且递增各个关联到的物理宏块的引用计数。

一致性快照位点:通过 OceanBase 的全局时间戳服务 GTS,来生成一致性快照位点。

租户克隆操作方法

  • 前置准备。

租户克隆功能依赖于日志归档,需要对源租户开启日志归档,才可以对该租户执行租户克隆。并且在租户克隆语句的执行期间,不可以关闭源租户的日志归档。

  • 租户克隆语句。
CREATE TENANT new_tenant_name FROM source_tenant_name WITHRESOURCE_POOL [=] resource_pool_name,UNIT [=] unit_config

new_tenant_name:新克隆租户名;

source_tenant_name:源租户名;

resource_pool_name:表示资源池的名称,租户克隆时将会自动根据源租户的资源分布,为新租户创建出资源池;

unit_config:新克隆租户 resource pool 的 unit 规格;

可以在系统租户下执行该语句,为指定租户创建出一个新的克隆租户。这个新克隆租户的 unit 数量,以及 unit 在各个 observer 上的分布,均与源租户保持一致。可以在语句中指定新克隆租户单个 unit 的规格。

-- 基于 mysql 租户当前状态创建克隆租户 clone_tenant
-- 该语句自动创建出的 resource pool 名为 clone_tenant_pool,其各个 UNIT 规格为 S1_unit_config
CREATE TENANT clone_tenant FROM mysqlWITHRESOURCE_POOL = clone_tenant_pool,UNIT= S1_unit_config;
  • 租户克隆速度。
源租户新克隆租户克隆租户操作执行时间
8核64G,单日志流包含 50w tablet,数据量 1TB。8核64G2min
  • 新克隆租户使用。

租租户克隆任务执行完成后,得到的新克隆出的租户为只读租户。

既可以将该新克隆租户继续作为只读租户提供相关服务,又或者可以将该新克隆租户设置为源租户的备租户,亦或者可以通过 ALTER SYSTEM ACTIVATE STANDBY TENANT new_tenant_name,将新克隆租户转为可读可写的主租户提供服务。

如上文中提到的,该新克隆租户与源租户之间存在严格的数据隔离和资源隔离。

  • 租户克隆任务。

当在系统租户下执行租户克隆语句时,该语句会生成对应的克隆任务,clone job。可以通过 DBA_OB_CLONE_PROGRESS 和 DBA_OB_CLONE_HISTORY 视图查看克隆任务的执行状态;PROGRESS 表中包含当前正在执行的 clone job;HISTORY 表中包含执行完成的 clone job。

相关字段:

列名类型含义
CLONE_JOB_IDbigint(20)主键,克隆任务 id
TRACE_IDvarchar(64)克隆任务 Trace Id
SOURCE_TENANT_IDbigint(20)源租户 id
SOURCE_TENANT_NAMEvarchar(128)源租户名称
CLONE_TENANT_IDbigint(20)克隆租户 id
CLONE_TENANT_NAMEvarchar(128)克隆租户名称
TENANT_SNAPSHOT_IDbigint(20)快照 id
TENANT_SNAPSHOT_NAMEvarchar(128)快照名
RESOURCE_POOL_IDbigint(20)克隆租户资源池 id
RESOURCE_POOL_NAMEvarchar(128)克隆租户资源池名
UNIT_CONFIG_NAMEvarchar(128)克隆租户 unit 配置名
RESTORE_SCNbigint(20) unsigned克隆租户的目标回放位点
STATUSvarchar(64)克隆任务的当前状态
CLONE_JOB_TYPEvarchar(16)克隆任务的当前状态RESTORE - 基于租户历史快照克隆租户FORK - 基于租户当前状态克隆租户
CLONE_START_TIMEtimestamp(6)克隆任务开始时间
CLONE_FINISHED_TIMEtimestamp(6)克隆任务结束时间
RET_CODEbigint(20)克隆任务返回值
ERROR_MESSAGEvarchar(512)克隆任务失败时的提示信息,允许为空

示例:

MySQL [(none)]> select * from oceanbase.DBA_OB_CLONE_PROGRESS\G
*************************** 1. row ***************************CLONE_JOB_ID: 1702211800546509768TRACE_ID: YA4740B7C050F-00060C210F4A4848-0-0SOURCE_TENANT_ID: 1004SOURCE_TENANT_NAME: mysqlCLONE_TENANT_ID: 1016CLONE_TENANT_NAME: clone_tntTENANT_SNAPSHOT_ID: 1702211800802135214
TENANT_SNAPSHOT_NAME: _inner_snapshot$1702211800702058206RESOURCE_POOL_ID: 1008RESOURCE_POOL_NAME: clone_tnt_poolUNIT_CONFIG_NAME: box8RESTORE_SCN: 1702211802014048020STATUS: CLONE_SYS_CREATE_TENANTCLONE_JOB_TYPE: FORKCLONE_START_TIME: 2023-12-10 20:36:40.551169CLONE_FINISHED_TIME: NULLRET_CODE: NULLERROR_MESSAGE: NULL
1 row in set (0.00 sec)MySQL [(none)]> select * from oceanbase.DBA_OB_CLONE_HISTORY where CLONE_JOB_ID = 1702211800546509768\G
*************************** 1. row ***************************CLONE_JOB_ID: 1702211800546509768TRACE_ID: YA4740B7C050F-00060C210F4A4848-0-0SOURCE_TENANT_ID: 1004SOURCE_TENANT_NAME: mysqlCLONE_TENANT_ID: 1016CLONE_TENANT_NAME: clone_tntTENANT_SNAPSHOT_ID: 1702211800802135214
TENANT_SNAPSHOT_NAME: _inner_snapshot$1702211800702058206RESOURCE_POOL_ID: 1008RESOURCE_POOL_NAME: clone_tnt_poolUNIT_CONFIG_NAME: box8RESTORE_SCN: 1702211802014048020STATUS: CLONE_SYS_SUCCESSCLONE_JOB_TYPE: FORKCLONE_START_TIME: 2023-12-10 20:36:40.551169CLONE_FINISHED_TIME: 2023-12-10 20:37:53.919247RET_CODE: 0ERROR_MESSAGE: NULL
1 row in set (0.01 sec)
  • 取消租户克隆任务。

可以通过执行 ALTER SYSTEM CANCEL CLONE new_tenant_name 语句,以取消一个正在执行的克隆任务;该语句会显式的将对应的克隆任务切换到 CANCELING 的状态(执行完成后置为CANCELED状态),同时当该克隆任务被取消之后,其申请的各类资源都会被自动释放。

若在该 ALTER SYSTEM CANCEL CLONE new_tenant_name 语句执行之前,新克隆租户已经实际执行完成了克隆操作,切换到了正常服务状态,则该 CANCEL 语句不会被执行。

示例:

MySQL [(none)]> alter system cancel clone clone_tnt;
Query OK, 0 rows affected (0.02 sec)MySQL [oceanbase]> create tenant clone_tnt from mysql with resource_pool="clone_tnt_pool",unit=box8;
ERROR 12004 (HY000): clone job has been canceledMySQL [(none)]> select * from oceanbase.DBA_OB_CLONE_HISTORY where CLONE_JOB_ID=1702212525544202404\G
*************************** 1. row ***************************CLONE_JOB_ID: 1702212525544202404TRACE_ID: YA4740B7C050F-00060C210F4A4859-0-0SOURCE_TENANT_ID: 1004SOURCE_TENANT_NAME: mysqlCLONE_TENANT_ID: 0CLONE_TENANT_NAME: clone_tntTENANT_SNAPSHOT_ID: 1702212525756306747
TENANT_SNAPSHOT_NAME: _inner_snapshot$1702212525656187169RESOURCE_POOL_ID: 1009RESOURCE_POOL_NAME: clone_tnt_poolUNIT_CONFIG_NAME: box8RESTORE_SCN: 18446744073709551615STATUS: CLONE_SYS_CANCELEDCLONE_JOB_TYPE: FORKCLONE_START_TIME: 2023-12-10 20:48:45.544031CLONE_FINISHED_TIME: 2023-12-10 20:48:51.070501RET_CODE: -4072ERROR_MESSAGE: clone job has been canceled
1 row in set (0.00 sec)

执行完“取消克隆租户”命令后,该 clone_tenant 对应的克隆任务切换到 CLONE_SYS_CANCELED 的状态,该克隆任务申请的各类资源(比如:租户快照、资源池以及处于克隆状态的租户)都会被自动释放。

  • 注意事项。

源租户和新克隆租户,在初始状态下是共享物理宏块,后续随着源租户的写入,新克隆租户的写入,以及二者的迁移调度和故障恢复等事件的发生,会使得两个租户共享的宏块逐步减少,独占宏块逐步增多,存储空间使用量逐步增加。

租户克隆特点

租户克隆功能加速了任务执行效率的同时降低了任务带来的风险,总的来说,该功能具有以下4个特点。

特点1:快。租户克隆过程中,只拷贝元数据信息,不拷贝数据信息,使得租户克隆操作可以快速完成。在我们的测试环境中,8核64G 的源租户,包含 50w tablet,1TB 数据,克隆出一个 8c64g 的新租户,可以在 2min 内完成。

特点2:易。只需要在系统租户中,执行一条租户克隆语句,就可以完成租户克隆操作。同时 OceanBase 的租户克隆是完全实现在数据库内核中,不需要外部存储系统的支持,只需要在平时用的机器上,拉起OceanBase 4.3 版本的进程,就可以用上租户克隆功能。

特点3:稳。租户克隆过程,不会阻塞源租户正常的 SQL 执行。克隆出的新租户和源租户是两个完全独立的租户,存在严格的数据隔离和资源隔离,不会互相影响。

特点4:省。新克隆租户会尽可能的和源租户共享宏块,最大限度的节省存储资源。

更多关于租户克隆的信息可参见官网文档 ,

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

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

相关文章

589.N叉树的前序遍历

刷算法题: 第一遍:1.看5分钟,没思路看题解 2.通过题解改进自己的解法,并且要写每行的注释以及自己的思路。 3.思考自己做到了题解的哪一步,下次怎么才能做对(总结方法) 4.整理到自己的自媒体平台。 5.再刷重复的类…

达梦sql中参数个数太多导致出现SOH等特殊字符报错无效的序列号是不是达梦的bug

mybatis的Mapper.xml中如下: in中的参数大概有1万6千多个,分成每1000个一组拼接成sql,然而在达梦中执行时报如下: Caused by: dm.jdbc.driver.DMException: Invalid sequence noat dm.jdbc.driver.DBError.throwException(DBError.java:710)…

【风变】Python爬虫精进复习-20240430

参考笔记 下面给出一个巨佬学习风变pyhton基础语法和爬虫精进的笔记(链接) 风变编程笔记(一)-Python基础语法 风变编程笔记(二)-Python爬虫精进 技术总结 request BeautifulSoup selenium BeautifulSoup 练习0-1:文章下载 import requ…

舜山木业有限公司现已加入2024长三角快递物流供应链与技术装备展览会

参展企业介绍 绍兴舜山木业有限公司是中华人民共和国出境木质包装定点企业、浙江省林业重点龙头企业。2011年起全面导入和开发应用符合木包装企业生产特点的ERP管理系统,顺利通过国家三级安全生产标准化验收,取得欧标托盘在中国大陆区的生产商执照资格。…

九、e2studio VS STM32CubeIDE之const修饰BSP函数的形参

目录 一、概述/目的 二、通过串口发送函数对比 2.1 stm32 hal库 VS renesas FSP 2.2 const修改函数形参的作用 2.2.1 值传递-副本 2.2.2 指针传递(就近原则) 2.2.2.1 const修饰:*P 2.2.2.2 const修饰:指针变量P 2.2.2.3 …

手撸XXL-JOB(二)——定时任务管理

在上一节中,我们介绍了SpringBoot中关于定时任务的执行方式,以及ScheduledExecutorService接口提供的定时任务执行方法。假设我们现在要写类似XXL-JOB这样的任务调度平台,那么,对于任务的管理,是尤为重要的。接下来我们…

最新Linux Debian12安装和使用ImageMagick图像处理工具 常见图片png、jpg格式转webp格式

在Linux系统中,使用ImageMagick可以图片格式转换,其中最常用的是通过命令行工具进行。 ImageMagick是一个非常强大的图像处理工具集,它包含了许多用于图像转换的命令。 一、安装ImageMagick(如果尚未安装)&#xff1…

在线音乐系统

文章目录 在线音乐系统一、项目演示二、项目介绍三、部分功能截图四、部分代码展示五、底部获取项目(9.9¥带走) 在线音乐系统 一、项目演示 音乐网站 二、项目介绍 基于springbootvue的前后端分离在线音乐系统 登录角色 : 用户、管理员 用…

Git 的原理与使用(中)

Git 的原理与使用(上)中介绍了Git初识,Git的安装与初始化以及工作区、暂存区、版本库相关的概念与操作,本文接着上篇的内容,继续深入介绍Git在的分支管理与远程操作方面的应用。 目录 五、分支管理 1.理解分支 2.创…

java约拍摄影小程序

获取源码配套资料论文等、问题解答,可以加华神扣扣:3753599439 扣扣:1590404240 叩叩:1306749621

数据结构与算法学习笔记十---链队列的表示和实现(C语言)

目录 前言 1.什么是链队 2.链队的表示和实现 1.定义 2.初始化 3.销毁 4.清空 5.空队列 6.队列长度 7.获取队头 8.入队 9.出队 10.遍历队列 11.完整代码 前言 本篇博客介绍链栈队列的表示和实现。 1.什么是链队 链队是采用链式存储结构实现的队列。通常链队使用单…

【知识拓展】大白话说清楚:IP地址、子网掩码、网关、DNS等

前言 工作中常听别人说的本地网络是什么意思?同一网段又是什么意思?它俩有关系吗? 在工作中内经常会遇到相关的网络问题,涉及网络通信中一些常见的词汇,如IP地址、子网掩码、网关和DNS等。具体一点:经常会…

申请免费的必应搜索API

申请免费的必应搜索API 文章目录 申请免费的必应搜索API前言一、原理1.1 登录1.2 进入1.3 获取密钥1.4 申请VISA信用卡1.5 创建必应自定义搜索资源 二、创建成功 前言 准备条件: 1、outlook邮箱 2、招商银行全币种VISA信用卡【建议之前就有一张招商银行信用卡&…

【opencv】图像拼接实验

实验环境:anaconda、jupyter notebook 实验用到的包:opencv、matplotlib、numpy 注:opencv在3.4.2之后sift就不是免费的了 我用的是3.4.1.15版本 实验使用到的图片 一、sift函数获取特征值 读入图片 book cv2.imread(book.png, cv2.IMRE…

苹果macOS无法给App麦克风授权解决办法

好久没有在电脑上录制课程了,有些东西还是录下来记忆深刻,却意外发现MAC系统升级后无法授权给第三方的App使用摄像头和麦克风,而录屏软件是需要开启麦克风和摄像头才能录制屏幕上的操作和声音,官方提示在第三方APP若有使用摄像头和…

pyqt QComboBox下拉列表框控件

pyqt QComboBox下拉列表框控件 QComboBox效果代码 QComboBox QComboBox 是 PyQt(中的一个控件,它允许用户从下拉列表中选择一个选项。这个控件在需要用户从预定义选项中进行选择时非常有用。 效果 代码 import sys from PyQt5.QtWidgets import QAppl…

vite创建的项目使用rem适配

下面以创建vue3.0 项目为例: npm init vitelatest “名称” 选择vue (选择你所对应的语言) 更具提示步骤执行 cd xxx npm i npm run dev 然后再项目中使用 rem 需要安装插件 第一步安装插件 npm i amfe-flexible npm i postcss-pxtorem 第二…

CS144 Checkpoint 4: interoperating in the world(2024)

分析网络路径和性能: mtr命令 mtr 输出的详细分析: mtr 162.105.253.58 命令用于结合 traceroute 和 ping 的功能,实时监测并分析从你的计算机到目标主机(IP 地址 162.105.253.58,北京大学计算中心)之间…

Nginx配置Referer防盗链

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 HTTP Referer是Hea…

PBOOTCMS|URL静态制作教程(已解答)

0、先解压源码文件,在覆盖静态文件,全部点是。 打开程序后台登录地址www.xxx.com(你的域名)/admin.php/Menu/index 打开程序后台--系统菜单--菜单新增(清理缓存后重新登录账号) (选择父菜单,菜单名称&#…