diff --git a/docs/user_manual/user_best_practices/FAQ/_index.md b/docs/user_manual/user_best_practices/FAQ/_index.md deleted file mode 100644 index d90531674..000000000 --- a/docs/user_manual/user_best_practices/FAQ/_index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: FAQ -weight: 8 ---- diff --git a/docs/user_manual/user_best_practices/FAQ/all_faq.md b/docs/user_manual/user_best_practices/FAQ/all_faq.md deleted file mode 100644 index 5ad2743fc..000000000 --- a/docs/user_manual/user_best_practices/FAQ/all_faq.md +++ /dev/null @@ -1,4268 +0,0 @@ ---- -title: 生态组件FAQ大全 -weight: 1 ---- - -# **FAQ 汇总** - -## **格式要求:** - -### **问答类** - -不涉及具体方案步骤和流程性的问题可以参考如下格式。 - -> 问:(不涉及具体方案和流程性问题) -> 答:(判断、对错、简要知识点) - -### **操作类** - -一些问题排查,涉及到问题现象、原因分析、解决方案等内容的 FAQ 可以参考如下格式。 - -> **问题现象** -> -> 简单描述遇到的问题的操作、所用软件的版本、报错关键日志、错误码、抛出的异常等信息,方便参考及复现。 -> -> **可能原因** -> -> 描述可能导致该问题的原因。 -> -> **解决方案** -> -> 描述解决该问题的方法或临时规避手段。 - -问题之间使用 `---` 进行分割。 - -## **问答** - -### **知识点** - -> 问:社区版 OceanBase 数据库支持 IPv6 网络吗? -> 答:当前版本暂不支持,预计 OceanBase 数据库 4.2 版本支持。 - -> 问:OceanBase 数据库 4.x 版本支持哪几种备份介质? -> 答:目前已支持 NFS、阿里云 OSS、腾讯云 COS。 - -> 问:OceanBase 数据库 4.0 版本触发器、存储过程、自定义函数最大支持长度是多少? -> 答:触发器最大支持 65536 字节,存储和自定义函数 clob 类型存储,基本可理解为无上限。 - -> 问:OceanBase 数据库的主键是否是聚簇索引? -> 答:是的。 - -> 问:OceanBase 数据库 4.x 版本如何进行 rs 切主? -> 答:可执行 `ALTER SYSTEM SWITCH REPLICA leader LS=1 SERVER='目标ip:rpc_port' TENANT ='sys';` 命令进行切主,示例如下。 -> -> ```sql -> `ALTER SYSTEM SWITCH REPLICA leader LS=1 SERVER ='xx.xx.xx.xx:2882' TENANT ='sys';` -> ``` - -> 问:OceanBase 数据库是否能只进行数据备份,不做日志备份呢? -> 答:进行数据备份前提是开启日志备份,不支持只做数据备份。 - -> 问:为什么刚部署的 OceanBase 数据库,数据和日志磁盘占用 90%,或者占用很大? -> 答:OceanBase 数据库的数据和日志目录采用的是预占用机制,只能动态增加占用大小,暂不支持缩小。不过 OceanBase 数据库 4.1 版本支持 log_disk_size 动态扩缩。OceanBase 数据库 4.x 版本磁盘预占用相关参数为: -> 数据磁盘:datafile_size / datafile_disk_percentage -> 日志磁盘:log_disk_size / log_disk_percentage - -> 问:下划线开头格式的参数为隐藏参数,无法通过 SHOW PARAMETERS 语句来查询? -> 答:可通过如下命令查看。 -> -> ```sql -> `SELECT * FROM oceanbase.__all_virtual_sys_parameter_stat WHERE name='_ob_enable_prepared_statement';` -> ``` - -> 问:如何清理租户并释放空间? -> 答:先执行 `drop tenant xxx force;` 命令删除租户,再执行 `drop resource pool xxx;` 命令删除对应的资源池。 -> 两条命令均成功执行才算释放空间。但需注意该操作会清理租户下的所有数据。 - -> 问:MySQL 迁移到 OceanBase 数据库还需要用 ShardingSphere 做分库分表吗? -> 答:不需要,OceanBase 数据库本身支持分区表,可以通过分区表设计业务。 - -> 问:主机资源够的情况下,多个 Server 可以部署在同一台主机吗? -> 答:手动部署和使用 OBD 部署均支持该场景,使用 OCP 部署不支持该场景。如果使用 OBD 工具部署需使用 `obd cluster deploy` 命令修改端口和安装目录。但生产环境不建议如此部署,单服务器单 observer 进程最佳。 - -> 问:使用 OBD 部署社区版三节点集群,必须使用 admin 用户吗? -> 答:非强制要求,但是建议使用 admin 用户部署,后期 OCP 接管的集群需要是使用 admin 用户部署的 OceanBase 数据库。 - -> 问:使用 OceanBase 数据库社区版 3.x 部署一个读写分离的集群时,可以部署 4 个 Zone,其中 3 个 Zone 是全能型副本,一个 Zone 是只读的副本吗? -> 答:建议 Zone 的数量最好是奇数,以 3 个 Zone 或 5 个 Zone 为主。 - -> 问:手动部署的 OceanBase 数据库可以用 OBD 管理吗? -> 答:不支持。 - -> 问:OMS 可以搭建集群吗? -> 答:目前社区版 OMS 3.3.1 版本支持集群化部署,低于此版本不支持。 - -> 问:社区版 MySQL 模式的 JDBC 驱动在哪下载? -> 答:如果使用 OceanBase 数据库的 MySQL 模式,推荐使用 MySQL JDBC 驱动 5.1.47 版本。 -> 如果使用企业版 OceanBase 数据库的 Oracle 模式,可从 [Maven 中央仓库](https://central.sonatype.com/artifact/com.oceanbase/oceanbase-client/2.4.3) 下载的 OceanBase 数据库的 JDBC 驱动。 - -> 问:使用 SpringBoot 连接 OceanBase 数据库的正确写法是什么? -> 答:可以参考 MySQL 连接方式,区别在于通过直连 OBServer 节点连接时 username 写法是用户名@租户名,通过 OBProxy 代理连接时 username 写法是用户名@租户名#集群名。 - - - -> 问:OBD 部署失败,怎么清理配置和目录? -> 答:2 种方式: -> 1)命令方式:`obd cluster destory 部署名称,销毁会清理相关目录。 -> 2)后台方式:杀掉所部署组件相关进程(ps -ef|grep observer),按部署配置文件里配置路径删除安装、数据、日志目录,并检查目录所属用户权限是否正确即可重新部署。 - -> 问:OCP 的 Docker 容器是否为无状态? -> 答:无状态,数据保留在部署在宿主机上的 metadb(单机/集群 OceanBase 数据库)中。 - -> 问:内部表和内部虚拟表是什么? -> 答:内部表一般指系统表,虚拟表只是系统表中的一部分,ID 号介与 10000~20000 之间。 - -> 问:备份数据时候,需要每个 OBServer 节点都挂载 NFS 吗? -> 答:是的。 - -> 问:备份可以备份单个租户的么? -> 答:OceanBase 数据库 3.x 版本只支持集群备份,4.0 版本起支持租户级别备份,且不再支持集群级别备份。 - -> 问:副本保持同步是通过同步 clog 吗? -> 答:是的,以分区为单位做 clog 副本同步,只有分区的主副本才能进行更新,如果有多个分区的主副本同时在更新,仍然以分区为单位做 Paxos 同步。 - -> 问:社区版 OceanBase 数据库怎么查看哪些表是复制表? -> 答:3.x 版本中通过所示命令查看。 -> -> ```sql -> `select table_name from oceanbase.__all_table_v2 where duplicate_scope=1;` -> ``` - -> 问:OceanBase 数据库 4.0 版本中,CDB 开头的视图、DBA 开头的视图、GV 开头的视图,这些是什么规则? -> 答:CBD 开头的是 sys 租户的视图,能看到全局的信息。DBA 开头的视图只能看到租户自己的信息。gv$ 一般和 v$ 一起,直连数据库节点的时候,v$ 只能看到本机器,gv$ 是所有机器节点的信息。 - -> 问:OceanBase 数据库 4.0 版本,普通用户租户查看自己租户下所有的表,该查哪个视图? -> 答:先执行 `select distinct(OBJECT_TYPE) from DBA_OBJECTS;` 命令查询表类型。 -> 后执行 `select OBJECT_NAME from DBA_OBJECTS where OBJECT_TYPE='table' ;` 命令根据表类型查表名。 - -> 问:执行 `obd cluster list` 命令显示状态为 `destroyed`的集群怎么清理删除? -> 答:cd ~/.obd/cluster,删除集群对应的 Name 目录。 - -> 问:社区版 OCP 开源的吗? -> 答:目前工具开放供下载使用,不开源,后续轻量版 OCPExpress 会考虑开源。 - -> 问:hint 中的 NO_USE_PX 是什么? -> 答:NO_USE_PX 指的是关闭并行查询。OceanBase 数据库 4.x 版本暂不支持关闭并行查询功能。 - -> 问:X86 版本 OCP 是否支持接管 ARM 版本的 OceanBase 数据库集群? -> 答:不支持混合接管和部署。 - -> 问:OceanBase 数据库支持 openeuler(欧拉)部署么? -> 答:暂未进行兼容适配,能部署但不保证运行后功能可靠性。 - -> 问:OceanBase 数据库一个表的分区大小是否有推荐大小? -> 答:OceanBase 数据库对分区大小未做限制,一般建议一个分区大小不要超过 100G,分区大小也可以按照磁盘空间和分区数预估。 - -> 问:proxy_sessid 和 server_sessid 之间的关系是什么? -> 答:proxy_sessid 唯一标识客户端和 OBProxy 之间的连接,server_sessid 唯一标识 OBPrxoy 和 OBServer 之间的连接。 - -> 问:OBClient 是不是没有 ubuntu/debian 系统的版本? -> 答:暂不支持,可使用 MySQL 客户端。 - -> 问:社区版 OMS 是否支持将 MySQL 数据库数据迁移到企业版 OceanBase 数据库的 MySQL 租户? -> 答:不支持。 - -> 问:OceanBase 数据库 4.0 版本怎么查看锁和事务的信息? -> 答:可在 DBA_OB_DEADLOCK_EVENT_HISTORY 中查看。 - -> 问:怎么查租户表占用磁盘空间大小? -> 答:仅查询基线数据的,转储合并后能看到最新的占用大小 -> OceanBase 数据库 3.x 版本可执行 `select tenant_id, svr_ip, unit_id, table_id, sum(data_size)/1024/1024/1024 size_G from __all_virtual_meta_table group by 1, 2, 3, 4;` 命令。 -> OceanBase 数据库 4.x 版本可执行 `select sum(size)/1024/1024/1024 from (select DATABASE_NAME,TABLE_NAME,TABLE_ID,PARTITION_NAME,TABLET_ID,ROLE from DBA_OB_TABLE_LOCATIONS ) AA full join (select distinct(TABLET_ID) ,size from oceanbase.GV$OB_SSTABLES ) BB on AA.TABLET_ID=BB.TABLET_ID where AA.role='leader' and AA.table_name='table_name';` 命令。 - -> 问:OceanBase 数据库 4.0 版本 TRUNCATE TABLE 支持指定分区清除数据吗? -> 答:支持,具体清理分区类型参见官网 OceanBase 数据库文档 [Truncate 分区](https://www.oceanbase.com/docs/community-observer-cn-10000000000901598)。 - -> 问:OBD 和 OBProxy 能和 OceanBase 集群部署在一起么?有什么性能影响? -> 答:可以,OBD 和 OBProxy 占用资源很少,业务量或者节点数不大情况下,几乎没什么性能影响。如果是大规模生产环境建议单独节点部署 OBProxy。 - -> 问:PHP 怎么连接 OceanBase 数据库? -> 答:[https://github.com/oceanbase/oceanbase/issues/841]。 - -> 问:执行 `obd cluster destory` 命令销毁集群后能否再恢复? -> 答:不能,该操作直接删除物理数据文件,如果存在备份文件,可以新建集群备份恢复。 - -> 问:mysql-connector-java 的 8.x 版本怎么连接 OceanBase 数据库? -> 答:使用 MySQL Connector/J 8.x 版本连接 OceanBase 数据库时,`Class.forName("com.mysql.jdbc.Driver")` 中的 com.mysql.jdbc.Driver 需要替换成 com.mysql.cj.jdbc.Driver。 - -> 问:是否有 JDBC 连接 OceanBase 数据库的配置示例? -> 答:`conn=jdbc:oceanbase://x.x.x.x(ip):xx(port)/xxxx(dbname)?rewriteBatchedStatements=TRUE&allowMultiQueries=TRUE&useLocalSessionState=TRUE&useUnicode=TRUE&characterEncoding=utf-8&socketTimeout=3000000&connectTimeout=60000` - -> 问:社区版 OCP 支持 RHEL8 吗? -> 答:支持 RHEL7.2 及以上版本。 - -> 问:OceanBase 数据库是否支持主键修改? -> 答:3.x 版本不支持,4.x 版本支持。 - -> 问:Datax 的 ob writer 里 obWriteMode 支持 insert ignore 吗? -> 答:不支持,可以采用 insert into 或者 replace into 或者 ON DUPLICATE KEY UPDATE 语句。 - -> 问:如何查看所有的 SQL 记录? -> 答: -> OceanBase 数据库 3.x 版本可通过 SQL 审计视图 gv$sql_audit 查看。 -OceanBase 数据库 4.x 版本可通过 SQL 审计视图 gv$ob_sql_audit 查看。 - -> 问:OceanBase 数据库 4.0 版本合并表信息是哪些? -> 答:major freeze 相关的信息放到了 **all_merge_info(用于存放租户合并的整体 merge 信息)、**all_zone_merge_info(用于存放租户下每个 Zone 的 merge 信息)表中。 -> **all_merge_info 表中有合并版本号、合并状态、是否发生了 error 等信息。 -> 除了 sys 租户外,每个用户租户对应的 meta 租户下都有这两张表,用于保存该用户租户和 meta 租户的 merge 信息。也可以在 sys 租户下通过查看 `CDB_OB_MAJOR_COMPACTION` 来查看所有租户的 **all_merge_info 信息,通过查看 `CDB_OB_ZONE_MAJOR_COMPACTION` 来查看所有租户的 \_\_all_zone_merge_info 信息。 -> OceanBase 数据库 4.0 支持对每个租户单独设置合并时间点,相关配置项为 major_freeze_duty_time,系统租户下可执行 `alter system set major_freeze_duty_time = 'xxx' tenant sys;` 语句设置。 - -> 问:OceanBase 数据库 4.0 版本为什么取消了轮转合并? -> 答:4.0 版本将合并拆分成了租户级,粒度更小,执行时间更短,因此取消了轮转合并。 - -> 问:如何手动杀掉正在执行 SQL? -> 答:执行 `show processlist;` 命令查看 ID 后执行 `kill $ID;` 命令。 - -> 问:怎么查看 OceanBase 数据库版本? -> 答:有多种方式查看,例如: -> -> 1. selecet version(); -> 2. SELECT \* FROM DBA_OB_SERVERS; -- 推荐 -> 3. 安装程序目录 ./bin/observer --version -- 推荐 - -> 问:OceanBase 数据库有哪些内存区域? -> 答:OceanBase 数据库中主要有以下内存区域: -> -> 1. kv cache:LSM-Tree 中 SSTable 及数据库表模式等的缓存。 -> 2. memory store:LSM-Tree 中的 MemStore 的内存。 -> 3. sql work area:租户执行 SQL 过程中各个 Operator 工作区占用的内存,超出部分通常会走落盘流程。 -> 4. system memory:预留给 Net IO、Disk IO、Election 与负载均衡等各种功能的内存。 - -> 问:OceanBase 数据库的资源在租户中哪些是共享的,哪些是不共享的? -> 答:对内存而言,sql work area、memory store 与 kv cache 等资源为租户独享;system memory 为多个租户共享。对线程而言,sql worker 为租户间隔离;Net IO、Disk IO 与 Clog Writer 等资源为租户间不隔离。 - -> 问:OceanBase 数据库的内存使用有什么特征? -> 答:OceanBase 数据库在启动时会需要加载大约几个 GB 的内存,在运行过程中会逐渐按需进一步申请内存直至 memory_limit。而一旦 OBServer 节点向 OS 进行了内存申请,通常是不会释放或者返回给 OS,而是会维护在内存管理的使用列表和 Free List 当中。这个是 OceanBase 数据库设计上所建立的内存管理机制。 - -> 问:运行一段时间后,OceanBase 数据库使用内存接近 memory_limit 是否正常? -> 答:在 OceanBase 数据库的集群运行一段时间以后,内存消耗接近于 memory_limit 水平位置,且不发生降低的现象是符合预期的。 有一个例外是若设置了参数 memory_chunk_cache_size,OBServer 节点会尝试将 Free List 超过 memory_chunk_cache_size 的内存块还给 OS,增大 Free List 在 OceanBase 数据库内部的复用率,减少 RPC 由于内存操作慢而导致超时的风险。通常情况是不需要配置 memory_chunk_cache_size 的,在特定的场景下需要与 OceanBase 数据库 支持进行场景分析确定是否需要动态调整 memory_chunk_cache_size。 - -> 问:OBServer 节点的内存上限是否可以动态调整? -> 答:OBServer 节点的内存上限可以通过调整 memory_limit 或 memory_limit_percentage 来动态实现。需要注意的是调整前要检查内存资源是否够用,OBServer 节点内存上限目标是否已经低于了所有租户以及 500 租户的内存分配(租户建立时分配)的总和。memory_limit 的单位是 MB,例如,如果需要通过调整 memory_limit 参数来调整 OBServer 节点内存上限为 64G,可以通过语句 memory_limit ='64G' 或者通过 memory_limit = 65536 来指定。 另一方面,在使用 OBServer 节点的过程中,可能通过 memory_limit 来限制 OBServer 节点内存的生效参数。若将 memory_limit 设置为 0,还可以通过 memory_limit_percentage 以比例的形式更灵活地约束 OBServer 节点内存的使用情况。 - -> 问:在使用 OceanBase 数据库定义资源单元以及资源池时是否允许内存超卖? -> 答:在使用 OceanBase 数据库定义资源单元以及资源池时, Root Service 负责分配资源(Unit)。Root Service 在分配 Unit 的时候会根据 resource_hard_limit 来决定内存是否可以超卖 (resource_hard_limit 表示内存超卖的百分比,大于 100 表示允许超卖),Root Service 再具体分配资源的时候还会按照 Unit 本身定义的资源单位来进行分配。 但是在通常情况下,如果资源相对来说比较紧张,系统不同的租户负载可以有控制地交错运行,配置租户时有可能会对 CPU 资源进行超卖。如果 CPU 超卖生效,OceanBase 集群在运行过程中会产生负载过载等情况,那么不同租户间会产生线程竞争 CPU 的现象,直接导致的结果是租户实际业务场景变慢。若是内存配置成超卖场景,虽然在创建租户时租户规格总和可以超过 memory_limit,但实际使用时 OceanBase 数据库的内存使用还是会受到 memory_limit 约束。比如在运行中的租户消耗的内存总和将要超过 memory_limit,租户会报内存超限问题甚至进程直接 OOM。 - -> 问:使用 OceanBase 数据库在开发中要特别注意什么? -> 答:以下列出开发过程中的注意事项,仅供参考: -> -> 1. 大数据量导入需要特别关注内存的使用情况。 -> 2. 索引生效时间较长,建议在建表时将索引语句一并纳入。 -> 3. 表建好后,主键不能更改。如果需要修改,只能删表重建。 -> 4. mysql-connector-java 的版本建议使用 5.1.30 及以上。 -> 5. 列类型修改有较大限制。Varchar 长度只能由短变长,不能由长变短。 -> 6. 如果一个连接超过 15 分钟空闲,服务端会主动断开,在使用连接池的时候需要设置一个连接最大的空闲时间。例如,Druid 的 minEvictableIdleTimeMillis 小于 15 分钟。 - -> 问:什么是实例,什么是租户,它们的关系是什么? -> 答:OceanBase 数据库是一个多租户系统, 一个实例即 OceanBase 集群中的一个租户。 租户之间的数据不能互相访问。 - -> 问:OceanBase 数据库的性能和机器数量的关系是什么? -> 答:从 OceanBase 数据库的 TPC-C 报告中显示,相同场景下的基本都是线性扩展的。 - -> 问:数据文件对应哪个级别的数据库管理? -> 答:OceanBase 数据库中目前有两类数据文件,且两类数据文件均属于集群级别: -> -> 1. Data 文件:保存各个分区的数据,包括各个分区的 Checkpoint 点。 -> 2. Clog 相关:包含 Clog(也称为 Redo Log 或 WAL 日志) 和它的索引文件。 - -> 问:`ERROR 1045 (42000): Access denied for user 'xx'@'xx.xx.xx.xx' (using password: YES)` -> 答:填写了密码,但无法访问数据库。 -> -> 1. 密码不正确。 -> 2. 不在集群访问白名单中。 -> 3. 集群处于初始化节点,未启动完成,可能是资源不足导致。 - -> 问:`ERROR 1045 (42000): Access denied for user 'xx'@'xx.xx.xx.xx' (using password: NO) ` -> 答:没填写密码,无法访问数据库。 -> -> 1. 没有填写密码。 -> 2. 密码 proxyro_password 为空且和 observer_sys_password 未保持一致。 - -> 问:`ERROR 1049 (42000): Unknown database 'oceanbase123' ` -> 答:-D 指定的库不存在。 - -> 问:`ERROR 2002 (HY000): Can't connect to MySQL server on 'xx.xx.xx.xx'` -> 答:无法连接到数据库。 -> -> 1. -P 登录端口不正确。 -> 2. 所连接 IP 节点不存在 OB 数据库服务。 -> 3. 防火墙/安全策略禁止访问等。 - -> 问:`ERROR 4669 (HY000): cluster not exist ` -> 答:-u 参数里的集群名称不存在。 - -> 问:`ERROR 4012 (HY000): Get Location Cache Fail ` -> 答:-u 参数里的租户信息不存在。 - -> 问:旁路导入时 need_sort 排序参数的作用? -> 答:数据本身是有序的,就设置 false,如果是无序的就设置 true,可以加快导入速度,如果设置了 need_sort = false,但是导入数据又是无序的,那么旁路导入内部会检测出来并报错。 - -> 问:clog 目录下的 log_pool 是做什么的? -> 答:log_pool 是 ob 的日志池化,根据 log_disk_size/64M 创建出多个 log_file,当 ob 需要记录 redolog 时,需要从 log_pool 里申请 log_file,简单理解就是日志盘预分配池,不能删,删除了可能会导致日志盘的空间不足或者一些非预期的异常。 - -> 问:如何删除 OCP 下的监控数据? -> 答:monitor 租户的数据不推荐手动删除,可以在 OCP 系统参数中搜索关键字 retention 进行调整,会自动清理,注意部分参数需要带上单位。 - -> 问:ocp-express 上显示每个 observer 的 cpu 使用率比较高,但租户的 cpu 使用率很低? -> 答:没有开启 cgroup 时,目前监控上的 cpu 使用率是指线程使用率,不表示物理 cpu 使用率。租户运行时要维持恒定量的活跃工作线程,这个数量是 min*cpu * cpu*quota_concurrency 来控制的。租户上有时会挂起一些处理慢查询的线程,同时要分配新的线程以维持活跃线程数恒定,这时用 max_cpu * workers_per_cpu_quota 限制租户总共持有的线程数上限。 - -> 问:创建了 tablegroup 不生效? -> 答:首先 OB4.2 版本才支持 tablegroup,修改 tablegroup 之后,要等 partition_balance 触发才会变更表组内各表分布。分区均衡通过调配置项 partition_balance_schedule_interval 可以触发。 - -> 问:OB4.x 查看索引状态? -> 答:`select count(*) from cdb_objects where status<>'VALID' and object_type in ('INDEX','INDEX PARTITION');` -> 或者 `select * from cdb_indexes where status<>'VALID';` - -> 问:OB4.x 版本怎么查询分区数? -> 答:可以从不同维度查询分区数信息。 -> -> - 总表个数 -> `select count(*) from cdb_OB_TABLE_LOCATIONS;` -> - 租户在各个节点主分区个数 -> `select count(*),svr_Ip from cdb_OB_TABLE_LOCATIONS where tenant_id=1001 and role='leader' group by svr_ip;` -> - 统计分区最多的 10 个租户 -> `SELECT t2.tenant_name,t2.tenant_id, t1.replica_count -FROM (SELECT con_id, COUNT(*) AS replica_count FROM CDB_TAB_PARTITIONS -GROUP BY con_id ORDER BY replica_count DESC LIMIT 10) t1 JOIN (SELECT tenant_id, tenant_name FROM __all_tenant) t2 ON t1.con_id=t2.tenant_id ORDER BY replica_count DESC;` - -> 问:怎么确认 outline 绑定 hint 生效了? -> 答:通过 `select outline_id from gv$ob_plan_cache_plan_stat where query_sql like '%sql语句关键字信息%';` outline_id 如果返回非-1 ,则表示 outline 生效。 - -> 问:OB 有一些参数或者变量在集群或者租户创建好后不允许修改,哪些是只读的? -> 答:参数:`select svr_ip, name,value from gv$ob_parameters where edit_level='readonly';` -> 变量:源码文件`cat src/share/system_variable/ob_system_variable_init.json | jq ".[] | {name,flags}" | grep -C 2 "READONLY" | grep -v "ORACLE_ONLY"` - -> 问:执行 sql 报错:`Error 5930: maximum open cursors exceeded`。 -> 答:open_cursors 默认设置为 50,可以调大改参数,ps 协议执行 sql 也会占用 open_cursors 数,建议设置 500,按需逐渐调大。 - -> 问:统计租户的大小的 sql? -> 答:`select t.tenant_name, -round(sum(t2.data_size)/1024/1024/1024,2) as data_size_gb, -round(sum(t2.required_size)/1024/1024/1024,2) as required_size_gb -from dba_ob_tenants t,cdb_ob_table_locations t1,cdb_ob_tablet_replicas t2 -where t.tenant_id=t1.tenant_id -and t1.svr_ip=t2.svr_ip -and t1.tenant_id=t2.tenant_id -and t1.ls_id=t2.ls_id -and t1.tablet_id=t2.tablet_id --- and t1.role='leader' -group by t.tenant_name -order by 3 desc;` - -> 问:统计库的大小 sql? -> 答:`select t1.database_name, -round(sum(t2.data_size)/1024/1024/1024,2) as data_size_gb, -round(sum(t2.required_size)/1024/1024/1024,2) as required_size_gb -from dba_ob_tenants t,cdb_ob_table_locations t1,cdb_ob_tablet_replicas t2 -where t.tenant_id=t1.tenant_id -and t1.svr_ip=t2.svr_ip -and t1.tenant_id=t2.tenant_id -and t1.ls_id=t2.ls_id -and t1.tablet_id=t2.tablet_id --- and t1.role='leader' -and t.tenant_name='test1' -group by t1.database_name -order by 3 desc;` - -> 问:统计表/索引的大小的 sql? -> 答:`select t1.table_name, -round(sum(t2.data_size)/1024/1024/1024,2) as data_size_gb, -round(sum(t2.required_size)/1024/1024/1024,2) as required_size_gb -from dba_ob_tenants t,cdb_ob_table_locations t1,cdb_ob_tablet_replicas t2 -where t.tenant_id=t1.tenant_id -and t1.svr_ip=t2.svr_ip -and t1.tenant_id=t2.tenant_id -and t1.ls_id=t2.ls_id -and t1.tablet_id=t2.tablet_id --- and t1.role='leader' -and t.tenant_name='test1' -and t1.database_name='sbtest' -and t1.table_name='sbtest1' -group by t1.table_name -order by 3 desc;` - -> 问:统计表对应的分区大小的 sql? -> 答:`select t1.table_name,t1.partition_name, -round(sum(t2.data_size)/1024/1024/1024,2) as data_size_gb, -round(sum(t2.required_size)/1024/1024/1024,2) as required_size_gb -from dba_ob_tenants t,cdb_ob_table_locations t1,cdb_ob_tablet_replicas t2 -where t.tenant_id=t1.tenant_id -and t1.svr_ip=t2.svr_ip -and t1.tenant_id=t2.tenant_id -and t1.ls_id=t2.ls_id -and t1.tablet_id=t2.tablet_id -and t1.role='leader' -and t.tenant_name='test1' -and t1.database_name='sbtest' -and t1.table_name='sbtest1_part' -group by t1.table_name,t1.partition_name;` - -> 问:ocp-agent 或者 ocp-monagent 服务定时重启? -> 答:OCP403 版本缺陷,建议升级 OCP421 最新版本。 - -> 问:使用不符合 only_full_group_by 的 SQL 语法查询结果集不一致? -> 答:符合预期,原生 mysql 也有同样的风险,ob 默认的 sql_mode 虽然未强制遵循 only_full_group_by,但建议用户按标准 sql 语法使用。 - -> 问:unit 迁移怎么提高并发度或者迁移速度? -> 答:可以调整 ha_mid_thread_score 参数。 - -> 问:OCP421 版本部署 obproxy 时,负载 OBLB 无法使用? -> 答:OCP 社区版不支持 OBLB 功能。 - -> 问:为什么普通租户查看 DBA_OB_LS 视图表比 sys 租户返回的还多? -> 答:DBA_OB_LS 可以看到当前租户的日志流信息,而系统租户默认只有一个日志流。 - -> 问:AP 业务经常遇到租户内存不足报错:`No memory or reach tenant memory limit ` -> 答:调整 ob_sql_work_area_percentage 参数,增加 sql 工作区的内存比,默认是 5%,一般 AP 比 TP 对内存的需求要大一点。 - -> 问:为什么数据目录只有一个 block_file 文件? -> 答:类似 oracle 的 segment 管理,最终都是通过宏块/微块组织的,一个 block_file 可以理解为一个盘,底层存储自己来管理使用这块盘,多个文件并没带来什么收益。 - -> 问:如何看 block_file 文件中的不同租户、表的 sstable 布局? -> 答:先找到表的分区 tablet_id(CDB_OB_TABLE_LOCATIONS),然后查\_\_all_virtual_tablet_sstable_macro_info 过滤 tablet_id,会列出所有的宏块。可以通过表中的宏块的 macro_logic_version 和 GV$OB_SSTABLES 中 sstable 的 end_log_scn 来对应宏块和 sstable 的关系。 - -> 问:如何获取如果获取微块 micro_id? -> 答:知道宏块 id 后,可以使用 dumpsst 来 dump 出宏块信息,里面会包含微块信息。 - -> 问:创建 resource unit 资源单元时,内存和 CPU 的比例推荐? -> 答:基于当前大型生产环境提供的配比经验,一般初始按 CPU:MEMORY=1:2 或 1:4 比例,后期再根据业务需要进行调整。 - -> 问:OCP 的 Unit 迁移中目标端地址不显示内容? -> 答:unit 迁移只在 zone 内发生,不同 zone 之间的属于扩容缩容。 - -> 问:obclient 怎么输出更多执行信息? -> 答:增加 -v 或者 -vv 参数打印详细信息。 - -> 问:OB4.x 支持使用密文密码创建用户吗? -> 答:支持。 - -> 问:如果针对单 sql 设置查询超时? -> 答:使用`hint select /*+ QUERY_TIMEOUT (10000000) */ `注意,obclient 连接时需要加 -c 参数。 - -> 问:OceanBase 的 update 操作在 memtable 中什么操作? -> 答:主表是 update,如果有修改索引列,索引表是 delete+insert。 - -> 问:observer 之间可以加密传输吗? -> 答:开启 RPC 安全认证就可以,但是会有性能回退,酌情选择。 -> `alter system set ssl_client_authentication=true;` -> `alter system set rpc_client_authentication_method='SSL_IO';` -> `alter system set rpc_server_authentication_method='SSL_IO';` - -> 问:解除主备租户关系后,怎么删除备份源? -> 答:`alter system set log_restore_source='' tenant = '$tennant_name'; `设置日志源为空即可。 - -> 问:是否支持无锁结构变更,offline DDL 转 online DDL。 -> 答:ODC 工具支持影子表的方式进行无锁结构变更。 - -> 问:OB 的索引碎片化问题怎么处理的? -> 答:OB 基本不存在索引碎片化,每天都会做合并,重新组织数据。 - -> 问:OB 的归档和备份为什么需要使用 NFS 或 OSS 介质? -> 答:单机且测试场景可以使用本地目录,集群或者生产环境需要使用介质,因为所有节点都需要访问公共的备份目录,数据在一个目录才可以做集群恢复。暂时没有多节点本地备份,汇总恢复的功能。 - -> 问:创建分区表,怎么确认需要创建多少个分区? -> 答:单个 server 不建议超过 3 万个分区总数,分区表的每个单分区不建议超过 200G,推荐 100G。 - -> 问:OceanBase 支持 Cognos 连接吗? -> 答:暂时不支持。 - -> 问:OB 为什么不建议数据和日志同盘? -> 答:底层存储引擎, 因为下面 3 个原因, 采用了提前预占用磁盘空间的设计。 -> -> 1. 为了更好的获取 io 性能: -> -> - 底层存储引擎提前将磁盘空间申请下来, 并采用 mmap 技术, 将部分空间映射起来, 能获得更快的磁盘性能。 -> - 另外有一些文件 io 操作, 可以提前做, 减少 io 指令, 提升性能。 -> - 多块盘, 能提供更多的并发能力。 -> -> 2. 增强系统的稳定性, 提前将磁盘占住, 避免被一些其他的程序将磁盘空间消耗掉, 导致存储引擎申请磁盘空间出错, 导致一系列的问题。 -> 3. 部分场合下可以减少硬件成本. 更快的盘(更贵的盘)可以用于 clog, 更大容量或稍慢一点(更便宜的盘) 用于 data 盘。 -> -> 从运维角度更安全,曾经发生过的故障: -> 用户将 clog 盘和 data 盘共用, 刚开始的时候, 磁盘空间能支撑业务, 随着业务量增大, data 的文件大小设置小了, 用户赶紧把 data 文件大小 datafile_size 调大, 一不小心, 忘记了 data 和 clog 共用一个盘, 导致 data 侵占了 clog 的空间, 最后导致 clog 写日志失败, observer 处在只读状态。 -> 同时分盘部署, 能减少运维的故障风险。 - -> 问:gv$sql_plan_monitor 这个视图什么情况下会生成数据? -> 答:三种情况可以看到数据: - -1. 如果 query 执行时间超过 3s,会有数据; -2. 或者加上 /\*+ monitor \*/ 这样的 hint,例如:`select /*+ monitor */ * from t1;` -3. 或者是并行 SQL,比如:`select /*+ parallel(3) */ * from t1;` - -> 问:DDL 语句执行 1000s(16 分钟)报错超时? -> 答:隐藏参数 \_ob_ddl_timeout 控制。和数据量相关的 ddl 的超时时间可以理解成无穷大(实际被设成了 102 年),不受 \_ob_ddl_timeout 的控制,像这类追加索引、追加外键、追加 check 约束这类比较特殊的 ddl,超时时间也不受 \_ob_ddl_timeout 的控制。 - -> 问:OB4.x 转储合并会对 DML 有影响吗? -> 答:转储前会先冻结 memtable,后续的 DML 会在新的 memtable 上进行,并不会有影响。 -> 至于转储如果卡住了,内存会无法释放,当持续的 DML 把内存吃满后,会导致 DML 执行失败。 - -> 问:执行 sql 报错内存不足和 ob_sql_work_area_percentage 参数有什么关系? -> 答:ob_sql_work_area_percentage 是租户的工作区内存占租户内存的百分比,工作区内存,是指 SQL 排序等阻塞性算子使用的内存,通过租户系统变量,默认值是租户内存的 5%,如果请求并发量较大,且每个请求占用的工作区内存比较多,可能出现工作区内存不足的报错,经常出现的场景有 union、sort、group by 等。上述问题如果出现,可以通过适当调大系统变量或者调大租户的内存来规避。 - -> 问:OB4.x 能限制租户级别的数据磁盘使用空间吗? -> 答:目前不可以,集群的 data 盘是所有租户共用的。 - -> 问:OBKV 场景,数据流中并未有 I/U 的标识,分段拉取,是否可以只使用 insert 实现最终大宽表的数据模型? -> 答:大宽表模型的话可以直接使用我们的 hbase 客户端实现。如果是 table 模型,不确定是 insert 还是 update,可以使用 table 客户端的 insertOrUpdate 接口。 - -> 问:OB4.x 使用 truncate 表后不会进回收站? -> 答:OB3.x 版本支持 truncate 表回收,OB4.x 暂时不支持。 - -### **原理** - -> 问:OceanBase 数据库的索引采用的是哪种方式? -> 答:MemTable 使用的是 B+ 树结构,而 SSTable 使用的是宏块结构。 - -> 问:plan cache 分配资源过少或者大并发会造成 plan cache 命中率较低的问题吗? -> 答:根据资源和并发情况分为如下几种情况讨论。 -> -> - 资源少的情况下需要看流量 -> - 如果流量一直只是一个 query,那么这个 plan 将一直存在 plan cache 中,而且一直命中。 -> - 如果流量是多种多样的 query,那么由于资源分配有限,plan 将会发生频繁汰换,最终分配资源过少会导致 plan cache 命中过低的。 -> - 并发量大导致内存快速写满会造成 plan 的频繁汰换,这将导致计划命中率降低。但是需要注意的是并发量大不一定会导致 plan cache 命中率降低,只要汰换率没有上去,命中率也不会降低。 - -> 问:租户 MemStore 使用达到阈值后,选择合并或者转储的依据是什么? -> 答:有自动转储和手动转储两种转储方式。有自动合并、定时合并与手动合并三种合并方式,如果自动合并过程中失败了,除了个别错误外,会一直重试。合并在默认情况下是以每日合并的方式定时触发的。 -> -> - 自动转储是当 MemStore 使用达到预设的限定时,例如 MemStore 内存使用率大于 `memstore_limit_percentage * freeze_trigger_percentage` 的值,自动触发冻结 + 转储,转储为 mini sstable 后根据 minor_compact_trigger 来触发 mini minor 或 minor 。 -> - 手动转储是手动执行 `alter system minor freeze;` 进行转储。 - -> 问:parallel_servers_target 并发参数怎么理解? -> 答:parallel_servers_target 这个指标都是针对 px 类型的 SQL 进行约束的,当超过这个阈值,阻塞的是后续的 px 类型 SQL 的执行,不阻塞本地 SQL 的执行。也就是并发 SQL 不会占用所有线程,会给普通 SQL 预留一些线程资源。所以 parallel 数量的设置应该根据需求和资源来确定,如果全都设成并发 SQL,就会互相阻塞。 - -> 问:用户的 SQL 请求和 RPC 之间有什么关系? -> 答:OBServer 节点之间沟通使用的是 RPC 2882 端口,最简单的以广播查询 SQL,是建立一张分区表,但是 SQL 语句不带分区键,用户执行 SQL 是发到了一台 OBServer 节点机器上,但是分区表的各个分区的主副本是在多台 OBServer 节点机器上的,需要将 OceanBase 数据库接收 SQL 的请求拆分,调用各个分区主副本所在的 OceanBase 数据库获取数据,然后再聚合后返回给用户。 - -> 问:GLOBAL 的变量会持久化到什么地方? -> 答:仅 GLOBAL 级别的变量会持久化,SESSION 级别的变量不会进行持久化。持久化到内部表与配置文件,可以在 /home/admin/oceanbase/etc/observer.config.bin 与 /home/admin/oceanbase/etc/observer.config.bin.history 文件中查询配置项 - -> 问:OceanBase 数据库 4.0 版本中,GV$OB_SERVER_SCHEMA_INFO 视图中的 SCHEMA 是什么? -> 答:Oceanbase 数据库的 schema 泛指一切需要在集群范围内同步的数据库对象元信息,包括但不限于 table、database、user 等元信息。此外 Oceanbase 数据库的 schema 是多版本且各租户独立,在集群范围同步是最终一致的。 -> GV$OB_SERVER_SCHEMA_INFO 可以理解为每台 OBServer 节点机器中每个租户已经刷新的最新版本的 schema 的信息,这个视图用户比较关注的 schema 信息是 REFRESHED_SCHEMA_VERSION、SCHEMA_COUNT、SCHEMA_SIZE,其含义如下。 -> -> - REFRESHED_SCHEMA_VERSION:对应租户在对应机器已刷新到的 schema 版本。 -> - SCHEMA_COUNT:对应 schema 版本下,各 schema 对象数目的总和。 -> - SCHEMA_SIZE:对应 schema 版本下,各 schema 对象总共所占的内存大小(单位 B)。 - -> 问:执行计划中的 px partition iterator,px block iterator 是什么意思? -> 答:partition iterator 是以分区粒度迭代数据,block iterator 是以 block 粒度迭代数据,Iterator 是对数据的抽象,它把数据抽象成一块一块的,以便多个线程并发地处理这些数据。一块数据如果对应的是一个分区,则是 partition iterator。一块数据如果对应的是一个或数个宏块,则是 block iterator。 - -> 问:OceanBase 数据库是如何创建索引,不影响更新操作的? -> 答:OceanBase 数据库在创建索引的时候,是一个渐进的过程,首先不影响写入,因为 OceanBase 数据库是追加写。对于索引列有更新的情况,会进行记录。然后会去扫描现有数据去创建索引,扫描完成之后将更新的和新增的数据做一次合并,最后再将索引标记为可用状态,就完成了索引的创建。 - -> 问:OceanBase 数据库 4.0 版本生产环境最小磁盘使用空间限制为什么是 54G? -> 答:54G 是目前计算出来 OceanBase 数据库启动需要的空间,下面是计算公式。 -> slog 统计规则:slog 统计 10G,目前是硬编码,有计划优化到 2G、4G 左右。 -> SSTable 统计规则:空集群启动后转储合并实测大概会有 1G 磁盘开销,代码没有针对 SSTable 做限制,但是按照长稳运行需要 20G。 -> clog 统计规则如下。 -> -> 1. 理论最小值:新建一个租户预期需要创建 4 个日志流,每个日志流目前空间需求最低 512M,这里算出需要的最小磁盘是 2G。 -> 2. 用户设置值:系统租户:取 max(2G,用户设置);普通租户:取 max(2G,所在盘全部)。 -> 3. OBD 计算:根据 OceanBase 数据库最小规则(4C8G)计算,即 8 \* 3 = 24,其中 3 为 OceanBase 数据库中 4.x 中对于 clog 内存和磁盘占用的大概估算。 -> -> 所以目前,选了 24G+10G+20G = 54G 作为启动最小依赖的内存,不过后续有优化的计划。 - -> 问:OceanBase 数据库内存是怎么规划的? -> 答: -> ![image.png](/img/FAQ/all_faq/1668247000430-b1ee6f69-76c1-47de-9490-81703b27afd5.png) - -> 问:OceanBase 数据库中 SQL 执行流程 -> 答: -> ![image.png](/img/FAQ/all_faq/1679833693317-53646c42-6030-4025-9a9e-ef1ae0cdc6a4.png) > ![image.png](/img/FAQ/all_faq/1679833729438-ff8ed706-d886-4cce-a024-e5677c46fe67.png) - -> 问:sql_audit 性能视图中各个字段表述的含义? -> 答:参看文章: -> `https://open.oceanbase.com/blog/6621094960?_gl=1*5whfs0*_ga*ODQzNDgzMjU4LjE2NjM1NzU0MjE.*_ga_T35KTM57DZ*MTcwNDE4MTM5OC4xNzAzLjEuMTcwNDE4MjMxNi40My4wLjA` - -> 问:OB4.x 版本 medium_merge 的合并策略是什么? -> 答:建议看源码 ObAdaptiveMergePolicy 类,这里简单介绍下。 -> medium_merge 的触发条件基于一些按分区收集的写入、查询、数据操作类型等方面的信息,经过某些策略计算后判断是否触发。 -> 简单说分为以下几个场景: -> LOAD_DATA_SCENE = 1, -- 导数场景,转储多且插入多 -> TOMBSTONE_SCENE = 2, -- 删除、更新多场景 -> INEFFICIENT_QUERY = 3, -- 慢查询多场景 -> FREQUENT_WRITE = 4, -- 新增数据量多 - -> 问:memtable 中的 transnode 压缩是什么? -> 答:将多个 transnode,如多个 update 整合到一个 TransNode,得到一个集合了这些 update 的 update 行,和 compact 过程有点类似。 - -> 问:SUBPLAN SCAN 与 SUBPLAN FILTER 算子分别会在什么情况下生成? -> 答:当读视图中的内容时,执行计划中会分配 subplan scan 算子;类似地,读实体表就会分配 table scan 算子。 -> SQL 在优化器里首先会被改写,如果改写之后还是一个子查询,那么就会生成 SUBPLAN FILTER 算子,用于驱动子查询的计算;如果非相关子查询被改写成了等价的 JOIN,就不会生成 SUBPLAN FILTER 算子了。 - -> 问:SUBPLAN SCAN 与 SUBPLAN FILTER 算子的区别是什么? -> 答:SUBPLAN FILTER 的算子功能是驱动表达式中的子查询执行,以 nested-loop 算法执行去 subplan filter 算子。如果是相关子查询,执行时每从左边取一行数据,然后就会执行一遍右边的子计划;如果是非相关子查询,还是从左边逐行取数据,不过右边的子计划实际只会算一次。 - -> 问:tablet 的作用? -> 答:tablet 是分片,具备存储数据的能力,支持在机器之间迁移(transfer),是数据均衡的最小单位。分区包括多个分片,sstable 是基线数据,会包含多个 tablet.分区分裂和合并根据分区规则。最简单情况下,可以认为一个分区对应一个 Tablet,一个 Tablet 对应一个 SSTable。更多复杂情况下,这都不一定,对应关系会比较复杂。 - -### **报错** - -> 问:oblogproxy 的 config.setTableWhiteList(“sys.abc.\*”) 如何设置指定表的信息? -> 答:使用 `|` 分隔符,例如 `sys.abc.tableA|sys.abc.tableB`。 - -> 问:OceanBase 数据库 V3.1.0 升级到 V3.1.4 版本失败? -> 答:OceanBase 数据库 V3.1.0 不支持本地升级,只能做数据迁移逻辑升级,并且 3.x 版本不支持直接升级至 4.x 版本。 - -> 问:报错 OceanBase 数据库 D-1006: Failed to connect to oceanbase-ce 的原因是什么? -> 答:有如下几种情况。 -> -> - OBD 和目标机器之间网络不连通。 -> - 对应的组件进程已经退出或者不提供服务。 -> - 账号密码不匹配。 -> - OceanBase 数据库日志磁盘不足。 - -> 问:报错 `oceanbase-ce's servers list is empty `的原因是什么? -> 答:检查部署配置文件 oceanbase-ce 模块下的 servers 中 ip 格式是否填写正确。 - -> 问:使用 OBLOADER 导入数据失败,未返回报错怎么办? -> 答:OBLOADER 默认的行为是:运行错误日志记录在 logs/ob-loader-dumper.error,具体错误的记录会记录在 logs/ob-loader-dumper.bad。 -> 如果要求遇到错误快速失败,可以指定 --max-errors 选项。 - -> 问:OCP 界面无操作退出登陆,怎么设置超时时间? -> 答:默认是 30m 无操作退出,可在 OCP 系统参数中设置 server.servlet.session.timeout。 - -> 问:OCP 上集群状态显示运维中? -> 答:“任务”中可能有任务在执行或执行失败。 - -> 问:用 benchmarksql 导入数据时,出现如下 3 类错误。 -> -> - Worker 089: ERROR: Failed to init SQL parser -> - Worker 044: ERROR: No memory or reach tenant memory limit -> - Worker 083: ERROR: Internal error -> -> 答:首先看下相关日志文件,如 benchmarksql.log。 -> -> 1. 确认使用的租户,因为 sys 租户内存太小,很可能不够用,所以不建议使用 sys 租户。 可通过命令 `select a.tenant_id,max(tenant_name), round(sum(used)/1024/1024/1024,2) “mem_quota_used(G)” from gv$memory a, __all_tenant b where a.tenant_id=b.tenant_id;` 查询,如果查询结果 tenant_id > 1000 则是普通租户,这样可以判断租户的情况。 -> 2. 如果是普通租户,查看 memory_limit 的值是不是太小,建议调大些。 -> 3. 查看 props.ocenbase 配置文件,里面配置了很多测试的配置信息,比如数据库,仓库数等等。props.oceanbase 文件中参数 warehouses 和 loadWorkers 的值需要修改成较小的值,然后登录(用 user 参数值登录),再测试一下,如果没问题,说明测试资源不足,可能是租户资源,也可能是机器资源有问题,此时可以参考租户资源和机器配置分析原因。 - -> 问:使用 myloader 想 OB4.2 导入数据报错:Cannot parse integer value "-u" for -P -> 答:需要在 -u,-P,-p 参数 后增加空格,例如:-u test -P 2881 -p "123"。 - -> 问:ubuntu 系统下 obclient2.2.2 版本无法使用方向键和退格键。 -> 答:obclient2.2.3 修复,或者使用 mysql 客户端。 - -> 问:关于展示物理恢复租户、备租户的日志恢复源的视图 CDB_OB_LOG_RESTORE_SOURCE 中的 RECOVERY_UNTIL_SCN 字段为日志获取上限,为什么会有上限? -> 答:RECOVERY_UNTIL_SCN :表示租户可恢复到的终点,如果值为 4611686018427387903 ,则表示恢复到无穷。 -> 如果 RECOVERY_UNTIL_SCN 为某个指定的值(非 4611686018427387903 ),则当租户的同步位点 SYNC_SCN 与租户可恢复到的终点 RECOVERY_UNTIL_SCN 相等时,租户不再同步日志。 - -### **故障恢复** - -> 问:服务器硬件故障或者需要长时间停止某个节点或某个 Zone,怎么操作? -> 答:需要调整 server_permanent_offline_time 参数,防止被永久下线。默认是 1 小时。该配置项的适用场景及建议值如下。 -> -> - OceanBase 数据库版本升级场景:建议将该配置项的值设置为 72h(OCP 升级 OceanBase 数据库可忽略,默认会调整)。 -> - OceanBase 数据库硬件更换场景:建议手动将该配置项的值设置为 4h。 -> - OceanBase 数据库清空上线场景:建议将该配置项的值设置为 10m,使集群快速上线。 - -> 问:创建分区表报错 `Too many partitions (including subpartitions) were defined`? -> 答:有如下两个原因。 -> -> - 超出单表最大分区数限制(8192 个)。 -> - 租户内存不足。 - -> 问:系统租户 root@sys 用户密码忘记了? -> 答:根据部署方式的不同有两种方法。 -> -> - OCP 部署时可对应集群总览界面 - **修改密码**。 -> ![image.png](/img/FAQ/all_faq/1669531503193-3103f39a-b2c1-44e9-8a01-b71323c6f235.png) -> - OBD 部署时可执行 `obd cluster edit-config 部署名称` 命令查看配置文件中的密码信息。 - -> 问:OMS4.x 如何跳过 DDL 操作? -> 答:目前 OMS4.x 已支持界面操作跳过 DDL,前提是增量同步报错后,人为界面确认并跳过,且并非所有 DDL 都支持跳过。手动跳过方式如下: -> 查看组件监控 -> 更新 Incr-Sync 组件 -> skipDdl 项 -> 跳过 1 个 ddl 写法:["create table a as select * from b"] -> 跳过多个 ddl 写法:["create table a as select * from b","create table c as select * from d""] -> 跳过包含关键字的 ddl 写法:["utf8mb4_unicode_ci"] 表示增量中跳过所有包含 utf8mb4_unicode_ci 关键字的 DDL 语句。 - -> 问:OMS 增量同步点位配置方法? -> 答:分为 2 种场景,OMS4.1.1 之前版本和 OMS4.1.1 以及之后版本配置方式 -> -> - OMS 4.1.1-CE 及以后版本,迁移链路选择增量同步时,前端页面可以直接调整增量同步位点。 -> ![](/img/FAQ/all_faq/17041707929011.jpg) -> -> - OMS 4.1.0-CE 版本,前端页面无法选择 增量同步起始位点,修改方法如下: -> -> 1. 运维监控 -> 组件 -> store -> 新增 -> 填写同步起始点位。 -> 2. 再通过迁移链路停止原来迁移链路的 store 组件。 - -> 问:在别的服务器上重新部署了 OBD,怎么把原来的 OBD 集群信息接管? -> 答:可以把原部署的 obd 用户上的~/.obd/上的信息复制到新的环境上~/.obd/下(obd 的部署用户需要保持一致),同时需要保证免密等操作的一致性。 - -> 问:OCP 管理的 OB 数据磁盘预占用太大,也无法扩磁盘,怎么办? -> 答:OB 的数据磁盘参数 datafile_size 和 datafile_disk_percentage 是不支持缩小的,一旦预占用太大,特别是同盘场景,其他文件占用磁盘后,会出现磁盘空间不足等问题,影响集群正常使用。多节点集群环境是可以通过节点替换方案轮询重装单节点重新设置数据盘大小的。单机可以部署新环境,采用数据导出导入/ODC 数据归档/OMS 数据迁移/备份恢复/主备租户方式恢复数据。以下以多节点集群方案后台操作方式调整流程。 -> -> 该方法使用限制: -> 仅适用于 OB 集群环境,单节点禁止使用。 -> 用户部署 OB 的时候 datafile_size 或者 datafile_disk_percentage 采用默认值,后续觉得太大,datafile 参数不支持调小。 -> -> 4.x 确认 data/clog disk 使用情况 -> `select zone,svr_ip,svr_port, -round(log_disk_capacity/1024/1024/1024,2) as log_disk_capacity_gb, -round(log_disk_assigned/1024/1024/1024,2) as log_disk_assigned_gb, -round((log_disk_capacity-log_disk_assigned)/1024/1024/1024,2) as log_disk_free_gb, -round((data_disk_capacity/1024/1024/1024),2) as data_disk_gb, -round((data_disk_in_use/1024/1024/1024),2) as data_disk_used_gb, -round((data_disk_capacity-data_disk_in_use)/1024/1024/1024,2) as data_disk_free_gb -from gv$ob_servers;` -> -> 3.x 确认 disk 使用情况 -> `select svr_ip,svr_port, -round(total_size/1024/1024/1024,2) as total_size_gb, -round(used_size/1024/1024/1024,2) as used_size_gb, -round(free_size/1024/1024/1024,2) as free_size_gb -from __all_virtual_disk_stat ;` -> -> 通过上面的 sql 可以评估实际数据占用的磁盘大小,如果实际使用量小,可以采用重建节点来减小 datafile 大小。 -> -> 确认要 delete server 的节点上涉及的租户: -> `select tenant_id,tenant_name from dba_ob_tenants where tenant_id in (select tenant_id from dba_ob_units where svr_ip='xxx');` -> -> 确认要 delete server 所属的 zone: -> `select svr_ip,zone from gv$ob_units where tenant_id=xxx and svr_ip='xxx';` -> -> 调整租户的 locality: -> `alter tenant test2 locality="F@zone1,F@zone2";` -> -> 确认 locality 是否执行完成: -> `select * from DBA_OB_TENANT_JOBS where sql_text like 'alter tenant test2 localit%';` -> -> 说明: -> enable_rereplication=true,对应的租户 ls_gc_delay_time 默认是 1 小时 gc,可以调小加快日志流 gc。 -> -> 调整租户的 resource pool list: -> `select * from dba_ob_resource_pools where tenant_id=xxx and zone_list='xxx';` > `alter tenant test2 resource_pool_list=('pool_test2_zone1_dgu', 'pool_test2_zone2_brw');` -> -> 删除 resource pool: -> `drop resource pool xxx3;` -> -> 说明: -> 如果要删除的 resource pool 在多个 zone 上存在复用的情况,需要先执行 split 操作,示例: -> `ALTER RESOURCE POOL pool1 SPLIT INTO ('pool10','pool11') ON ('zone1','zone2');` -> -> 执行 delete server: -> `alter system delete server 'xxx:2882';` -> -> 确认待删除的节点在 dba_ob_servers 里不存在了 -> `select * from dba_ob_servers where svr_ip='xxx';` -> -> kill 掉待重建的节点上的 observer 进程: -> `kill -9 xxx` -> -> 导出 etc 配置文件内容: -> `strings observer.config.bin > observer_config.txt` -> -> 删除文件(注意:如果有软连接,需要保留,仅删除对应目录下的文件): -> `rm -rf sstable/*` > `rm -rf clog/*` > `rm -rf slog/*` > `rm -rf etc/observer.config.bin` -> `rm -rf etc/observer.config.bin.history` -> -> 注意在正确的用户下设置环境变量并启动: -> `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/admin/oceanbase/lib` -> 启动 observer(注意调整 datafile_size 大小,其他配置见前面导出的配置文件中的值) -> `/home/admin/oceanbase/bin/observer -r 'xx.xx.xx.xx:2882:2881;xx.xx.xx.xx:2882:2881;xx.xx.xx.xx:2882:2881' -p 2881 -P 2882 -z zone3 -c 4 -d /home/admin/oceanbase/store/obcluster -i eth0 -o "__min_full_resource_pool_memory=1073741824,memory_limit=24G,system_memory=2G,datafile_size=20G,enable_syslog_wf=False,enable_syslog_recycle=True,max_syslog_file_count=100,obconfig_url=http://xx.xx.xx.xx:8080/services?Action=ObRootServiceInfo&User_ID=alibaba&UID=ocpmaster&ObRegion=obcluster"` -> -> 将处理过的节点加进集群 -> `alter system add server '172.24.255.51:2882' zone 'zone3';` -> -> 给对应的租户添加副本 -> -> 开始其他节点的操作,步骤同上。 - -### **性能调优** - -> 问:explain 预估耗时不准,有具体过程执行耗时吗? -> 答:可以查看视图表 gv$sql_plan_monitor。 - -> 问:亿级别数据创建索引耗时超长怎么优化? -> 答:增加并行加快索引创建,设置并发 -> -> ```sql -> SET GLOBAL OB_SQL_WORK_AREA_PERCENTAGE = 30; --- 增加SQL执行内存百分比 -> SET SESSION_FORCE_PARALLEL_DDL_DOP = 32; --- 设置DDL并行度 -> SET GLOBAL PARALLEL_SERVERS_TARGET = 64; --- 并行度增加需要设置该参数,比所有并行度和大即可,所有 DDL 的并行度加起来不超过租户 max_cpu 的上限 -> ALTER SYSTEM SET _TEMPORARY_FILE_IO_AREA_SIZE = '5'; --- 调大临时文件内存缓存,建议小于10 -> -- 如果有限流可以关闭 -> 关闭 IO 限流:alter resource unit xxxx max_iops=10000000, min_iops=10000000; --- 1 个 Core 对应 1 万 IOPS 的值 -> 关闭网络限流:alter system set sys_bkgd_net_percentage = 100; --- 默认60 -> ``` - -> 问:大数据量插入如何提高效率? -> 答:可以从应用层、数据库层、工具层三个层面提高。 -> -> - 应用层: -> jdbc url 上开启批量参数,重写批量,ps 缓存相关参数打开。 -> -> ```bash -> cacheServerConfiguration=true -> rewriteBatchedStatements=true -> useServerPrepStmts=true -> cachePrepStmts=true -> ``` -> -> 当然代码也是要 addBatch,executeBatch。 -> -> - 数据库层: -> - OceanBase 数据库 4.1 版本支持旁路导入,详情请参见官网 OceanBase 数据库文档 [旁路导入概述](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001687926)。 -> - 4.x 版本支持并行导入,详情请参见官网 OceanBase 数据库文档 [体验并行导入 & 数据压缩](https://www.oceanbase.com/docs/community-observer-cn-10000000000900958)。 -> - 工具层: -> 使用 OBLOADER 导数工具,并按需进行调优,详情请参见官网 OceanBase 导数工具文档 [性能调优](https://www.oceanbase.com/docs/community-obloaderdumper-cn-10000000002035960)。 - -> 问:OceanBase 数据库系统日志打印太大太多怎么办? -> 答:可参考如下命令优化。 -> -> ```shell -> # 进入 OceanBase 数据库的日志目录 -> cd ~/observer/log -> # 删除 wf 和日期结尾的日志 -> rm -f observer*.wf observer.log.2023* -> # 登录系统租户(root@sys),设置系统日志参数 -> alter system set enable_syslog_recycle=true -- 开启日志回收 -> alter system set enable_syslog_wf=false --关闭 wf 日志打印 -> alter system set max_syslog_file_count=10 --限制日志个数,按需调整(单个日志 256M) -> alter system set syslog_level ='ERROR'; --设置系统日志级别(DEBUG/TRACE/INFO/WARN/USER_ERR/ERROR),生产环境建议保持 INFO 级别,方便问题排查定位。 -> ``` - -> 问:OBProxy 日志打印太大太多怎么办? -> 答:可使用系统租户(root@sys)或者 root@proxysys 连接 OceanBase 集群执行如下命令修改。 -> -> ```sql -> show proxyconfig like '%log_file_percentage%'; -> alter proxyconfig set log_file_percentage=75; -> ``` -> -> 以下是 OBProxy 日志相关参数。 -> -> | 参数 | 默认值 | 取值范围 | 解释 | -> | -------------------------- | ------ | ----------------------------------------- | ------------------------------------------------ | -> | **log_file_percentage** | 80 | [0, 100] | OBProxy 日志百分比阈值。超过阈值即进行日志清理。 | -> | **log_dir_size_threshold** | 64GB | [256MB, 1T] | OOBProxy 日志大小阈值。超过阈值即进行日志清理。 | -> | **max_log_file_size** | 256MB | [1MB, 1G] | 单个日志文件的最大尺寸。 | -> | **syslog_level** | INFO | DEBUG, TRACE, INFO, WARN, USER_ERR, ERROR | 日志级别。 | - -> 问:OceanBase 数据库怎么开启慢查询记录? -> 答:SQL 执行时间默认情况下超过 1s 就会记录到 observer.log 日志或者在 OCP 白屏监控有记录。该配置受租户参数 `trace_log_slow_query_watermark` 控制,可通过如下命令修改配置。 -> -> ```sql -> -- 查看参数配置 -> show parameters like '%trace_log_slow_query_watermark%'; -> -- 修改参数 -> alter system set trace_log_slow_query_watermark='2s'; -> ``` - -> 问:如何查看某个实例(租户)下库表分区主副本的位置和大小? -> 答:根据不同版本有如下两种方式。 -> -> - OceanBase 数据库 3.x 版本,执行如下命令。 -> -> ```sql -> SELECT t.tenant_id, a.tenant_name, t.table_name, d.database_name, tg.tablegroup_name , t.part_num , t2.partition_id, t2.ZONE, t2.svr_ip , round(t2.data_size/1024/1024/1024) data_size_gb -> , a.primary_zone , IF(t.locality = '' OR t.locality IS NULL, a.locality, t.locality) AS locality FROM oceanbase.__all_tenant AS a -> JOIN oceanbase.__all_virtual_database AS d ON ( a.tenant_id = d.tenant_id ) -> JOIN oceanbase.__all_virtual_table AS t ON (t.tenant_id = d.tenant_id AND t.database_id = d.database_id) -> JOIN oceanbase.__all_virtual_meta_table t2 ON (t.tenant_id = t2.tenant_id AND (t.table_id=t2.table_id OR t.tablegroup_id=t2.table_id) AND t2.ROLE IN (1) ) -> LEFT JOIN oceanbase.__all_virtual_tablegroup AS tg ON (t.tenant_id = tg.tenant_id and t.tablegroup_id = tg.tablegroup_id) -> WHERE a.tenant_id IN (1006 ) AND t.table_type IN (3) -> AND d.database_name = 'T_FUND60PUB' -- 库名 -> and table_name in ('BMSQL_HISTORY') -- 表名 -> ORDER BY t.tenant_id, tg.tablegroup_name, d.database_name, t.table_name, t2.partition_id; -> ``` -> -> - OceanBase 数据库 4.x 版本,执行如下命令。 -> -> ```sql -> select svr_ip,count(1) from__all_virtual_ls_meta_table where tenant_id=1002 groupby svr_ip; -> ``` - -> 问:OceanBase4.1 升级到 OB4.2 版本,NLJ 算子连接顺序变化导致查询慢问题。 -> 答:查看以下 2 个执行计划, -> -> - 第一个红框中的是 st 表作为驱动表和(od,cs)的 NLJ 结果进行关联, (od,cs)的结果作为物化表; -> - 第二个红框的是(od,cs)的 NLJ 的结果作为驱动表和 st 表进行关联,st 表的结果作为物化表; -> OB4.2 执行计划: -> ![](/img/FAQ/all_faq/17041808793823.jpg) -> OB4.1 执行计划: -> ![](/img/FAQ/all_faq/17041809432614.jpg) -> 优化思路: 既然是子查询的 NLJ 顺序变了,导致 4.2 版本的查询变慢,可以通过 hint 的方式将驱动表顺序换回来 4.1 的执行计划。子查询加上这个 hint, 确保 od,sc,st 做连接的时候 od,sc 在前,cs 在后;为了能让 od,sc 表关联走 NLJ,可以再加一个 hint 确保 od,cs 走的时候会用 NLJ 算子,所以总体上应该加的 Hint 是 /_+leading(od,cs,st) use_nl(od, cs)_/ 耗时从 5min 优化到 3s。 -> 例如:SELECT ... FROM ... s, ... p WHERE 1=1 and ... AND NOT EXISTS -> ( SELECT /_+leading(od,cs,st) use_nl(od, cs)_/ 1 FROM ... st, ... od, ... cs WHERE 1=1 and ... - -> 问:OBCDC 如何按照建表语句的顺序获取列名? -> 答:在 OBCDC 启动时指定配置项 enable_output_by_table_def=1(配置项自 OBCDC4.1.0.1 版本生效)。 - -## **OBD 部署问题** - -> **问题现象** -> -> 使用 OBD 部署 OceanBase 数据库 V3.1.4 时提示 Warn 级别报错信息 `[WARN] (x.x.x.x) clog and data use the same disk (/)`,安装流程可正常进行,程序正常。 -> -> **可能原因** -> -> 生产环境下要求数据目录和安装目录配置为不同盘。小规模且短期的测试环境可以忽略,生产环境必须保证安装目录、数据目录、日志目录均是分盘目录,否则后期使用会出现非业务数据占满磁盘而出现磁盘使用率满问题。 -> -> **解决方案** -> -> 可执行 `obd cluster edit-config xxx` 命令编辑配置文件,将 data_dir 和 redo_dir 设置为不同磁盘目录,保存后根据黑屏输出执行对应命令。 - ---- - -> **问题现象** -> -> 使用 OBD 部署 OceanBase 集群后,通过 OBProxy 登录数据库报错 `ERROR 2013 (HY000): Lost connection to MySQL server at ‘reading authorization packet’`。 -> -> **可能原因** -> -> 配置文件中 proxyro_password 和 observer_sys_password 两个配置项设置未保持一致。 -> -> **解决方案** -> -> 可执行 `obd cluster edit-config xxx` 命令编辑配置文件,将配置项 proxyro_password 和 observer_sys_password 保持一致,保存后之后根据黑屏输出命令执行即可。 - ---- - -> **问题现象** -> -> obd web 部署 ob 失败,白屏显示:(2003, "Can't connect to MySQL server on 'xx.xx.xx.xx' ([Errno 113] No route to host))" -> observer.log 日志报错,ERROR 级别信息:Unexpected internal error happen, please checkout the internal errcode(errcode=-4009, file="ob_local_device.cpp", line_no=1405, info="Fail to fallocate block file, ") -> -> **可能原因** -> -> 用户使用的 ext3 文件系统,ob 预占用使用 fallocate -l 3G /data/xx/block_file 方式申请磁盘空间,ext3 文件系统执行会报错 Operation not supported。 -> -> **解决方案** -> -> 数据盘和日志盘使用 ext4 或者 xfs 文件系统。 - ---- - -> **问题现象** -> -> 使用 OBD 部署 OceanBase 集群后,通过 OBProxy 登录数据库报错 `ERROR 2013 (HY000): Lost connection to MySQL server at ‘reading authorization packet’`。 -> -> **可能原因** -> -> 配置文件中 proxyro_password 和 observer_sys_password 两个配置项设置未保持一致。 -> -> **解决方案** -> -> 可执行 `obd cluster edit-config xxx` 命令编辑配置文件,将配置项 proxyro_password 和 observer_sys_password 保持一致,保存后之后根据黑屏输出命令执行即可。 - ---- - -> **问题现象** -> -> 使用 OBD V1.6.0 部署 OceanBase 数据库时报错 `Open ssh connection x`。 -> -> **可能原因** -> -> 机器之间跳转使用配置文件中的 user 模块下的 ssh 连接信息,无法连接可能是未配置或配置用户信息不正确等。 -> -> **解决方案** -> -> 可查看 config.yaml 内关于 user 部分设置是否正确。若使用的 password 的方式,可尝试下手动使用改密码和对应用户能否登录到对应机器。 - ---- - -> **问题现象** -> -> OBD V2.0.0 中执行 obd web 命令后无法访问到白屏部署界面。 -> -> **可能原因** -> -> - 防火墙问题 -> - 其他安全程序禁止 IP 或端口访问 -> - 内外网环境,部署使用内网 IP,访问需要使用外网 IP -> - obd web 进程被手动杀掉 -> - 离线网络环境,未关闭远程仓库,导致 8680 端口未起 -> -> **解决方案** -> -> 您可检查是否是上述原因引起,并根据具体原因进行处理。 - ---- - -> **问题现象** -> -> 执行 `obd cluster deploy obce-single -c obce-single.yaml` 命令不成功,报错 `[ERROR] Failed to download [mirrors.aliyun.com/oceanbase/community/stable/el/None/aarch64///repodata/repomd.xml](http://mirrors.aliyun.com/oceanbase/community/stable/el/None/aarch64///repodata/repomd.xml) to /home/admin/.obd/mirror/remote/OceanBase-community-stable-elNone/repomd.xml` -> -> 后执行 `obd cluster list` 命令查看 Cluster 状态是 configured。 -> -> **可能原因** -> -> OBD 默认开启远程镜像下载,无法联网环境将下载失败,需要关闭远程镜像获取,将安装包导入本地仓库,本地安装即可。 -> -> **解决方案** -> -> 执行 `obd mirror disable remote` 命令禁用远程镜像仓库,并执行 `obd mirror clone` 命令将安装包复制本地仓库。 - ---- - -> **问题现象** -> -> 使用 OBD 部署 OceanBase 数据库 V3.1.3 时报错 NTP 服务未同步 `[ERROR] Cluster NTP is out of sync`。 -> -> **可能原因** -> -> 使用 OBD 部署时做了时差校验,当服务器之间时差超过 100ms 时,会出现 Root Server 无主情况。 -> -> **解决方案** -> -> 安装 NTP,进行时钟同步。 - ---- - -> **问题现象** -> -> 使用 OBD 部署 OceanBase 数据库 V4.0.0 时初始化失败,报错:`[ERROR] Cluster init failed`。查看 observer.log 日志出现 `fail to send rpc(tmp_ret=-4122` 字段。 -> -> ![image.png](/img/FAQ/all_faq/1670835604707-b526c34a-d51f-482c-8cdb-af4c578cd5f9.png) -> -> **可能原因** -> -> 现场防火墙做了限制,导致 rpc 无法互相通信。 -> -> **解决方案** -> -> 关闭防火墙。 - ---- - -> **问题现象** -> -> 在 pymysql 模块已安装的情况下,使用 OBD V1.6.0 安装 OceanBase V4.0.0 集群的时候提示缺少 pymysql 模块:`ModuleNotFoundError:No module name 'pymysql'`。 -> -> ![image.png](/img/FAQ/all_faq/1670665435483-e597b7b5-1ade-485a-8b32-bf683933f950.png) > ![image.png](/img/FAQ/all_faq/1670665860175-1f4b8886-409a-41d6-9ecf-c47b579adf8d.png) -> -> **可能原因** -> -> 示例中是启动用户不对,直接使用 root 即可,不要切换用户再 sudo,并且看报错缺少 /usr/obd/lib/site-packages 目录应该是 OBD 安装有问题,可以重新安装 OBD,现场通过创建目录拷贝方式最终也能解决。 -> -> **解决方案** -> -> 安装时不要使用 sudo 方式,安装使用的是当前用户进行的,创建缺少的 /usr/obd/lib/site-packages 目录,并把 python 模块拷贝进去。 - ---- - -> **问题现象** -> -> 使用 OBD 部署 OceanBase 数据库时部署卡在 Remote oceanbase-ce\* repository install 阶段。 -> ![image.png](/img/FAQ/all_faq/1682389370244-dedd852a-3c9d-487f-96ef-0d69dce7b26f.png) -> -> **可能原因** -> -> 此处会涉及是拉包和传包过程,涉及基础命令,如果非公网是无法使用远程仓库的,如果磁盘可使用空间不足,也无法下载成功。 -> -> **解决方案** -> -> 具体需根据 OBD 日志分析,目前已知有如下可能。 -> -> - 本机缺少 rsync 命令:可执行 `yum install -y rsync` 命令安装 rsync 命令。 -> - sftp-server 不一致:/etc/ssh/sshd_config 配置和本机 sftp-server 路径保持一致,并重启 sshd 服务。 -> - 非公网环境,使用了在线安装方式:执行 `obd mirror disable remote` 命令关闭远程仓库拉取安装包,采用离线安装。 -> - 本地磁盘满:下载包较大,磁盘满下载将失败。 - ---- - -> **问题现象** -> -> 安装过 obd2.1 版本后,重新安装 obd 低版本 2.0.1 后,obd web 执行界面报错,type object 'ConfigUtil' has no attribute 'get_random_pwd_by_rule' -> -> **可能原因** -> -> obd2.1 版本支持随机密码功能,但重新安装的 2.0.1 版本不支持,导致此类 get_random_pwd_by_rule 无法被找到。obd 不支持降级,如果需要降级,需要清理环境再安装 obd。 -> -> **解决方案** -> -> - 方案 1:(适用于生产环境) -> 1)cd ~/.obd/ -> 2)rm -f versiom (隐藏文件) -> 3)执行 obd --version,会重新生成新的 plugins -> 4)obd web 部署即可 -> - 方案 2:(适用于测试环境) -> 1)rpm -e 卸载 obd 包 -> 2)rm -rf ~/.obd/ 删除.obd 目录 -> 3)重新 install.sh 安装 obd 初始化环境 -> 4)obd web 部署即可 - ---- - -> **问题现象** -> -> OBD2.4.0 白屏部署 OB 预检查失败,报错:OBD-2007: xx.xx.xx.xx ens192 fail to ping xx.xx.xx.xx. Please check configuration `devname` -> -> **可能原因** -> -> - 网卡信息不正确; -> - 防火墙或 selinux 未关闭或禁止 ping; -> - 普通用户无 ping 权限; -> -> **解决方案** -> -> - 白屏部署时 更多配置中 需要指定 devname 参数对应的实际网卡名称。 -> - 关闭防火墙或 selinux,或者防火墙规则去掉禁 ping 行为。 -> - 部分系统普通用户无 ping 权限,root 执行:chmod u+s /usr/sbin/ping - ---- - -> **问题现象** -> -> OBD2.3.1 部署 OBProxy 时报错:`[ERROR] failed to extract file from /root/.obd/mirror/remote/OceanBase-community-stable-el7/obproxy-ce-4.2.1.0-11.el7.x86_64.rpm` -> -> **可能原因** -> -> 可能存在包不完整,或者包损坏等问题。 -> -> **解决方案** -> -> 建议去 `~/.obd/repository`,` ~/.obd/mirror/local` 路径删除对应安装包,重新 obd mirror clone 到本地仓库,重新部署。 - ---- - -> OBD2.3.1 在 ky10 银河麒麟系统中部署 OB 卡在 Initialize oceanbase-ce 阶段。 -> -> **可能原因** -> -> OBD2.3.1 版本暂未和 ky10 系统做兼容。 -> -> **解决方案** -> -> OBD2.4.0 版本已经兼容 ky10 系统。 - ---- - -> **问题现象** -> -> OBD250 白屏部署 OB 无法选择每个节点的网卡名称。 -> -> **可能原因** -> -> 白屏部署暂不支持指定每台机器的网卡名称,可以黑屏部署使用配置文件指定。 -> -> **解决方案** -> -> 在配置文件中组件下的 servers 模块指定各个节点的网卡名。例如: -> oceanbase-ce: -> ` ` ` ` servers: -> ` ` ` ` ` ` ` ` - name: server1 -> ` ` ` ` ` ` ` ` ` ` ip: xx.xx.xx.1 -> ` ` ` ` ` ` ` ` - name: server2 -> ` ` ` ` ` ` ` ` ` ` ip: xx.xx.xx.2 -> ` ` ` ` servere1: -> ` ` ` ` ` ` ` ` zone: zone1 -> ` ` ` ` ` ` ` ` ` ` devname: eth0 -> ` ` ` ` ` ` ` ` zone: zone2 -> ` ` ` ` ` ` ` ` ` ` devname: eth1 - ---- - -> **问题现象** -> -> OBD2.4.0 部署报错:`[ERROR] OBD-2000: (127.0.0.1) not enough memory. (Available: 2.6G, Need: 3.0G)` -> -> **可能原因** -> -> 这里看出是可用内存不足。 obd 检查的内存是可用的 非缓存的。 -> -> **解决方案** -> -> 如果服务器没其他程序,可以清理一下缓存,释放内存。 -> `echo 3 > /proc/sys/vm/drop_caches` - ---- - -> **问题现象** -> -> OBD2.4.0 在 ubuntu 部署 OB 失败,报错: -> `OBD-1002: Fail to init dip(127.0.0.1) home path: /home/admin/oceanbase/ is not empty`。 -> -> **可能原因** -> -> - 目录非空,部署需要检测安装目录是否非空。 -> - 用户环境变量问题。 -> -> **解决方案** -> -> - 删除目录,重新部署。 -> - 创建用户需要指定-s /bin/bash,例如:`useradd -U admin -d /home/admin -s /bin/bash`。 - ---- - -## **OBD 使用问题** - -> **问题现象** -> -> 使用 OBD V1.5.0 执行 `obd cluster display xx` 命令时报错 `ERROR Another app is currently holding the obd lock`。 -> -> **可能原因** -> -> 有其他进程在使用 OBD,OBD 不支持多进程同时操作。 -> -> **解决方案** -> -> 确认是否有未结束的 OBD 其他操作,或者有人也在调用 OBD。 - ---- - -> **问题现象** -> -> 连接 OceanBase 数据库并执行命令修改完密码,使用 OBProxy 登录数据库报错 `ERROR 2013 (HY000): Lost connection to MySQL server at 'reading authorization packet'`。 -> -> **可能原因** -> -> 这个报错基本是密码不一致导致,后台修改密码不会同步到 OBD 配置文件中,导致无法使用 OBD 配置中的密码登录。 -> -> **解决方案** -> -> 后台登录数据库还原成 OBD 配置中的密码,再通过 `obd cluster edit-config` 命令编辑配置文件修改密码,保存后执行 `obd cluster reload` 重载集群。 - ---- - -> **问题现象** -> -> 使用 OBD2.3 升级 OB4.1 到 OB4.2 时报错:`tenant tenant_cube does not meet rolling upgrade conditions(zone number greater then 2)` -> -> **可能原因** -> -> OBD2.3 升级 OB 时的轮转升级条件检查存在缺陷,OBD2.4.0 修复。 -> -> **解决方案** -> -> 升级 OBD 版本。 - ---- - -> **问题现象** -> -> OBD2.3.1 升级 OB4.x 时,在 exec upgrade_post.py 阶段失败。升级日志 upgrade_post.log 报错:`check enable_rebalance:True sync timeout` -> -> **可能原因** -> -> OBD2.3.1 版本升级 OB 时,需要检查 enable_rebalance 参数是开启状态,默认是开启。如果有人为关闭自动负载参数,升级会失败,后续 OBD 版本会优化。 -> -> **解决方案** -> -> 查看哪些租户 enable_rebalance 参数非 true: -> `select * from oceanbase.GV$OB_PARAMETERS where name = 'enable_rebalance';` -> 登录对应租户修改参数: -> `ALTER SYSTEM SET enable_rebalance = true;` - ---- - -> **问题现象** -> -> OBD2.3.1 扩容 OB 节点后,ocp-express 不显示扩容的节点信息。界面报错 404:没有找到指定 obagent 类型的记录,参数:xx.xx.xx.xx,请检查后重试。 -> -> **可能原因** -> -> 后续版本会优化。 -> -> **解决方案** -> -> 需要带参数重启下 ocp-express -> obd cluster restart 部署名称 -c ocp-express --wp - ---- - -> **问题现象** -> -> OBD2.3.1 进行白屏部署时,配置的用户有 sudo 和免密,预检查 ssh 相关操作还是不通过。 -> -> **可能原因** -> -> 执行安装 obd web 命令用户需要和免密用户不是同一个。 -> -> **解决方案** -> -> 使用同一个用户执行 obd web 部署。 -> 或者界面上选择主机用户时填写密码也可以。 - ---- - -> **问题现象** -> -> OBD2.3.1 执行 deploy 命令部署报错:`[ERROR] No such install plugin for oceanbase-ce-4.2.1.1` -> -> **可能原因** -> obd 的安装有 all-in-one 和独立安装包安装 ,可以通过 command -v obd 查看一下 obd 来源 -> -> - obd 环境变量不正确,部署了多个 obd。 -> - obd 环境污染。 -> -> **解决方案** -> -> - 如果 command -v obd 指向的 all-in-one 相关的路径 可以去用户下的.bash_profile 环境变量里面删除这个记录,继续执行。 -> - 如果 command -v obd 是/usr/bin/obd,可能是插件生成时候被污染了。 -> 1. 删除 obd 版本标识 rm -rf ~/.obd/version -> 2. 执行 obd cluster list 会更新插件,有报错可以直接忽略。 -> 3. 重新执行 deloy 安装部署。 - ---- - -> **问题现象** -> -> OBD2.4.0 重启 ocp-express 服务失败,jdbc 连接 ocp-expresss 数据库时报错:`Get Location Cache Fail`。客户端连接租户报错:`Server is initializing`。 -> -> **可能原因** -> -> Server is initializing 说明不是连接串租户名称不正确,而是租户还在初始化。 -> 查看 observer.log:`allocate memory fail`。导致初始化申请不到内存。 -> -> **解决方案** -> -> 检查本机是否有其他应用程序占用内存,或者内存设置是否合理。 -> 该案例是其他程序占用内存,导致内存不足。 - ---- - -> **问题现象** -> -> OBD2.4.0 启停集群时报错:`UnicodeEncodeError: 'latin-1' codec can't encode characters in position 77-80: ordinal not in range(256)` -> -> **可能原因** -> -> python 的默认编码是 latin-1,用户的环境变量可能编码有问题。 -> -> **解决方案** -> -> `echo 'export LANG=en_US.UTF-8' >> ~/.bashrc` -> `echo 'export LC_CTYPE=en_US.UTF-8' >> ~/.bashrc` - ---- - -> **问题现象** -> -> OBD240 重启 OCP 失败,报错:`KeyError:'tenant_name'`。 -> -> **可能原因** -> -> OBD240 缺陷 -> -> **解决方案** -> -> OBD 启动 OCP 的命令加上 --skip-create_tenant 参数可规避,后续版本会优化。 - ---- - -> **问题现象** -> -> 使用 OBD2.4.0 执行 obd mirror clone 导入文件失败,报错: -> `[ERROR] Running Error: 'LocalMirrorRepository' object has no attribute 'self'`。 -> `OSError: [Errno 28] No space left on device` -> -> **可能原因** -> -> 可能是 obd 存放文件的目录快满了,导入文件太大。 -> -> **解决方案** -> -> 手动清理本地文件释放空间。 -> ~/.obd/repository/: 解压组件后存放的位置,可以随便删。 -> ~/.obd/mirror/local/:obd 的本地仓库,建议删除不需要使用的安装包。 - ---- - -## **OceanBase 数据库使用问题** - -> **问题现象** -> -> 手动部署 OceanBase 数据库 V3.1.3,初始化 alter system bootstrap 报错 `ERROR 4015 (HY000): System error`。 -> -> **可能原因** -> -> 资源不足,官方要求最小 2.5 个核心。 -> -> **解决方案** -> -> 调大虚拟机 CPU 核数。 - ---- - -> **问题现象** -> -> 使用 OceanBase 数据库 4.x 版本新导入的数据时,做 count 查询特别慢? -> -> **可能原因** -> -> - OceanBase 数据库 4.x 版本支持自动收集统计信息,如果还未触发自动收集统计,需要手动收集统计。 -> - 数据量大和数据场景复杂的时候,需要手动触发合并会重整数据,加快查询效率。 -> - 查询如果非单纯 count 表,且租户资源不足扩展时,count 性能就会很慢,需要增大 ob_query_timeout 参数防止查询超时中断。 -> -> **解决方案** -> -> - 手动收集统计信息 -> - 触发转储合并 -> - 增加 ob_query_timeout 超时参数 - ---- - -> **问题现象** -> -> OceanBase 数据库 V4.1 创建租户阶段,创建资源时报错 `[1235] [0A000]: (conn=3221487627) unit MEMORY_SIZE less than __min_full_resource_pool_memory not supported`。· -> -> **可能原因** -> -> 该报错主要是设置的 memory_limit 比 **min_full_resource_pool_memory 小导致,**min_full_resource_pool_memory 在 4.x 版本默认是 2G,3.x 版本默认是 5G。 -> -> **解决方案** -> -> 如果 MEMORY_SIZE 设置太小,建议是增大资源单元 unit 的 MEMORY_SIZE 参数大小,命令如下。 -> -> ```sql -> alter resource unit your_unit_name MEMORY_SIZE='2G'; -> ``` -> -> 如果 MEMORY_SIZE 无法再增大了,可以降低最小资源池参数限制 \_\_min_full_resource_pool_memory。可参考示例查看和修改该隐藏参数。 -> -> ```sql -> -- 查看 -> SELECT * FROM oceanbase.__all_virtual_sys_parameter_stat WHERE name='__min_full_resource_pool_memory'; -> -- 修改,示例中 2147483648 为 2G -> alter system __min_full_resource_pool_memory=2147483648; -> ``` - ---- - -> **问题现象** -> -> 使用 OB4.0 版本源码编译再部署时失败 -> 报错:`[ERROR] oceanbase-ce start failed` -> 报错:`ERROR [SHARE] operator() (ob_common_config.cpp:128) [20728][][T0][Y0-0000000000000000-0-0] [lt=5] Invalid config, value out of [1073741824,) (for reference only).name=min_full_resource_pool_memory, value=268435456, ret=-4147` -> -> **可能原因** -> -> \_\_min_full_resource_pool_memory 该参数为 OB 隐藏参数,是允许以最小多少内存的规格创建租户的。不同版本,不同部署方式,该参数均有不同默认设置,OB 默认是 2G 大小。 -> -> **解决方案** -> -> 配置文件中调大**min_full_resource_pool_memory 参数的值 2147483648 -> `alter system set min_full_resource_pool_memory= '2147483648';` -> 隐藏参数查看方式: -> `SELECT \* FROM oceanbase.**all_virtual_sys_parameter_stat WHERE name='\_\_min_full_resource_pool_memory';` - ---- - -> **问题现象** -> -> OceanBase 数据库 V4.0 中使用 select outfile 语法报错权限问题。 -> -> ```sql -> obclient [jydb]> select * into outfile ‘/tmp/a.csv’ from a; -> ERROR 1227 (42501): Access denied -> ``` -> -> **可能原因** -> -> 导出是到本地位置,OBProxy 代理不是本地。 -> -> **解决方案** -> -> 通过直连 OBServer 节点连接集群,不能通过 OBProxy 代理连接集群 - ---- - -> **问题现象** -> -> OceanBase 数据库 V4.0 中插入 1W 条数据,报错 `1203 (42000): Too many sessions`。 -> -> **可能原因** -> -> 连接池需要程序设置回收。OBProxy 自身的故障或者因流量上升,OBProxy 线程用满(报错信息类似 too many sessions)。 -> -> **解决方案** -> -> 应用层使用未配置连接池回收时,可参考官网 OceanBase 数据库文档 [ODP 线程满](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001579566) 增加 proxy 服务线程数 client_max_connections 参数。 -> -> OBProxy 自身故障可参考官网 Oceanbase 数据库文档 [ODP 端故障](https://www.oceanbase.com/docs/enterprise-oceanbase-database-cn-10000000001579572)。 - ---- - -> **问题现象** -> -> 测试环境,内存有限,日志报 1001 租户内存不足 `WARN [COMMON] try_flush_washable_mb (ob_kvcache_store.cpp:630) [52886][T1001_ReplaySrv][T1001][Y0-0000000000000000-0-0] [lt=4] can not find enough memory block to wash(ret=-4273, size_washed=0, size_need_washed=2097152)`。 -> -> **可能原因** -> -> OceanBase 数据库 V4.0 引入了用户的 meta 租户概念,其规格配置不支持用户独立设置,具体的规则详见官网 OceanBase 数据库文档 [租户的资源管理](https://www.oceanbase.com/docs/community-observer-cn-10000000000901425)。 -> -> **解决方案** -> -> 可以通过调整 1001 租户的规格来自动调整对应 meta 租户的规格。 - ---- - -> **问题现象** -> -> 集群中有三个 Zone,停止 Zone 操作失败 `ERROR 4660 (HY000): cannot stop server or stop zone in multiple zones`。 -> -> **可能原因** -> -> 停止 Zone 时需要确保多数派副本均在线,否则操作会失败。 -> -> **解决方案** -> -> 环境是三个 Zone,停止一个 Zone 后,再停止一个是不允许的。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V3.1.4 新建租户后,使用租户信息无法登录集群,报错 `ERROR 1227 (42501): Access denied`。 -> -> **可能原因** -> -> 创建租户时默认密码为空,如果不能登录,一般是未设置白名单,导致无访问权限。 -> -> **解决方案** -> -> - 登录系统租户(root@sys)为新创建的租户设置白名单。 -> -> ```sql -> ALTER TENANT xxx SET VARIABLES ob_tcp_invited_nodes='%'; -> ``` -> -> - 使用 -h127.0.0.1 登录业务租户,执行命令为本租户设置白名单。 -> -> ```sql -> SET GLOBAL ob_tcp_invited_nodes='%'; -> ``` - ---- - -> **问题现象** -> -> 字段为外键时插入数据报错 `1235 - Not supported feature or function`。 -> -> **可能原因** -> -> 使用的是 sys 租户,sys 租户仅做集群管理使用,不可当做业务租户。普通租户下字段为外键时可以正常插入。 -> -> **解决方案** -> -> 新建普通租户进行业务使用。 - ---- - -> **问题现象** -> -> 集群无法连接,observer 进程挂掉,报错 `worker cnt larger than max cnt`。 -> -> **可能原因** -> -> 基本为租户 CPU 资源不足够导致。 -> -> **解决方案** -> -> 调大 sys_cpu_limit_trigger 参数,使其大于 16,或者更大。 - ---- - -> **问题现象** -> -> load data 导入数据报错 `ERROR 1227 (42501): Access denied`。 -> -> **可能原因** -> -> 访问数据文件无权限导致。 -> -> **解决方案** -> -> 将导入的数据文件放到启动 OceanBase 数据库的用户权限下执行,或者执行如下命令。 -> -> ```sql -> set global secure_file_priv = '/tmp’; -> ``` - ---- - -> **问题现象** -> -> 在 Anolis8.6 机器下,部署 OceanBase 数据库 V3.1.4 时,eth0 网卡 ping 服务器正常,但部署报错 ping 失败:eth0 fail to ping xxx.xxx.xxx.xxx. Please check configuration `devname`。 -> -> **可能原因** -> -> 兼容性问题,后续版本修复。 -> -> **解决方案** -> -> 执行 `chmod u+s /usr/sbin/ping` 命令。 - ---- - -> **问题现象** -> -> 升级 OceanBase 数据库 V3.1.0 至 V3.1.4 失败,报错:`fail to get upgrade graph: ‘NoneType’ object has no attribute 'version'`。 -> -> **可能原因** -> -> OceanBase 数据库 V3.1.0 不支持本地升级,只能做数据迁移这种逻辑升级。 -> -> **解决方案** -> -> 重新搭建一套 OceanBase 数据库 V3.1.4 环境,通过 OMS 进行数据迁移。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V3.1.4 中使用业务租户修改 lower_case_table_names 参数报错 `Variable 'lower_case_table_names'is a read only variable`。 -> -> **可能原因** -> -> MySQL 的只读变量在 MySQL 里即使是 root 也不能修改,需要拥有主机访问权限的人在外部修改配置文件后重启实例生效。OceanBase 数据库的 MySQL 租户是逻辑实例,没有独立进程,不存在重启操作。所以需使用有 sys 租户权限的超级管理员去修改租户的全局参数变量。 -> -> **解决方案** -> -> 使用系统租户 root@sys 操作。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V3.1.4 中一次性导入大批数据报错租户内存不足 `ERROR: No memory or reach tenant memory limit`。 -> -> **可能原因** -> -> 有如下两种原因。 -> -> - 系统租户本身作为系统管理者,资源有限,不适合业务使用。 -> - 磁盘写满,无法触发转储合并,租户内存会写满。 -> -> **解决方案** -> -> 根据不同原因有如下几种解决方案。 -> -> - 不能使用系统租户 root@sys 当业务租户使用。 -> - datafile_size 不能太小,防止租户的磁盘被写满。 -> - 通过参数调优,可以降低内存写满风险 -> - 租户内存阈值 memstore_limit_percentage 调大。 -> - 触发版本冻结的阈值 freeze_trigger_percentage 调小。 -> - MemStore 写入限速阈值 writing_throttling_trigger_percentage 设置 70。 - ---- - -> **问题现象** -> -> OBClient(2.x 版本)通过 OBProxy 连接 OceanBase 数据库(V4.0.0)proxysys 租户时报错 `ERROR 2027 (HY000): received malformed packet`,但是使用 MySQL 客户端连接正常。 -> -> **可能原因** -> -> OBClient 2.x 版本使用 root@proxysys 用户连接到 OceanBase 数据库之后,会发送 SQL 语句查询 version,OBProxy 会直接回复 EOF 包,这个包在 OBClient 1.x 版本时是会被忽略,在 OBClient 2.x 版本时会解析异常。 -> -> **解决方案** -> -> 可暂时使用 MySQL 客户端,该问题下个迭代修复。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V4.0.0 中导入表的时候总是报 `ERROR 4263 (HY000): Minor freeze not allowed now`,且无法 truncate 表数据。查看日志显示如下。 -> -> ```shell -> WARN [STORAGE.BLKMGR] alloc_block (ob_block_manager.cpp:305) [9021][T1_MINI_MERGE][T1][YB42C0A81FCA-0005EC7D0FFFD8D6-0-0] [lt=22] Failed to alloc block from io device(ret=-4184) -> WARN [STORAGE] alloc_block (ob_macro_block_writer.cpp:1132) [9021][T1_MINI_MERGE][T1][YB42C0A81FCA-0005EC7D0FFFD8D6-0-0] [lt=4] Fail to pre-alloc block for new macro block(ret=-4184, current_index=0, current_macro_seq=0) -> ``` -> -> **可能原因** -> -> Minor freeze not allowed now 指的是无法进行转储,原因一般有: -> -> - 租户分配磁盘空间占满,内存的数据无法转储。 -> - 使用了系统租户当业务租户使用,系统租户资源较少,转储性能跟不上。 -> - 使用了业务租户,但该租户的内存资源较少,转储性能跟不上。 -> -> **解决方案** -> -> 增大对应租户的磁盘资源,命令如下。 -> -> ```sql -> alter resource unit xxx datafile_size='xxG' -> ``` - ---- - -> **问题现象** -> -> 部署 OceanBase 数据库 V4.0.0 时报错 `[ERROR] OBD-1006: Failed to connect to oceanbase-ce`。 -> 查看 observer.log 日志显示 `ERROR [CLOG] resize (ob_server_log_block_mgr.cpp:183) [2790][][T0][Y0-0000000000000000-0-0] [lt=5] The size of reserved disp space need greater than 1GB!!!(ret=-4007`。 -> -> **可能原因** -> -> log_disk_size 用于设置 Redo 日志磁盘的大小,不能小于 1G 的限制,默认为分配内存的 3~4 倍。 -> -> **解决方案** -> -> 配置文件中调大 log_disk_size 参数。 - ---- - -> **问题现象** -> -> 单机 demo 部署 OceanBase 数据库 V4.0.0 ,通过 OBProxy 连接数据库时经常出现断连现象,但是通过 OBServer 直连却没有问题。报错信息如下。 -> -> ```shell -> ERROR 2013 (HY000): Lost connection to MySQL server during query -> ERROR 2006 (HY000): MySQL server has gone away -> No connection. Trying to reconnect… -> ``` -> -> **可能原因** -> -> 现在 demo 模式中 OBProxy 给的内存比较少(200M),可能有触发内存超限重启的问题,后续版本会优化。 -> -> **解决方案** -> -> 执行如下命令调大 proxy_mem_limited 参数为 500M 或 1G。 -> -> ```sql -> alter proxyconfig set proxy_mem_limited ='1G'; -> ``` - ---- - -> **问题现象** -> -> 通过 OBD 单机部署 OceanBase 数据库 V3.1.4 后启动失败,通过 OBProxy 连接报错 `ERROR 2013 (HY000): Lost connection to MySQL server at 'reading authorization packet', system error: 0`,日志报错如下。 -> -> ```shell -> ERROR [SERVER.OMT] alloc (ob_worker_pool.cpp:93) [24864][454][Y0-0000000000000000] [lt=22] [dc=0] worker cnt larger than max cnt(worker_cnt_=256, max_cnt_=256) -> ``` -> -> **可能原因** -> -> OceanBase 数据库最低以 cpu_count 16 启动,如果修改过该参数可能导致无法启动。 -> -> **解决方案** -> -> 执行 `obd cluster edit-config` 编辑配置文件,调大 cpu_count 配置项,保存修改后根据黑屏输出执行对应命令重启即可。 - ---- - -> **问题现象** -> -> 导入数据到 OceanBase 数据库 V4.0.0 期间出现切主报错信息 `ERROR [ELECT] leader_revoke (ob_election.cpp:2151) [42767][1849][Y0-0000000000000000] [lt=13] [dc=0] leader_revoke, please attention!(revoke reason=“clog sliding_window timeout”` -> -> **可能原因** -> -> Leader 同步 Clog 超时导致切主。 -> -> **解决方案** -> -> 可以尝试调小客户端压力,或者调大 \_ob_clog_timeout_to_force_switch_leader,这是日志同步超时配置项,可以通过 oceanbase.\_\_all_sys_parameter 表查看,默认值为 10 秒,可适当调大,影响是 leader 异常时,相应的 RTO 也会变长。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V3.1.0 申请不到内存,合并超时,转储卡住。日志报错:`Fail to alloc data block, (ret=-9202)`。 -> -> **可能原因** -> -> 有如下两种可能原因。 -> -> - 可能是磁盘被占满,可以通过 `df -h` 查看。 -> - 可能是设置的 datafile 使用完了,可以通过查询 \_\_all_virtual_disk_stat 查看 OceanBase 数据库整体磁盘占用信息。 -> -> **解决方案** -> -> 可通过扩容解决,或者增大 datafile_size / datafile_disk_percentage 配置。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V3.1.4 执行合并报错:`ERROR 4179 (HY000): Operation not allowed now`,rootserver 日志报错:`enable_major_freeze is off, refuse to to major_freeze`。 -> -> **可能原因** -> -> enable_major_freeze 默认是开启的,测试环境修改了参数,导致无法手动触发合并。 -> -> **解决方案** -> -> 执行 `alter system set enable_major_freeze='true';` 打开合并参数。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V4.1 中 sys 租户设置数据备份路径报错。 -> -> - 执行 SQL: `ALTER SYSTEM SET data_backup_dest=‘file:///data/bak’;`。 -> - 报错:`ERROR 1235 (0A000): Not supported feature or function`。 -> - 日志信息:`WARN log_user_error_inner (ob_table_modify_op.cpp:1042) [7800][EvtHisUpdTask][T1][YB42ACAC1F54-0005F645ABA8855E-0-0] [lt=18] Data too long for column ‘value2’ at row 1`。 -> -> **可能原因** -> -> OceanBase 数据库 4.x 版本为租户级别备份,且 sys 租户不能为其自身备份,sys 租户登录操作的话,需要指定备份的租户,其中 log_archieve_dest/data_backup_dest 也只能为业务租户备份设置。 -> -> **解决方案** -> -> 设置时指定为业务租户:`ALTER SYSTEM SET data_backup_dest=‘file:///data/bak/’ TEANT = $your_tenant_name;`。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V4.1 后台设置日志归档备份路径报错。 -> -> - 执行 SQL: `ALTERSYSTEMSET LOG_ARCHIVE_DEST='LOCATION=file:///data/bak' TENANT = fund;`。 -> - 报错:`ERROR 9081 (HY000): the content of the format file at the destination does notmatch`。 -> -> **可能原因** -> -> OceanBase 数据库 4.x 版本,日志备份目录 log_archive_dest 和数据目录 data_backup_dest 不能在同级目录下。因为每一个路径下都有一个 format 校验文件,该文件记录该路径的一些相关属性,设计上不允许备份目录相同。 -> -> **解决方案** -> -> 将日志备份目录和数据目录设置到不同目录层级下,例如:`ALTER SYSTEM SET data_backup_dest='file:///data/bak/logdir';`。 - ---- - -> **问题现象** -> -> OCP 备份和手动发起 ALTER SYSTEM ARCHIVELOG 命令报错 `ERROR 1235 (0A000): start log archive backup when not STOP is not supported` -> -> **可能原因** -> -> 日志备份进程仍在,不能重复执行备份。 -> -> **解决方案** -> -> 如果是初次全量备份,可以关停备份并清理备份任务和进程,重新发起。 -> -> ```sql -> --关闭日志备份 -> ALTER SYSTEM NOARCHIVELOG; -> --强制取消所有备份任务(备份目录会重置) -> ALTER SYSTEM CANCEL ALL BACKUP FORCE; -> --查看备份路径 -> SHOW PARAMETERS LIKE 'backup_dest' -> --修改备份目录 -> ALTER SYSTEM SET backup_dest='file:///data/obbackup'; -> --启动日志备份 -> ALTER SYSTEM ARCHIVELOG -> ``` - ---- - -> **问题现象** -> -> OceanBase 数据库 V3.1.4 启动日志备份后,很快就显示断流 interrupted。查看日志显示 `log archive status is interrupted, need manual process(sys_info={status:{tenant_id:1, copy_id:0, start_ts:1671070878595931, checkpoint_ts:1671070878595931, status:5, incarnation:1, round:3, status_str:“INTERRUPTED”, is_mark_deleted:false, is_mount_file_created:true, compatible:1, backup_piece_id:0, start_piece_id:0}, backup_dest:“file:///home/nfs_server/backup”})`。 -> -> **可能原因** -> -> 有如下两种可能原因。 -> -> - nfs 客户端配置错误。 -> - nfs 服务器目录权限不够。 -> -> **解决方案** -> -> 根据以上原因,有如下两种解决办法。 -> -> - OceanBase 数据库为 NFS 客户端,需要所有 OBServer 节点本地创建备份目录挂载到服务端的 NFS 地址。 -> - 将 NFS 服务器上 backup 目录修改属组 nfsnobody:nfsnobody,或者 777 权限。 - ---- - -> **问题现象** -> -> 用 OBD 管理集群时报错 `OBD-1006: Failed to connect to oceanbase-ce`,实际集群状态正常,OBD 日志报错密码不正确 `Access denied for user`。 -> ![image.png](/img/FAQ/all_faq/1668842141080-38f78b3b-ce4d-4359-b57d-b6ef1efbb0de.png) -> -> **可能原因** -> -> 用户未通过 OBD 方式修改密码,而是直接登录数据库修改,但 OBD 配置文件不会同步修改,导致密码配置不一致,且当前情况下因为密码已经不一致,无法再通过 OBD 修改密码。 -> -> **解决方案** -> -> 登录数据库设置系统租户密码和 OBD 配置文件中 `root_password` 配置项保持一致,此时可以成功通过 OBD 管理集群。若仍想修改密码,可执行 `obd cluster edit-config` 命令编辑配置文件,修改 `root_password` 配置项为想要修改的密码。 - ---- - -> **问题现象** -> -> ODC 上 执行 SQL 报错 `Unkown thread id`。 -> ![image.png](/img/FAQ/all_faq/1669539589485-040e0a5f-16f6-4bff-85e8-2b5d3589924a.png) -> -> **可能原因** -> -> unknow thread id 是因为在 JDBC 代码执行 SQL 时,设置了 ob_query_timeout,超时后驱动就会执行 `kill query connectionId` 命令将超时执行的 SQL 取消掉。但是这个命令在存在多个 OBProxy 时,可能会发给其他的 OBProxy,就会报错 unknow thread id。 -> -> **解决方案** -> -> 增大超时参数 ob_query_timeout,同时也建议增大事务参数 ob_trx_timeout、ob_trx_idle_timeout。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V3.1.4 动修改 observer.config.bin 文件后启动失败,报错 `check data checksum failed(ret=-4103)`。 -> ![image.png](/img/FAQ/all_faq/1670657367060-af9a5525-b9d6-448f-bdb8-a94e93f3ae9d.png) -> -> **可能原因** -> -> 首先不支持直接手动修改该二进制文件,该文件配置参数是通过 alter system 方式持久化此处的,可以通过 `./bin/observer -o 参数=参数值` 的方式启动,启动成功后也会持久化到该配置,当然。 -> -> **解决方案** -> -> etc2 和 etc3 下有备份文件,该配置文件加上 history 一共有六份,可以 cp etc2/observer.conf.bin etc/observer.config.bin 恢复配置。 -> -> ```shell -> find /home/admin/oceanbase |grep "observer.conf" -> /home/admin/oceanbase/etc3/observer.conf.bin -> /home/admin/oceanbase/etc3/observer.conf.bin.history -> /home/admin/oceanbase/etc2/observer.conf.bin -> /home/admin/oceanbase/etc2/observer.conf.bin.history -> /home/admin/oceanbase/etc/observer.config.bin -> /home/admin/oceanbase/etc/observer.config.bin.history -> ``` - ---- - -> **问题现象** -> -> springboot 项目使用 OceanBase 数据库 V4.0.0,数据库中字段是 datetime 格式,用 bean 接收 SQL 查询的结果,bean 中使用 string 来接收,出现`2022-12-08 12:23:56.0`,预期结果是 `2022-12-08 12:23:56`。 -> -> **解决方案** -> -> 对于 OceanBase 数据库的 datatime 格式,建议使用 Date 的数据类型接收。如果想要用 String 接收 OceanBase 数据库的时间类型,建议尝试将数据库改为 timestamp 格式。 - ---- - -> **问题现象** -> -> OceanBase 数据库 V4.0.0 在给日期类型 datetime 的字段按年分区时报错 `ERROR 1564:This partition function is not allowed`。 -> ![image.png](/img/FAQ/all_faq/1685863518267-caae7175-4423-4415-a508-53517fac19e3.png) -> -> **可能原因** -> -> 原生 MySQL 也如此表现,报错符合预期,Hash 分区键的表达式必须返回 INT 类型,left 截取方式返回的是字符串类型。 -> -> **解决方案** -> -> 可以按如下函数语法获取年/月。 -> -> - PARTITION BY RANGE( YEAR(dt) ) -> - SUBPARTITION BY HASH( MONTH(dt) ) - ---- - -> **问题现象** -> -> OceanBase 数据库 V4.0.0 在 write only 场景时,磁盘读在一个较高的频率,纯写场景为什么会有这个高的读操作? -> ![image.png](/img/FAQ/all_faq/1685871560930-10712934-79b6-4810-abc0-546f39cf2899.png) -> -> **可能原因** -> -> OceanBase 数据库的存储整体是一个 LSM 架构,从上到下是 MemTable,Minor SSTable,Major SSTable,数据从 MemTable 到 SSTable 会写盘,从 Minor 到 Minor/Major 会读盘 + 写盘,Minor SSTable 累积一定数量后也会触发合并,合并需要重排列,需要读+写磁盘。 -> -> **解决方案** -> -> 符合预期,无需解决。 - ---- - -> **问题现象** -> -> 500 租户内存超限,500 租户内存使用过多会导致机器内存资源耗尽,OBServer 内的租户会因为内存不足而无法正常工作。 -> -> **可能原因** -> -> 先看下 500 租户内存占用模块大小信息: -> `select * from __all_virtual_memory_info where tenant_id='500' order by hold desc limit 10;` -> 开启内存泄露监控 -> `alter system set leak_mod_to_check= '$mod_name';` -> 查询有明显的增长时停止 -> `select * from __all_virtual_mem_leak_checker_info order by alloc_count;` -> 然后及时关闭内存泄露监控 -> `alter system set leak_mod_to_check= ''`; -> 查看 back_trace -> `SELECT * FROM __all_virtual_malloc_sample_info WHERE mod_name='IlogMemstoCurso' ORDER BY alloc_bytes DESC limit 10;` -> 根据 addr2line 解析对应的 back_trace -> `addr2line -pCfe bin/observer 0x9a98f75 0x984dddc 0x9847352 0x22e173d 0x22e2886 0x22e3025 0x7c5931e 0x7c58854 0x7c58469 0x7c0b548 0x7b6c3aa 0x7b2d630 0x7b2bea9 0x7f91517 0x7f91ddc 0x7f92c71 0x84b8115 0x7c94650 0x7c9e717 0x7c9d391 0x3432bfa 0x3436070 0x340b9af 0x2cabf02 0x9820da5 0x981f792 0x981c24f ` -> -> **解决方案** -> -> 如果没有 addr2line 命令,yum install -y binutils 进行安装如果无法分析堆栈,可以开源问答区发帖,社区值班开发会协助分析。 -> 应急处理:重启告警对应节点 observer 服务,但可能无法分析问题原因。 - ---- - -> **问题现象** -> -> OB4.x 版本建表时提示 Caused by: java.sql.SQLException: Row size too large -> -> **可能原因** -> -> OB 限制单表行长度 1.5M,指的是所有字段类型长度加在一起不超过 1.5M。 -> -> **解决方案** -> -> 使用其他字符集或使用其他数据类型,将大字段或长字段类型适当调整为其他可替代类型或长度。 - ---- - -> **问题现象** -> -> oceanbase4.1 版本扩容节点后性能未提升? -> -> **可能原因** -> -> 可以查看表数据分布情况,因为 OB4.1 不支持扩容前的数据均衡,即历史数据无法负载到所有节点,4.2 版本开始支持。 -> `select svr_ip,count(*) from dba_ob_table_locations where database_name='xx' group by svr_ip;` -> -> **解决方案** -> -> 升级到 OB4.2 版本或最新版本,或者重新部署最新版本集群。 - ---- - -> **问题现象** -> -> OCP 告警 OceanBase 归档日志备份延迟,`tailf observer.log |grep "get ls arcchive progress failed" ` 或者 `"failed to stat file"`所有租户的 1 号日志流都报错 ret=-4023。 -> -> **可能原因** -> -> 这种所有租户日志流失败基本可以定位是全局问题,涉及备份的全局场景要么是集群故障,或者是 nfs 服务有问题。 -> -> **解决方案** -> -> 排查 NFS 挂载信息。例如文件目录访问权限、用户组权限、NFS 连通性等。 -> 该案例排查比较特殊,observer 的进程用户 admin 的 uid 被修改了,导致部分文件和资源无法访问。 -> 该案例恢复方式:`usermod -u 1001 admin` #1001 根据实际的 uid 填写。 - ---- - -> **问题现象** -> -> OB4.2 版本创建表时报错:`ERROR 1499 (HY000): Too many partitions (including subpartitions) were defined` -> -> **可能原因** -> -> 1. 内存不足以支持更多表; -> 2. 创建的单分区表超过 8192 个分区; -> 3. 如果是开启了回收站,大量废弃的表未即使清理也会占用分区数; -> -> **解决方案** -> -> 1. OB4.x 版本,1G 内存约支持 2 万 tablet,如果内存太小,可以尝试扩大租户内存来支撑更多表分区。 -> 2. OB 单分区表限制,单分区表不能超过 8192 个子分区。 -> 3. 需要清理回收站。 - ---- - -> **问题现象** -> -> OB3.1.4 版本的 sql 中条件 in 超过 1000 个参数后执行报错:`Size overflow`。 -> -> **可能原因** -> -> 大 in 场景,该版本存在优化问题。 -> -> **解决方案** -> -> 1. 减少 in 的个数 -> 2. 或者调整参数 stack_size 为 15M,同时调大 system_memory=system_memory+OBserver 线程数\*stack_size。适用于 OB3.x 版本环境。 - ---- - -> **问题现象** -> -> OB3.1.5 版本在执行 alter system restore 做数据恢复时报错:`ERROR 9011(HY000): cannot find backup file`。 -> -> **可能原因** -> -> 该报错基本是恢复 sql 语句 at 和 with 中的参数和实际配置不符合导致。 -> -> **解决方案** -> -> 该案例是 with 参数中的集群名称填写错误导致。 -> 集群名称查看:`show parameters like '%cluster%';` 返回的 value 字段对应。 - ---- - -> **问题现象** -> -> OB4.1 版本连接业务租户长时间等待,无法登录进去。 -> observer.log 报错: -> `WDIAG [COMMON] wait (ob_io_define.cpp:859) [14132][][T500][Y0-0000000000000000-0-0] [lt=21][errcode=-4224] IO error, (ret=-4224 ` -> `WDIAG [COMMON] read (ob_io_manager.cpp:159) [14132][][T500][Y0-0000000000000000-0-0] [lt=46][errcode=-4224] io handle wait failed(ret=-4224 ` -> -> **可能原因** -> -> 通过日志报错解析出现了磁盘 IO 等待问题,如果磁盘 IO 读写性能变慢或者读写卡顿会出现该问题现象。 -> -> - 磁盘硬件可能有故障; -> - 磁盘 IO 可能被占满,比如:使用性能较低的机械盘,或者数据和日志同盘,或者业务不合理占用大量磁盘 IO 等。 -> -> **解决方案** -> -> - 该案例是磁盘故障,压缩和解压文件时会出现长时间卡住无法结束,需要替换磁盘。 -> 磁盘故障可以使用 smart 命令检查或者查看/var/log/messages 系统日志。 -> - 磁盘 IO 可以通过`iostat -x 1`命令检查磁盘 IO 读写或者 IO 等待。如果是生产环境,存在机械盘或者同盘问题,强烈建议更换 SSD 磁盘且数据和日志分盘部署。 - ---- - -> **问题现象** -> -> OB4.1 版本调整租户 locality 时,提示:`operation not allowed now` -> observer.log 报错:`WARN [RS] process_ (ob_rs_rpc_processor.h:117) [5653][DDLQueueTh0][T0][YB420A581A34-0005F940407C5D80-0-0] [lt=1] ddl operation not allow, can not process this request(ret=-4179, pcode=528)` -> -> **可能原因** -> -> `operation not allowed now`比较宽泛,需要通过报错日志查看内核代码(ob*rs_rpc_processor.h:117),对应版本和行号代码(!GCONF.enable_ddl && !is_allow_when_disable_ddl(pcode, ddl_arg*) ,可以判断和 enable_ddl 参数相关。 -> -> **解决方案** -> -> 该案例 enable_ddl 被修改为 false,将数据库系统参数 enable_ddl 设置为 true 即可。该参数被修改大概率是升级 OB 失败导致,建议用户将 OB 升级完成,不建议跳过任务。 - ---- - -> **问题现象** -> -> OB3.1.4 节点宕机后重新上线后 unit 不自动负载了,observer.log 报错有关键字:`ld tenant can't be dropped`。 -> -> **可能原因** -> -> 有 unit 的 GC 无法完成。当一个 zone 内有节点汇报给 RS 的资源占用与实际不符时(包括未 GC 的 unit),则此时该 zone 不会执行负载均衡,unit 也无法 GC。后续 OB3.1.5 版本会优化修复。 -> -> **解决方案** -> -> 需要重启 observer.log 打印`old tenant can't be dropped`关键字的节点 ob 服务。 - ---- - -> **问题现象** -> -> OB4.2.0 版本主备租户场景,执行租户升级`ALTER SYSTEM RUN UPGRADE JOB "UPGRADE_ALL"`版本未统一,重复执行报错:`[4179][HY000]: Operation not allowed now`。 -> -> **可能原因** -> -> 主备租户集群需要保持版本一致,纯主集群和纯备集群升级顺序:升级备集群,再升级主集群,两边的租户版本会保持一致,无需再执行升级租户命令。 -> -> **解决方案** -> -> 主租户集群也升级至同版本即可。 - ---- - -> **问题现象** -> -> OB4.2.1 初始化失败,observer.log 报错: -> `Unexpected internal error happen, please checkout the internal errcode(errcode=-4013, file="protected_stack_allocator.cpp", line_no=85, info="alloc failed")` -> -> **可能原因** -> -> 启动环境中 stack_size 值被修改,默认参数 512k,导致申请线程栈失败,该参数不建议用户调整。 -> -> **解决方案** -> -> 手动指定参数启动 `./bin/observer -o "stack_size=512k" ` - ---- - -> **问题现象** -> -> OB4.1.1 版本,磁盘未占满,但磁盘 IO 繁忙,OCP 告警:`Server out of disk space(msg="disk is almost full", ret=-4184`。observer.log 有相关报错: -> `fail to check space full(ret=-4184)` -> `fail to write tmp block(ret=-4184)` -> -> **可能原因** -> -> OB420 版本修复磁盘负载过高时,临时文件 I/O 异常可能导致报错 4184 磁盘满问题。 -> -> **解决方案** -> -> 升级 OB420 版本或以上版本即可。 - ---- - -> **问题现象** -> -> obproxy 访问 OB4.2.1 时,提示`ERROR 4669 (HY000):cluster not exists`。 -> -> **可能原因** -> -> - `show parameters like '%cluster%';` value 字段对应的是正确的集群名称; -> - 如果集群名称正确的可能是配置了 obproxy_config_server_url 参数,该参数优先级高于 rootservice_list。 -> -> **解决方案** -> -> - 使用 value 字段对应的集群名称即可; -> - 如果配置了 obproxy_config_server_url 且和实际集群名称不一致,可以把参数置空,`alter proxyconfig set obproxy_config_server_url='';` - ---- - -> **问题现象** -> -> OB4.2.1 租户合并失败,observer.log 报错:`failed to merge partition(ret=-4184)` -> -> **可能原因** -> -> 可能是剩余磁盘空间不足,用户业务数据占用接近阀值 90%时,导致合并刷宏块时,磁盘 free block 容量不足,无法触发合并。 -> -> **解决方案** -> -> 扩数据磁盘容量。 - ---- - -> **问题现象** -> -> OB4.2.1 做 sysbench 压测报错 lost connection。 -> 屏幕打印:`SQL error,errno = 2013,state = 'HY000' : Lost connection to server during query` -> obproxy.log 报错:`obproxy's memory is out of limit,will be going to commit suicide` -> -> **可能原因** -> -> proxy_mem_limited 太小,压测期间导致 obproxy 内存太小出现断连问题。 -> -> **解决方案** -> -> 增大 obproxy 内存,proxy_mem_limited 参数,默认值为 2G。 - ---- - -> **问题现象** -> -> OB4.2.1 执行 sql 报错磁盘故障,`ERROR 4392 (HY000) : disk is hung`。 -> -> **可能原因** -> -> 磁盘性能不足会导致读写占用大量 IO,出现等待,超出 data_storage_warning_tolerance_time,log_storage_warning_tolerance_time 默认时间,会判定为磁盘故障。 -> -> **解决方案** -> -> - 更换 SSD 或者高性能磁盘。 -> - 如果确定磁盘性能正常,可以适当调大 log_storage_warning_tolerance_time 或者 data_storage_warning_tolerance_time 参数,默认 5s。 - ---- - -> **问题现象** -> -> OB4.2.1 版本表结构存在生成列,load data 导入数据报错`ERROR 1048 (23000): Column cannot be null`。 -> -> **可能原因** -> -> 报错指的是生成列不能为空,当存在有生成列的情况下,需要指定非生成列字段名导入。 -> -> **解决方案** -> -> `load data infile '$path' into table $tableName fields terminated by ',' (field1,field2,field3,field4);`插入的字段不指定生成列字段。 - ---- - -> **问题现象** -> -> OB4.2.1 的收站中过期数据未自动清理,设置了很小的保留时间也未触发清理。 -> -> **可能原因** -> -> 控制回收站清理的除了过期时间参数 recyclebin_object_expire_time,还有回收频率的隐藏参数\_recyclebin_object_purge_frequency,默认 10 分钟。 -> -> **解决方案** -> -> 等待 10 分钟或者设置隐藏参数的值 `SELECT * FROM oceanbase.__all_virtual_sys_parameter_stat WHERE name='_recyclebin_object_purge_frequency';` - ---- - -> **问题现象** -> -> OB4.1.0 测试回收站手动清理时报错:`4179 Operation not allowed now`。 -> -> **可能原因** -> -> 在 sys 租户下进行回收站操作了,sys 租户作为管理租户存在一些操作和功能限制。 -> -> **解决方案** -> -> 回收站操作在普通租户中操作,sys 租户不建议使用。 - ---- - -> **问题现象** -> -> 做了磁盘阵列 reid0 的 OB4.1.0 的 observer.log 有大量报错:`Data checksum error(msg="log checksum error", ret=-4103` -> -> **可能原因** -> -> 已经和 intel 官方确认为磁盘阵列卡中的 bug。使用 write through, 数据直接写到磁盘, 读取时也会从磁盘上读取, 解决了 cache 脏数据的问题。 -> -> **解决方案** -> -> 磁盘阵列 cache 设置为 write through。 - ---- - -> **问题现象** -> -> docker 环境安装 OB,只能使用 localhost 进行链接,使用 IP 地址连接不上,报错:`2013 - Lost connection to server at 'handshake:reading initial communication packet',system error:0。 ` -> -> **可能原因** -> -> 宿主机上 OB 的 2881 和 2882 端口被占用。 -> -> **解决方案** -> -> 杀掉占用端口的进程。 - ---- - -> **问题现象** -> -> 使用 mybatis-plus-boot-starter 3.4.2 版本,批量删除和插入数据到 OB4.2,出现删除和插入不完整现象。 -> -> **可能原因** -> -> mybatis-plus-boot-starter 3.4.2 版本,取出来的数据 id 没有顺序,导致后面的逻辑错误。 -> -> **解决方案** -> -> mybatis-plus-boot-starter 3.5.3 版本修复。 - ---- - -> **问题现象** -> -> 使用 mybatis-plus-boot-starter 3.4.2 版本,做关联查询时结果集是无序的。 -> -> **可能原因** -> -> mybatis-plus-boot-starter 3.4.2 版本,取出来的数据 id 没有顺序,导致后面的逻辑错误。 -> -> **解决方案** -> -> mybatis-plus-boot-starter 3.5.3 版本修复。 - ---- - -> **问题现象** -> -> OB4.1.0 设置了 max_syslog_file_count 参数后,系统日志未自动删除,重启 OB 也未清理。 -> -> **可能原因** -> -> enable_syslog_recycle 自动回收参数未打开。 -> -> **解决方案** -> -> 打开该参数,`ALTER SYSTEM SET enable_syslog_recycle='True';` - ---- - -> **问题现象** -> -> TPCC 测试 OB4.2 版本时,terminals 设置大测试会失败。obproxy.log 报错:`failed,-4124和failed to accept con(ret=-10024)` -> -> **可能原因** -> -> accept 系统调用时报错了:EMFILE,打开的 file 太多了,猜测是系统 fd 不够用了,需要检查 ulimit -a 打开文件数配置。 -> -> **解决方案** -> -> 设置 OB 和 OBProxy 节点的最大文件数 open files,建议 OB 的部署用户文件数设置为 655350。 - ---- - -> **问题现象** -> -> miniOB 在 mac M1 芯片编译报错:`CMake Error at /usr/local/lib/cmake/libevent/LibeventConfig.cmark:172 (message):Can not find any libraries for libevent`。 -> -> **可能原因** -> -> 使用 brew 安装的 libevent 时没有生成 cmake 文件。 -> -> **解决方案** -> -> - 正确的安装 libevent 方式如下: -> 1. `git clone https://github.com/libevent/libevent.git` -> 2. `mkdir build && cd build` -> 3. `cmake ..` -> 4. `make` -> 5. `make install` -> -> make install 后出现以下内容即为成功。 -> \-\- Installing: /usr/local/lib/cmake/libevent/LibeventConfig.cmake -> \-\- Installing: /usr/local/lib/cmake/libevent/ LibeventConfigVersion.cmake -> \-\- Installing: /usr/local/lib/cmake/libevent/LibeventTargets-static.cmake -> \-\- Installing: /usr/local/lib/cmake/libevent/LibeventTargets-static-release.cmake -> \-\- Installing: /usr/local/lib/cmake/libevent/LibeventTargets-shared.cmake -> \-\- Installing: /usr/local/lib/cmake/libevent/LibeventTargets-shared-release.cmake - ---- - -> **问题现象** -> -> JDBC 批量(一次一万条)插入 OB4.1 版本时报错: -> `com.oceanbase.jdbc.internal.util.exceptions.MaxAllowedPacketException:query size (4194308) is >= to max_allowed_packet (4194304)` -> 设置 max_allowed_packet 参数后,报错: -> `java.sql.SQLNonTransientConnectionException: (conn=2388917) packet sequence mismatch, expected obSeqNo=3, but received obSeqNo=1 at com.oceanbase.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:122) ~[oceanbase-client-2.4.3.jar:?]` -> -> **可能原因** -> -> - max_allowed_packet 报错比较明显,需要调整 max_allowed_packet 参数,可以按 5-10 倍增加。 -> - 和 OB 的驱动有关系,可以调整 jdbc 参数观察下。 -> -> **解决方案** -> -> - `SET GLOBAL max_allowed_packet= '';` -- 根据情况定义,单位为 byte。 -> - 两种方法: -> 1. jdbc 链接增加 useOceanBaseProtocolV20=false -- 不使用 ob2.0 协议。 -> 2. jdbc 链接增加 useServerPrepStmts=true -- 使用服务器 prepare。 - ---- - -> **问题现象** -> -> OB4.2.1 版本 1-1 架构扩容成 2-2 后,租户已经设置 unit_num=2,但新插入的数据不会自动负载到新节点。 -> -> **可能原因** -> -> 需要扩容节点版本和原集群版本保持一致。 -> -> **解决方案** -> -> 扩容节点和原集群版本需要一样,可以整体升级或者缩容重新扩容相同版本。 - ---- - -> **问题现象** -> -> OB4.2.1 使用腾讯 COS 进行做备份时,归档备份报错:`ERROR 9060 (HY000): COS error`。 -> `grep "list_objects.*ob_cos_wrapper.cpp" rootservice.log `和 observer.log: -> status->error_code: InvalidRegionName -> status->error_msg: The specified region is invalid or unreachable. -> -> **可能原因** -> -> COS 使用姿势的问题,可能 host 设置错了或者不可访问。 -> -> **解决方案** -> -> host=obbackup-xxxxxx.cos.xxxx.com 改为 host=cos.xxxx.com 路径格式不正确。 - ---- - -> **问题现象** -> -> obcdc 连接 OB4.2 时报时区不正确:`fail to get get_tz_info_by_name(tz_name=Zulu, ret=-4018)`。 -> observer.log 报错: -> `Unexpected internal error happen, please checkout the internal errcode(errcode=-5192, file="ob_log_timezone_info_getter.cpp", line_no=65, info="tz_info_wrap set_time_zone failed")`。 -> `[errcode=-5192] tz_info_wrap set_time_zone failed(ret=-5192, ret="OB_ERR_UNKNOWN_TIME_ZONE", timezone_str=Zulu`。 -> -> **可能原因** -> -> obcdc 配置的时区是 Zulu -> `config.add("timezone","Zulu")`。 -> -> **解决方案** -> -> timezone 支持 `'+08:00'` 的 offset 形式和 Asia/Shanghai 的地域形式取值,但不支持直接配置 Zulu 方式,可以改写成 `config.add("timezone","+00:00:00")`。 - ---- - -> **问题现象** -> -> OB4.x 迁移完后数据存储空间出现膨胀,比实际数据占用空间要大很多。 -> -> **可能原因** -> -> OceanBase 存储出现空洞的原因:OceanBase 的数据文件 SSTABLE 按照主键顺序进行存储,如果业务数据插入比较离散,期间有合并时,2M 宏块出现分裂会导致数据空洞率提升,进而导致存储空间大于数据数据空间, 这种现象多见于业务主键非递增插入的场景。 -> -> **解决方案** -> -> 对空洞较大的表强制执行全量合并。 -> 强制执行全量合并,不执行渐进合并。 -> 对于新建表:`set default_progressive_merge_num=1`。 -> 对于现存表:`ALTER TABLE $table SET progressive_merge_num=1;` 这样把需要的表设置上,再进行合并。 -> 注意:全量合并会消耗大量资源,需要设置完之后再设置回 0。 - ---- - -> **问题现象** -> -> OB4.2 基于日志归档的主备租户同步,延迟 2 分钟。 -> -> **可能原因** -> -> 记录租户数据变化的是这个/home/admin/back/piece_d1001r1p1/logstream_1001/log/下的 .obarc 文件,并非是只有业务数据的变化。日志归档备份默认 120s 提交一次,控制参数 archive_lag_target。 -> -> **解决方案** -> -> 可以调整 archive_lag_target 参数增加同步频率。 - ---- - -> **问题现象** -> -> 服务器重启后,OB4.x 启动失败。 -> observer.log 报错: -> `EDIAG [CLOG] renameat_until_success_ (ob_server_log_block_mgr.cpp:1375) [16589][T1007_L0_G0][T1007][YB4A0A000407-00060B310E94B152-0-0] [lt=6][errcode=-9100] ::renameat failed(ret=-9100, this={dir::"/home/admin/oceanbase/store/clog/log_pool",` -> `ERROR issue_dba_error (ob_log.cpp:1866) [2582][T1001_IOWorker][T1001][Y0-0000000000000000-0-0] [lt=12][errcode=-4388] Unexpected internal error happen, please checkout the internal errcode(errcode=-9100, file="ob_server_log_block_mgr.cpp", line_no=1375, info="::renameat failed")` -> -> **可能原因** -> -> 错误码:-9100,是存储层找不到对应的目录或者文件,结合服务器重启,可能是: -> -> - 磁盘挂载异常,例如数据或日志盘未挂载上。 -> - 存储层目录设置文件系统是临时文件格式,例如存放到/tmp 目录下。 -> - 有人为删除过存储层文件等。 -> -> **解决方案** -> -> - 检查挂载信息,或手动 mkdir 测试创建目录是否有权限相关问题。 -> - OB 推荐使用 ext4 或 XFS 文件系统。 -> - OB 数据存储目录文件不能删除,删除无法恢复,只能重装该节点 ob 服务。 - ---- - -> **问题现象** -> -> OB4.2 查询归档历史 CDB_OB_ARCHIVELOG_SUMMARY 视图,部分租户只有一条记录,且 ROUND_ID=1,有些租户多个 ROUND_ID。 -> -> **可能原因** -> -> 租户有多个 round_id 是出现过日志断流或者日志归档启停,每次日志归档重新启动会生成性的 round_id。 -> -> **解决方案** -> -> 并不影响使用,只要日志归档状态是 doing,即正常状态。 - ---- - -> **问题现象** -> -> OCP4.0.3 上传软件包失败,搭建有 nginx 转发 OCP 地址,nginx 有报错:`client intended to send too large body: 89964166 bytes`。 -> -> **可能原因** -> -> 可能是 nginx 的 client_max_body_size 参数限制导致。 -> -> **解决方案** -> -> 可调整 nginx 参数 client_max_body_size 1024M; 和 proxy_read_timeout 300s; - ---- - -> **问题现象** -> -> OB4.2 备租户恢复报错:`Error 4018: No enough log for restore`。 -> -> **可能原因** -> -> 恢复的归档时间参数超出备份时间范围。 -> -> **解决方案** -> -> 1. `select * from CDB_OB_BACKUP_SET_FILES where tenant_id=xxx;` -> 2. `select * from CDB_OB_ARCHIVELOG where tenant_id=xxx;` -> 3. `select * from CDB_OB_RESTORE_HISTORY where tenant_id=xxx;` -> -> 需要满足: -> restore_scn `>=` 备份集的 min_restore_scn -> restore_scn `<=` 日志归档的 checkpoint_scn - ---- - -> **问题现象** -> -> OB4.2 使用带参数的存储过程,首次调用慢。 -> -> **可能原因** -> -> 可能是执行计划缓存被淘汰了。 -> -> **解决方案** -> -> 可以调大租户的 ob_plan_cache_percentage 参数,增加执行计划缓存空间。 - ---- - -> **问题现象** -> -> OB4.1 合并卡住,observer.log 报错: -> `ERROR try_recycle_blocks (palf_env_impl.cpp:692) [1042160][T1001_PalfGC][T1001][Y0-0000000000000000-0-0] [lt=16][errcode=-4264] Log out of disk space(msg=“log disk space is almost full”, ret=-4264` -> -> **可能原因** -> -> 看报错是日志磁盘满,导致回放卡住后无法合并。 -> -> **解决方案** -> -> 可以增大 log_disk_size 或者重启该节点 ob 服务。但该案例引发 clog 磁盘满的是根因是磁盘设置 raid 缓存模式是 write back,此为 intel 的 cache bug,需要设置为 write through 规避。 - ---- - -> **问题现象** -> -> 部署 OB4.2.0 失败,observer.log 报错: -> `[errcode=-4388] Unexpected internal error happen, please checkout the internal errcode(errcode=-4009, file="main.cpp", line_no=588, info="observer start fail")`。 -> -> **可能原因** -> -> 报错码:-4009,日志中还有报错:`convert sys errno(ret=-4009, errno=27, errmsg="File too large")`,和磁盘申请空间相关,用户使用 OBD 白屏部署时使用最大使用模式部署,由于磁盘规格过大,一次性申请到的磁盘资源超出内存数倍规格发生报错。 -> -> **解决方案** -> -> 部署时指定 datafile_size 参数大小,避免一次性申请太大,部署完成后,可以再调大该参数。 - ---- - -> **问题现象** -> -> .NET framework 框架连接 OB4.2 版本失败,使用 mysql-for-visualstudio-2.0.5 驱动,报错: -> `using method 'mysql_native_password' failed with message:Reading from the stream has failed`。 -> -> **可能原因** -> -> 已知的驱动问题。 -> -> **解决方案** -> -> 1. 尝试下使用 MySql.Data.MySqlClient .Net Core Class Library 8.0.22 版本来规避。 -> 2. 或者使用第三方 MySqlConnector 驱动来连接即可。官网:`https://mysqlconnector.net`,代码中直接在 nuget 里加载组件,把原来的 Mysql.Data 卸载掉。 - ---- - -> **问题现象** -> -> OB4.2.0 创建用户的语法报错:`near 'mysql_native_password'`。 -> -> **可能原因** -> -> OB 暂不支持 mysql_native_password 语法。 -> -> **解决方案** -> -> 建议手动修改创建语句进行用户创建。 - ---- - -> **问题现象** -> -> OB3.1.5 扩容新节点失败,observer.log 报错: -> `ERROR [LIB] start (thread.cpp:107) [357097][0][Y0-0000000000000000] [lt=14] [dc=0] pthread create failed(pret=11, errno=11)`。 -> -> **可能原因** -> -> 创建线程失败,可能和用户进程数限制相关。使用 observer 的进程用户 ulimit -a 确认 max user processes 大小,推荐配置:655360。 -> -> **解决方案** -> -> 在/etc/security/limits.conf 配置配置用户最大进程数限制。 - ---- - -> **问题现象** -> -> OB3.1.3 进行 replace into 大数据量耗时很长。 -> -> **可能原因** -> -> replace 暂时不支持并发,该版本执行慢基本符合预期。 -> -> **解决方案** -> -> 可以业务上改造成 insert 方式,或者同时升级 OB3.1.4,使用并行导入功能提升性能。 - ---- - -> **问题现象** -> -> 使用 datagrip 连接 oceanbase 报错:`The server time_zone 'GMT+8:00' defined in the 'serverTimezone' parameter cannot be parsed by java TimeZone implementation. See java.util.TimeZone#getAvailableIDs() for available TimeZone, depending on your JRE implementation`。 -> -> **可能原因** -> -> 工具的时区和 OB 不一致。 -> -> **解决方案** -> -> 可以参考:`https://blog.csdn.net/ethan__xu/article/details/111149561 `设置 datagrip 时区。 - ---- - -> **问题现象** -> -> OB4.1.0 使用`'01'`数据 update 更新表中的 decimal(2,2)字段报错:`ERROR 1264 (22003): Out of range value for column`。 -> -> **可能原因** -> -> 符合 decimal(2,2)有效数值范围。 -> -> **解决方案** -> -> decimal(2, 2) 中第一个 2 的含义是说这个 decimal 整数位+小数位一共最多有两位有效数字,第二个 2 的含义是说这个 decimal 小数点儿后最多有两位有效数字,这样整数位最多就只有 2 - 2 = 0 位有效数字。所以这个 decimal(2, 2) 只能存绝对值小于 1 的小数,不能有整数位的。 - ---- - -> **问题现象** -> -> Docker 部署 OB,删除容器后无法启动,日志提示: `cat: /root/obagent/run/ob_mgragent.pid: No such file or directory`。 -> -> **可能原因** -> -> 容器使用 obd 部署的 OB,存在其他组件未挂载目录,导致无法找到组件文件。 -> -> **解决方案** -> -> 除了挂载/root/ob 和/root/.obd 外,还要把/root/obagent 挂载,然后`rm -rf /root/obagent/run/*` ,能成功重启。 - ---- - -> **问题现象** -> -> OB4.1 进行 TPCC 压测报错:`"parameter index out of bounds. 38929 is not between valid values of 1 adn 38928"`。 -> -> **可能原因** -> -> JDBC 需要设置批处理参数。 -> -> **解决方案** -> -> JDBC 设置 allowMultiQueries=true 参数,可以执行批处理,同时发出多个 SQL 语句。 - ---- - -> **问题现象** -> -> obcdc 读取 TINYINT 类型的数据报错,STRING 类型不报错,但是会读取到数据为 true/false。 -> -> **可能原因** -> -> OB 中关键字 BOOL/BOOLEAN 是 TINYINT 的同义词。OB 中 TINYINT(1) 对应 flink cdc 中 BOOLEAN 类型。 -> -> **解决方案** -> -> 修改要同步表的 TINYINT 字段长度,将 TINYINT(1)修改为 TINYINT(2),这样可以临时规避解决。 - ---- - -> **问题现象** -> -> 使用 mysql 客户端连接 OB4.2,load data 加载数据报错:`ERROR 1227 (42501): access denied`。 -> -> **可能原因** -> -> OB4.x load data 增加了安全策略。 -> -> **解决方案** -> -> 使用 obclient 客户端通过 socket 文件连接 OBSERVER 的用户租户。同时注意修改目录权限:`set global secure_file_priv = '/path_name';` - ---- - -> **问题现象** -> -> miniob 编译的时候报错 cmark 命令错误,报错:`build.sh: line 83: cmake: command not found`。 -> -> **可能原因** -> -> cmark 命令安装的用户和编译用户不一致。 -> -> **解决方案** -> -> 尝试添加一下 cmake 到系统的 PATH 环境变量中 -> `sudo admin ~/.bashrc` -> 文件末尾增加: -> `export PATH="/path/to/cmake/bin:$PATH"` -> 执行: -> `source ~/.bashrc` - ---- - -> **问题现象** -> -> OB4.x 使用 mybatisplus 的 updateBatchById 方法报错:`Cause: java.sql.SQLException: Not supported feature or function`。 -> -> **可能原因** -> -> jdbc 连接池配置问题。 -> -> **解决方案** -> -> JDBC 连接池配置: -> rewriteBatchedStatements:true -> allowMultiQueries:true - ---- - -## **OCP 部署问题** - -> **问题现象** -> -> 部署 OCP V3.3.0 时报错 `1146(42S02):Table‘meta_database.compute_vpc’ doesn’t exist`。 -> -> **可能原因** -> -> 这是 OCP V3.3.0 的已知 bug 问题,已发布 bp 版本中已经修复。 -> -> **解决方案** -> -> 可以先在 ocp meta 租户的库中确认是否已经存在 compute_vpc 表,如果存在,把其他的表删除,仅保留 compute_vpc,之后重新初始化 OCP。 - ---- - -> **问题现象** -> -> 部署 OCP V3.3.0 时报错 `resource not enough:memory(Avail:1.6G,Need:8.0G)`。 -> -> **可能原因** -> -> memory_limit 默认使用物理总内存 80%,但如果可用内存不足,会出现创建租户时申请不到资源。 -> -> **解决方案** -> -> 可在 custom_config 模块中将 memory_limit 设置为服务器可用内存范围以内(可执行 `free -g` 命令查询内存)。 - ---- - -> **问题现象** -> -> 部署 OCP V4.0.0 时报错预检测失败:`ocp precheck failed`。 -> ![image.png](/img/FAQ/all_faq/1677054083006-45acdf0a-de75-4077-962c-cb64ed224fe1.png) -> -> **可能原因** -> -> 预检测会检查服务器硬件资源是否符合生产环境标准,如果不通过会报此错误,测试环境建议关闭预检测功能。 -> -> **解决方案** -> -> 关闭预检参数,即将 precheck_ignore 设置为 true。 - ---- - -> **问题现象** -> -> 部署 OCP V3.3.0 是报错无法连接到 metadb:`2003:Can't connect to MySQL on 'xx.xx.xx.xx' (111 Connection refused)`。 -> ![image.png](/img/FAQ/all_faq/1670161481022-cbfd877d-fc4c-4d16-8ce4-028e43bbabb7.png) -> -> **可能原因** -> -> create_metadb_cluster 参数默认为 false,会使用配置中 ob_cluster 模块信息当 metadb,可能是实际操作中 metadb 不存在。 -> -> **解决方案** -> -> 配置文件 create_metadb_cluster 设置为 true,默认安装个 metadb 数据库。 - ---- - -> **问题现象** -> -> OCP V3.3.0 部署过程中,create meta user 阶段报错 `NameError: name ‘traceback’ is not defined`。 -> ![image.png](/img/FAQ/all_faq/1670661197103-eb4f28ed-0eb6-4277-b719-417938c4419f.png) -> -> **可能原因** -> -> 可能是找不到对应的文件导致,删除重新部署会产生新的文件。 -> -> **解决方案** -> -> 检查 `/tmp` 目录下是否有 `precheck-*.sh` 文件(-\*是当时生成的 uuid),如果有,删除即可。 - -## **OCP 使用问题** - -> **问题现象** -> -> 使用 OCP V4.0.0 部署 OBProxy 时,check if process not exit 阶段失败,报错:`status=500 INTERNAL_SERVER_ERROR, errorCode=COMMON_UNEXPECTED, args=process obproxy should not exists on host`。 -> ![image.png](/img/FAQ/all_faq/1670131241278-78cdfae8-ae76-4960-8b72-c508421880d9.png) -> -> **可能原因** -> -> 该节点已存在 OBProxy 服务和进程。 -> -> **解决方案** -> -> 更换默认的 2883 端口,或者卸载该节点已经部署的 OBProxy 服务。 - ---- - -> **问题现象** -> -> 使用 OCP 接管时报错 observer 进程属于 root,需要使用 admin 账号。 -> -> **可能原因** -> -> OCP 接管限制中,要求 observer 进程启动用户必须是 admin。 -> -> **解决方案** -> -> 接管的详细操作可参见 SOP 文档 [【SOP 系列 07】如何使用 OCP 接管 OBD 部署的 OceanBase 集群](https://ask.oceanbase.com/t/topic/30100012) - ---- - -> **问题现象** -> -> OCP V4.0.0 部署集群时卡在 Bootstrap ob 阶段,日志提示 `try adjust config ‘memory_limit’ or ‘system_memory’`。 -> -> **可能原因** -> -> system_memory 默认是 30G,memory_limit_percentage 默认是占用 80% 的物理内存,如果不指定这两个参数配置信息,小规格服务器配置可能出现申请不到内存问题。 -> -> **解决方案** -> -> 调小 memory_limit 和 system_memory 参数。 - ---- - -> **问题现象** -> -> OCP 部署 OceanBase V3.1.4 集群时 bootstrap ob 失败,查看日志显示 `ERROR [CLOG] update_free_quota (ob_log_file_pool.cpp:465) [77084][0][Y0-0000000000000000] [lt=3] [dc=0] clog disk is almost full(type_=0, total_size=1888745000960, free_quota=-198549053440, warn_percent(%)=80, limit_percent=95, used_percent(%)=90)`。 -> -> **可能原因** -> -> OceanBase 数据库中数据文件采用预占用方式,同盘会出现磁盘空间不足问题,生产环境必须分盘,测试环境可降低占用比例。 -> -> **解决方案** -> -> 使用 OCP 部署集群时安装路径参数 home_path 、data、redo 采用分盘部署,不要混部到一块盘上,如果是简单测试,可以把 datafile_size 或者 datafile_disk_percentage 调小。 - ---- - -> **问题现象** -> -> 使用 OCP V3.3.0 部署 OceanBase 数据库时,Pre check for install ob 阶段异常,报错 `ob install pre check failed,maybe from another observer`。 -> -> **可能原因** -> -> OCP 不支持在已有 observer 进程的集群环境再部署一套 OceanBase 集群。 -> -> **解决方案** -> -> 卸载 OBServer 节点上的 observer 进程,或者使用未部署过 OceanBase 数据库的资源环境。 - ---- - -> **问题现象** -> -> 使用 OCP V3.3.0 接管 V3.1.4 OceanBase 集群时,使用 proxy 连接 OCP 报错 OBProxy proxyro 用户密码与 OCP 设置不相同。 -> -> **可能原因** -> -> OCP 初始的 proxyro 密码是空,如果 OceanBase 集群中设置了 proxyro 密码,需要 OCP 也保持一致。 -> -> **解决方案** -> -> 执行如下命令修改 OCP 中 proxyro 密码。 -> -> ```shell -> curl --user admin:aaAA11__ -X POST "" -H "Content-Type:application/json" -d '{"username":"proxyro","password":"*****"}' -> ``` -> -> - `--user` 后需填写 OCP 白屏登录用户:密码, -> - `xx.xx.xx.xx:8080` 为 OCP 地址:端口 -> - `*****` 为 OBD 部署配置文件中 observer_sys_password 参数的密码。 - ---- - -> **问题现象** -> -> 使用 OCP V4.0.3 接管 OBD 部署的 4.x 版本 OceanBase 数据库时,报错 OBProxy proxyro 用户密码与 OCP 设置不相同。 -> -> **可能原因** -> -> OCP V4.0.3 废弃了 /api/v2/obproxy/password 接口,无法通过接口方式保持两端密码一致。后续版本会优化。 -> -> **解决方式** -> -> - 方案 1: -> 可执行 `obd cluster edit-config 部署名称` 命令修改 proxyro_password 和 observer_sys_password 一致为 `*********`,保存修改后再执行 `obd cluster reload 部署名称` 命令即可。 -> - 方案 2: -> -> 1. proxy 里面改(2883 端口,root@proxysys) -> `alter proxyconfig set observer_sys_password = '*********';` -> 2. 直连 observer,sys 租户改 proxyro 用户的密码跟上面一致 -> `set password for proxyro=password('********* ');` - ---- - -> **问题现象** -> -> OceanBase 数据库 V3.1.4 在业务租户有批量或者手工导数期间,偶然性会有告警 `plan cache memory used reach limit`。 -> -> **可能原因** -> -> 如果租户内存设置太小,或者批量导数条数不一致可能出现此类报错。 -> -> **解决方案** -> -> 扩容租户内存或执行如下命令调整 ob_plan_cache_percentage 阈值。 -> -> ```sql -> show variables like'%ob_plan_cache_percentage%'; set global ob_plan_cache_percentage =10; -> ``` - ---- - -> **问题现象** -> -> 使用 OCP 接管 OceanBase V4.0.0 集群时报错所在 IDC 与目标 OBServer 的 IDC 不匹配。 -> -> **可能原因** -> -> 因为接管前已经人为将节点添加到主机列表中,列表中的 IDC 机房和 Region 地区信息和接管的集群默认信息不一致。 -> -> **解决方案** -> -> 主机列表删掉对应的节点信息,重新接管。也可以登录 OceanBase 数据库通过 SQL 修改 IDC 和 Region 信息。 -> -> ```sql -> alter system alter zone 'zone1' set idc = 'xx;alter system alter zone 'zone1' set region = 'xx'; -> ``` - ---- - -> **问题现象** -> -> OCP 上创建的 unit 规格配置不显示。 -> -> **可能原因** -> -> 小于 5G 的 Unit 规格在前端展示的时候会被过滤掉。 -> -> **解决方案** -> -> 连接 OCP 的 meta 租户,查看该限制的控制参数:`select * from config_properties where key like ‘%small%’ \G`。在特殊情况下,比如 demo 演示等非生产环境中,可以将其改为 true,就会取消这个限制,命令如下。 -> -> ```sql -> UPDATE config_properties SET value='true' WHERE `key` = 'ocp.operation.ob.tenant.allow-small-unit'; -> ``` - ---- - -> **问题现象** -> -> OCP V4.0.3 中添加主机,无法安装 ocp agent 服务。 -> ![image.png](/img/FAQ/all_faq/1685439897417-af7f8be4-67bc-4a68-af42-3ddb52ee120f.png) -> -> 报错:`args:/tmp/8c76f061414e4d6/pos.py uninstall_package ^t-oceanbase-ocp-agent, return code:2, output:failed to call pos: func=uninstall_package, args=['^t-oceanbase-ocp-agent'], code=2, output=/tmp/a463f6de-fde4-11ed-8e6e-fefcfeb8fb: line 1: unexpected EOF while looking for matching `。 -> -> **可能原因** -> -> 示例中使用的是 Centos7 系统,安装 ocp-agent 前会做操作系统检查,可能因为 dpkg 命令会错误的判断现场操作系统类型。 -> -> **解决方案** -> -> 执行如下命令卸载 dpkg 命令。 -> -> ```shell -> rpm -qa|grep dpkg , rpm -e --nodeps $dpkg -> ``` - ---- - -> **问题现象** -> -> 使用 OCP V4.0 添加主机报错,白屏页面显示没有找到指定 OCP Agent 类型的记录。 -> ![image.png](/img/FAQ/all_faq/1679294604936-21502072-f996-49b8-a69c-2ea5a9583539.png) -> -> 报错:No route to host (Host unreachable) -> ![image.png](/img/FAQ/all_faq/1679294670666-987ea895-1277-4cb1-bd5d-1761b065e343.png) -> -> **可能原因** -> -> 待添加的主机防火墙开启,安装程序路由不到该节点。 -> -> **解决方案** -> -> 关闭待添加主机的防火墙即可。 - ---- - -> **问题现象** -> -> 使用 OCP 部署 OceanBase 数据库 V3.1.2,使用 root 用户后台启动 observer 后,再重新使用 admin 用户启动失败。。查看日志报错如下。 -> -> ```shell -> ERROR [COMMON] inner_open_fd (ob_log_disk_manager.cpp:1043) [5889][0][Y0-0000000000000000] [lt=5] [dc=0] open file fail(ret=-4009, fname="/home/admin/oceanbase/store/lzq/slog/4", flag=1069122, errno=13, errmsg="Permission denied") -> ERROR [SERVER] init (ob_server.cpp:172) [4195][0][Y0-0000000000000000] [lt=2] init config fail(ret=-4009) -> ``` -> -> ![image.png](/img/FAQ/all_faq/1670653105470-80824e4b-b9d5-4b23-8c53-fbcd928294c4.png) -> -> **可能原因** -> -> OCP 部署或接管的 OceanBase 集群均是 admin 用户权限,使用 root 启用后会导致 observer.conf.bin 文件和 redo 日志目录下的文件权限变更,无法再使用 admin 启动成功。 -> -> **解决方案** -> -> 执行如下命令修改所有 OBServer 节点目录的权限。 -> -> ```shell -> chown -R admin.admin /home/admin/oceanbase/ -> chown -R admin.admin /data/1 -> chown -R admin.admin /data/log1 -> ``` - ---- - -> **问题现象** -> -> 使用 OCP V3.3.0 部署 OceanBase 数据库时安装路径检测失败。 -> -> **可能原因** -> -> 安装目录和数据目录没有 admin 用户权限。 -> -> **解决方案** -> -> 可执行如下命令为安装目录和数据目录赋予 admin 用户权限。 -> -> ```shell -> chown -R admin:admin /data && chown -R admin:admin /redo && chown -R admin:admin /home/admin -> ``` - ---- - -> **问题现象** -> -> 使用 OCP V3.3.0 接管使用 OBD 部署的 OceanBase V4.0.0 集群时失败,报错 `(conn=10) Table 'oceanbase.v$ob_cluster' doesn't exist`。 -> -> **可能原因** -> -> OceanBase 数据库 4.0.0.0 版本相较 3.x 版本架构变动较大,部分系统表进行调整,使用不配套的 OCP 版本无法接管。 -> -> **解决方案** -> -> 可升级 OCP 版本到 V4.0.0。 - ---- - -> **问题现象** -> -> 无法使用 OCP 把管理的 OceanBase 数据库移除。 -> -> **适用版本** -> -> OCP 3.x、4.x 版本。 -> -> **可能原因** -> -> OCP 暂不支持迁出部署和接管的集群,但可以使用接口实现,并且迁出的集群不管是使用 OBD 部署的还是使用 OCP 部署的,均支持再次接管。后续版本会增加迁出集群功能。 -> -> **解决方案** -> -> 可以通过调用后端 restful api 的方式来迁出 OceanBase 数据库集群。命令如下: -> -> ```sql -> curl -X POST --user {user}:{password} -H "Content-Type:application/json" -d '{}' "http://{ocp-url}:{port}/api/v2/ob/clusters/{cluster_id}/moveOut" -> # example -> curl -X POST --user admin:aaAA11__ -H "Content-Type:application/json" -d '{}' "" -> ``` -> -> 需注意的是,命令中的 cluster_id 指的是 OCP 浏览器地址中的集群 ID,例如 xx.xx.xx.xx:8080/cluster/2,表示 cluster_id 是 2。 - ---- - -> **问题现象** -> -> 使用 OCP V3.3.0 重启 OceanBase(V3.1.4)集群被卡住,该集群使用 OBD 可正常重启。 -> -> ![image.png](/img/FAQ/all_faq/1671330730151-a86d1780-969f-469c-b778-0603e730219e.png) -> -> 查看日志报错:Connect to xx.xx.xx.xx:62888 [/xx.xx.xx.xx] failed: Connection refused (Connection refused) -> -> **可能原因** -> -> ocp-agent 服务的端口为 62888,查看日志显示报错连接被拒绝,基本为 ocp-agent 服务异常无法通过 agent 服务下发指令导致。 -> -> **解决方案** -> -> 重启主机节点的 ocp-agent 服务。 - ---- - -> **问题现象** -> -> 使用 OCP V4.0 关闭集群页面报错:集群 obcluster 不允许进行操作。 -> ![image.png](/img/FAQ/all_faq/1676864800701-308adf5b-2473-4c80-8219-2e5aa7b50029.png) -> -> **可能原因** -> -> OCP 的 metadb 被 OCP 自身接管,如果使用 OCP 管理该集群会导致 OCP 服务不可用。 -> -> **解决方案** -> -> 不能使用 OCP 停止接管的 metadb。 - ---- - -> **问题现象** -> -> 使用 OCP 4.x 版本删除租户下的 test1 库,提示不允许进行该操作。 -> -> ![image.png](/img/FAQ/all_faq/1684810148859-3d4ebd0c-5c6f-4efb-9c09-e8be5d825fca.png) -> -> **可能原因** -> -> 此集群和 metadb 共用,OCP metadb 默认是不允许通过 OCP 做运维操作的。 -> -> **解决方案** -> -> OCP meta 租户下执行如下命令将黑名单对应的 value 置空。 -> -> ```sql -> update config_properties set value='' where `key`='ocp.ob.cluster.ops.blacklist'; -> ``` - ---- - -> **问题现象** -> -> 将 OCP 从 V3.1.1 升级至 V3.3.0 时失败,报错 yaml 格式不正确。 -> -> ![image.png](/img/FAQ/all_faq/1665307305238-ceb2f464-3711-42c4-8ac1-698ef8c880fe.png) -> -> **可能原因** -> -> OCP V3.1.1 和 V3.3.0 的配置文件格式差异较大,升级需要使用 V3.3.0 的配置格式。 -> -> **解决方案** -> -> 按 OCP V3.3.0 的配置模版文件改写。 - ---- - -> **问题现象** -> -> 将 OCP 从 V3.1.1 升级至 V3.3.0 报错 `Can't connect to MySQL server on xx.xx.xx.xx:2881(-2 Name or server not know)`。 -> -> ![image.png](/img/FAQ/all_faq/1665308040282-69fbfbc7-38cc-4b1b-9ab8-7542921f6f0f.png) -> -> **可能原因** -> -> OCP V3.1.1 配置是直连方案,V3.3.0 配置是 proxy 连接方案,升级需要保持原方案,直连时用户名不能带集群名称,否则会被误把 “租户#集群名称” 解析成租户。 -> -> **解决方案** -> -> 在 yaml 配置文件 metadb 模块使用直连方式,去掉租户的 `#集群名称`。 - ---- - -> **问题现象** -> -> 将 OCP 从 V3.3.0 升级到 V4.0.0 报错 `KeyError: 'buildVersion'`。 -> -> ![image.png](/img/FAQ/all_faq/1671178876425-4f89a6d7-14e7-4c5f-81a7-7b45d832204b.png) -> -> **可能原因** -> -> 升级需要调用接口连接 OCP,使用的账户为配置文件中的 auth 模块信息,auth 模块配置只有升级过程会用到。如果通过 OCP 白屏修改过登录密码,该配置中也需同步修改。 -> -> **解决方案** -> -> cinfig.yaml 文件 auth 模块的信息改为 OCP 白屏登录用户和密码。 - ---- - -> **问题现象** -> -> OCP4.0.3 上扩容 OB4.0 节点,在启动 OB 阶段 start observer process with param 失败,obsever.log 日志报错`server available memory is little than unit min memory, can not create sys tenant. try adjust config 'memory_limit' or 'system_memory'.(ret=-4147, ret="OB_INVALID_CONFIG", unit_min_memory=1073741824, server_avail_memory=-5372854272, system_memory=32212254720, server_memory_limit=26839400448) ` -> -> **可能原因** -> -> 报错在创建扩容节点的 sys 租户时 system_memory 过大,超出了 memory_limit 内存总量。 -> 主要是内存资源分配有问题,可能是扩容节点的资源配置和原集群不一致或差异较大。也可能是扩容时 OCP 传参的资源参数不正确导致。 -> meta 租户登录 metadb 查询 mate_database 库下的 ob_cluster 表的 startup_parameters 字段,即原集群首次部署时初始化资源配置,查看资源和当前集群是否保持一致。 -> -> **解决方案** -> -> 经过确认为 OCP 部署 OB 后,重新调整了资源参数,修改上述 ob_cluster 表信息和实际集群资源一致即可。 - ---- - -> **问题现象** -> -> OCP4.0.3 版本 metadb 的连接池占满,ocp.log 报错:`active 100,maxActive 100` -> -> **可能原因** -> -> 1)ocp 的 metadb 资源存在瓶颈导致查询性能慢,造成连接阻塞。例如使用 sata 磁盘,cpu 分配太小等。 -> 2)ocp 管理上百个租户,造成采集数据较多,资源和数据不成正比。需要增加配置或降低监控数据保留周期。 -> -> **解决方案** -> -> - 方法 1: -> meta 租户设置 spring.datasource.druid.maxActive 连接池调大 -> `update config_properties set value = '200' where key = 'spring.datasource.druid.maxActive';` -> - 方法 2: -> ocp 的系统参数中调整历史监控数据保留周期 ocp.monitor.data.retention-days,降低数据量,ocp_metric_data_1 就是秒级数据的保留时间,默认是 8 天。 - ---- - -> **问题现象** -> -> OCP4.0.3BP1 使用 ocp-installer 命令停止 ocp 服务失败,报错`[ERROR] Running Error:'oceanbase-ce'` -> -> **可能原因** -> -> ocp-installer 命令缺陷,生成的配置缺少联动,推荐部署或升级 OCP 最新版本。 -> -> **解决方案** -> -> 1. ocp-installer cluster list 查看 ocp 服务配置文件位置 configuration path。 -> 2. ocpmetadb-OCP 服务对应的配置文件路径下有个.date 文件记录状态数据,删除其中 oceanbase-ce 模块内容。 -> 3. 重新执行停止命令即可。 - ---- - -> **问题现象** -> -> OCP4.0.3 版本接管 OB 时执行 clockdiff 命令失败,报错`Cannot run program "clockdiff"(in directory ".")` -> -> **可能原因** -> -> 部分操作系统 普通用户是没有执行 clockdiff 命令权限的,即使普通用户有 sudo 权限,OCP 当前版本并使用 sudo 方式去执行。 -> -> **解决方案** -> -> 操作系统 root 执行 `setcap cap_net_raw+ep /usr/sbin/clockdiff` 赋权即可。 - ---- - -> **问题现象** -> -> OCP4.0.3 版本,想删除运维中状态的 OBProxy 集群失败,提示:OBProxy 集群[xxxx]的状态不合法:[LOCK],不允许该操作 -> -> **可能原因** -> -> 运维中状态是不允许做删除操作的,防止正在执行的任务受影响。 -> -> **解决方案** -> -> 如果确认删除组件无影响,可以登录 ocp 的 meta 租户查看在 meta_database 库下的 obporxy_cluster 表,将 status 字段 lock,改 unlock,然后 ocp 上任务删掉 obproxy。 - ---- - -> **问题现象** -> -> OCP 升级到 4.2.0 后部署 OB,在 refresh ocp agent config 阶段报错:`key ocp.agent.snapshot.maxsize is not found in config properties`。 -> -> **可能原因** -> -> ocp420 升级未把主机的 ocp-agent 版本升级上来,ocp421 版本修复。 -> -> **解决方案** -> -> 升级 ocp-agent 版本和 ocp 版本保持一致 - ---- - -> **问题现象** -> -> OCP 升级到 4.2.0 后再部署 OBProxy,在 install rpm by package and version 阶段报错:`http request is failed, response:Bad request: 404 not found` -> -> **可能原因** -> -> ocp420 升级未把主机的 ocp-agent 版本升级上来,ocp421 版本修复。 -> -> **解决方案** -> -> 升级 ocp-agent 版本和 ocp 版本保持一致 - ---- - -> **问题现象** -> -> OCP 升级到 4.2.0 后再部署 OBProxy,在 install rpm by package and version 阶段报错:`message=InvalidKeyException: lllegal key size or default parameters` -> -> **可能原因** -> -> 密钥长度受限制,java 运行时环境读到的是受限制的 policy 文件,后续版本会做预检查。 -> `https://stackoverflow.com/questions/6481627/java-security-illegal-key-size-or-default-parameters` -> -> **解决方案** -> -> 升级 jre 版本,注意需要和 JDK 版本对应 - ---- - -> **问题现象** -> -> OCP4.0.3 版本告警 OB 日志有 ERROR 级别报错:`the hold of observer tenant is over the system_memory` -> -> **可能原因** -> -> system_memory 是集群的 500 租户内存,此内存也是共享内存,可能会出现内存占满问题。 -> `select tenant_id,svr_ip,sum(hold),sum(used) from __all_virtual_memory_info where tenant_id=500;` -> 查看到 hold 和 used 接近上限。 -> -> **解决方案** -> -> 调大集群 system_memory 参数。 - ---- - -> **问题现象** -> -> OCP 升级到 4.2.0 后接管 OB 在 Wait observer accessible 阶段报错:`ERROR 1045 (42000): Access denied for user 'ocp_monitor'@'xxx.xxx.xxx.xxx' (using password: NO)` -> -> **可能原因** -> -> mysql client 如果是 8.0.21 版本不配置--default-auth=mysql_native_password 无法认证登录。该问题已经在 OCP421 版本解决,后续 OCP 将不依赖 mysql 客户端。 -> -> **解决方案** -> -> OB 节点 mysql 客户端升级到 8.0.28 版本,重试升级即可 - ---- - -> **问题现象** -> -> OCP4.2.0 告警`call web service failed,ret=-4216`。 -> -> **可能原因** -> -> 这个报错是 AWR 功能依赖,社区版不支持该功能,OCP421 会关闭。 -> -> **解决方案** -> -> 忽略告警或者升级 OCP421 版本。 - ---- - -> **问题现象** -> -> OCP4.2.1 创建 obproxy 时关联 oceanbase 报错:集群 xxx 不允许进行该操作。 -> -> **可能原因** -> -> metadb 默认不支持被 OCP 运维操作的,防止高危操作(例如:重启 metadb),出现 OCP 不可用。 -> 强制支持 metadb 运维方式(不推荐,仅供测试):metadb 下的 meta 租户 meta_database 库下更新操作: -> `update config_properties set value='' where key='ocp.ob.cluster.ops.blacklist';` -> -> **解决方案** -> -> 不能关联 OCP 的 metadb,其他业务 OB 集群可关联。 - ---- - -> **问题现象** -> -> OCP 部署 OB4.2.1 初始化失败,`ret=-9102,prepare_dir_and_create_meta_ failed`。 -> observer.log 报错 -> `ERROR issue_dba_error (ob_log.cpp:1866) [2604157][observer][T0][Y0-0000000000000000-0-0] [lt=7][errcode=-4388] Unexpected internal error happen, please checkout the internal errcode(errcode=-9102, file="ob_server_log_block_mgr.cpp", line_no=506, info="prepare_dir_and_create_meta_ failed")` -> -> **可能原因** -> -> 目录权限相关,日志可以看出来和 mkdir 目录操作相关,建议检查目录权限问题。 -> -> **解决方案** -> -> 手动执行 `mkdir -p /home/admin/oceanbase/store/xxx/clog` 创建目录会提示无访问权限。因此需要检查磁盘访问或目录挂载问题。 - ---- - -> **问题现象** -> -> OCP4.2.1 修改 ocp.site.url 参数之后重启不生效,而且重启后参数被还原了。 -> -> **可能原因** -> -> 版本缺陷,后续 OBD250 修复,--without_ocp_parameter 参数会跳过 ocp.site.url 参数的初始化。 -> -> **解决方案** -> -> 修改 ocp.site.url 参数后,使用如下命令重启 OCP,`obd cluster start xxx --skip-create_tenant ` - ---- - -> **问题现象** -> -> OCP4.2.1 重装 ocp-agent 失败,mgragent.log 日志报错:`error=sudo:unknown user:admin` -> -> **可能原因** -> -> OCP4.2.2 版本才会解除 ocp-agent 对 admin 用户的强依赖问题。之前的版本 admin 用户为 ocp-agent 创建,但存在环境变量等问题。 -> -> **解决方案** -> -> 1)需要在所有机器上创建一下 admin 操作系统用户 -> 2)`cd /home/admin/ocp_agent/bin && ./ocp_agentctl stop` -> 3)跳过之前升级自动触发的 reinstall ocp agent 任务 -> 4)在 ocp 主机管理看到对应的机器变成离线 -> 5)在 ocp 主机管理处点击安装新版本 ocp-agent - ---- - -> **问题现象** -> -> OCP 升级 OB4.2.1 时卡在 execute upgrade post script 阶段,界面报错超时:`"UPGRADE_ALL",timeout`。 -> observer.log 报错:`clog disk hang event`。 -> -> **可能原因** -> -> clog 磁盘写的慢,clog 和 data 共用,使用的机械盘,升级过程中,rs 不断切换,导致升级超时失败。 -> -> **解决方案** -> -> 使用 SSD 磁盘且数据盘和日志盘分盘部署。 - ---- - -> **问题现象** -> -> OCP4.2.1 不断告警服务器时钟同步服务不存在,告警信息:服务器时钟同步服务(ntp 或 chrony)不存在。 -> -> **可能原因** -> -> - 未安装同步工具 ntp/chrony。 -> - 开启了 ipv6 服务,ipv6 开启后,执行 ntpq -pn 命令会返回 tomeout 超时,OCP 检测 ntp 服务失败。 -> -> **解决方案** -> -> - OB 对时间延迟比较敏感,超过 100ms 延迟会将节点踢出集群,因此需要时钟同步。 -> - 关闭 ipv6。 - ---- - -> **问题现象** -> -> OCP 告警 OB4.1.0 数据备份任务失败,报错:`ERROR 9031 (HY000) : Cannot backup ob replica`,错误码:9031。 -> -> **可能原因** -> -> 该 Replica 正在迁移复制或者是日志副本时,无法提供数据备份的能力,OB4.2 已优化。 -> -> **解决方案** -> -> 升级 OB 到 4.2 版本或以上版本。 - ---- - -> **问题现象** -> -> OCP421 不显示 OB 集群监控信息。 -> -> **可能原因** -> -> 可能是做过压测,并对集群做过压测优化,把 enable_perf_event 参数关闭了。该参数默认是开启的,用于性能事件的信息收集。 -> -> **解决方案** -> -> OCP 集群参数中将 enable_perf_event 设置为 true。 - ---- - -> **问题现象** -> -> OCP4.2.0 版本,黑屏手动杀掉 OB 的进程后会被自动拉起。 -> -> **可能原因** -> -> OCP420 版本新增功能,会监控守护 OB 进程。 -> -> **解决方案** -> -> 如果维护想关闭该功能,登录 ocp_meta 元租户,将 selfcure_contingency_config 表对应的 enabled 改成 0 即可。 - ---- - -> **问题现象** -> -> OCP4.2.1 版本做数据恢复时,不显示源租户信息。 -> -> **可能原因** -> -> - OCP 接管 OBD 部署的 OB,OB 的 bin 目录下缺少运维程序 ob_admin。 -> - OB 节点 admin 用户环境变量有问题。 -> -> **解决方案** -> -> - 官网下载工具集成包(OceanBase Utils),解压后将 ob_admin 文件上传至 observer 安装目录的 bin 目录下,并赋予同级 observer 文件相同权限即可。 -> - OCP4.2.2 版本会修复 ocp-agent 依赖 admin 用户问题。 - ---- - -> **问题现象** -> -> 使用 OCP4.2.1 升级 OB 时在 Install dependencies 阶段报错: -> `message = Install software package failed, reason: previous installed package oceanbase-ce-libs prefix not matched. prev: /home, new: /home/admin/observer` -> -> **可能原因** -> -> ocp-agent 本地文件可能有问题,后续版本将优化。 -> -> **解决方案** -> -> 查看每个 ob 节点的/home/admin/ocp-agent 目录下的 task_store、pkg_store,清理掉 pkg_store 目录下的 oceanbase-ce-libs 包以后,重新执行任务即可。 - ---- - -> **问题现象** -> -> 容器部署的 OCP4.2.1 接管 OB 时界面报错: -> `Unhandled exception, type=IllegalArgumentException, message=Illegal Connect-User '%s': missing '@'` -> -> **可能原因** -> -> 连接 metadb 时的配置可能不正确,导致接管时验证参数解析错误。 -> -> **解决方案** -> -> OCP 容器将 OCP_METADB_USER 参数增加完整租户信息。 -> 写法: -> -> - 用户名@租户名#集群名称(使用 obproxy 连接 metadb 时)。 -> - 用户名@租户名(使用直连 metadb 时)。 - ---- - -> **问题现象** -> -> OCP4.2.0 界面会提示错误信息: -> `Out of range value for column 'MAX_IOPS' : value 2.7670116110564327E19 is not in class java.lang.Long range`。 -> -> **可能原因** -> -> OB 集群的 sys 租户资源配置的 max_iops 取值越界缺陷,后续版本会修复,不影响使用。 -> -> **解决方案** -> -> sys 租户登录 `select * from oceanbase.gv$ob_units;` 如果 MAX_IOPS 值异常大(9223372936854775807),可以修改 sys 租户的 unit 配置。 -> `alter resource unit sys_unit MIN_IOPS=10000,MAX_IOPS=10000;` 即可。 - ---- - -> **问题现象** -> -> OCP4.0.3 删除主机失败,放弃任务正常,但主机状态显示:删除中。 -> -> **可能原因** -> -> 任务回滚信息写入元数据库可能失败。 -> -> **解决方案** -> -> 手动更新 metadb 中 ocp_meta 租户的组件状态。 -> `update compute_host set status = 'AVAILABLE' where id='xx';`。 - ---- - -## **OMS 使用问题** - -> **问题现象** -> -> 使用 OMS V3.3.1 在迁移多张大表到 OceanBase 数据库时,checker 组件报错 `NNER_ERROR[CM-RESONF000003]: no enough host resource for a CHECKER, reason [host: IP unavailable cause: current memory usage 0.8573829 exceed limited 0.85]`。 -> -> **可能原因** -> -> 迁移大表较多,使用默认的 checker 组件资源过小。 -> -> **解决方案** -> -> 迁移项目详情页点击 **查看组件监控**,更新 checker 配置中的 `task.checker_jvm_param` 调整 jvm 参数。 - ---- - -> **问题现象** -> -> 使用 OMS V3.3.0 启动增量同步报错 `INVALID_STATUS_ERROR [INVALID_STATUS_ERROR] {“message”:“Not found ocp name from connectInfo: jdbc:oceanbase://xx.xx.xx.xx:2883?allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false&useSSL=false”}`。 -> -> **可能原因** -> -> 使用 OMS 启动增量任务时没找到 OCP 或者 configUrl,检查一下数据源里有没有配置 OCP 或者 configUrl。 -> -> **解决方案** -> -> 在数据源的高级选项里配置 configUrl。 - -> **问题现象** -> -> OMS4.x 版本增量同步 OB3.x 到 OB3.x 的链路失败,DDL 日志报错`event source process failed: [ not support create table select as option, origin sql:CREATE TABLE AA AS SELECT a,b FROM BB` -> -> **可能原因** -> -> OMS 不支持同步 create table as select 语句,因为其操作创建临时表,同步数据量无法规范,对 OMS 性能影响较大,未来也不考虑支持该语法。 -> -> **解决方案** -> 如果只采取跳过方式只能规避该 sql 语句,后续该表可能涉及其他的 dml 操作同步时,会报错表不存在问题,因此可以直接将表纳入黑名单即可。 -> 查看组件监控->增量同步组件->更新,将 BB 表加入黑名单,`https://www.oceanbase.com/docs/community-oms-cn-1000000000425982` - ---- - -> **问题现象** -> -> OMS4.1 版本表结构迁移失败,报错`Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: interrupt` -> -> **可能原因** -> -> 表结构一次性迁移太多导致,同时也和资源环境有一定关系。 -> -> **解决方案** -> -> 分多个链路分批迁移表结构,或者单链路迁移表数建议不要太多,建议少于 400 个。 - ---- - -> **问题现象** -> -> 重装 OMS4.1.1 版本后使用默认用户和密码登录失败,界面报错 `OMS_USER_NOT_ACTIVE` -> -> **可能原因** -> -> 浏览器缓存登录信息和重装的 OMS 有冲突导致。 -> -> **解决方案** -> -> 清理浏览器缓存。 - ---- - -> **问题现象** -> -> OMS4.1.0 升级到 OMS4.1.1 后,登录报错鉴权错误。 -> -> **可能原因** -> -> OMS4.1.1 之前的版本不支持升级到 OMS4.1.1 以及以上版本,因为 OMS4.1.1 版本开始由社区版独立分支,元表结构做了较大改动,后续 OMS4.2 版本会做升级不支持拦截。 -> -> **解决方案** -> -> 重新部署 OMS4.1.1 版本或新版本。 - ---- - -> **问题现象** -> -> OMS4.0 增量同步失败,JDBCWriter 组件状态停止。界面报错:CM-SCHEOR000021,jdbcwriter 日志报错执行 replace into 语句时超时断开连接 Broken pipe(Write failed) -> -> **可能原因** -> -> replace into 语法操作的目标表很大事后,会出现写入慢的现象,导致同步超时。 -> -> **解决方案** -> -> 修改 OMS 增量组件参数 `JDBCWriter.sinkFile.isMysqlReplaceMode=false` ,重启 JDBCWriter 增量组件。设置这个之后,优化 replace into 语法为 insert ,加速写入。 - ---- - -> **问题现象** -> -> 做 OMS HA 高可用中的 store、jdbcWriter 无法切换了。 -> -> **可能原因** -> -> OMS 高可用节点中存在资源使用比例超过 80%,包括磁盘、cpu、内存等,可以在 OMS 机器资源中确认。 -> -> **解决方案** -> -> 释放其他链路或者扩容机器资源。 - ---- - -> **问题现象** -> -> OMS4.1 迁移预检查失败,界面报错:TIME_EXCEPTION,操作 PreCheckAction doRunningAction timeout.projectld xxx1 超时。 -> -> **可能原因** -> -> 查询 `select gmt_modified from oms_step where project_id='xxx1';` 发现和本地时间不一致,差 8 小时。 - -> **解决方案** -> -> OMS 元数据库和 OMS 节点需要时间保持一致。 - ---- - -> **问题现象** -> -> OMS4.2.0 表结构迁移报错获取 DDL 失败。 -> 错误码:CHANA-MIGRAT000201。 -> 错误信息:`OBSCHEMA_ERROR:Failed to migrate the tables,We recommend status filtering under the structure migration step for more information`。 -> 错误原因:库、表或者视图迁移失败。 -> /home/admin/logs/ghana/Ghana/dbcat.log 日志报错:`Unknow column 'EXPRESSION' in 'field list' ` -> -> **可能原因** -> -> OB420_BETA_HF1 版本的系统视图缺少 EXPRESSION 字段。 -> -> **解决方案** -> -> OB 源端升级到 OB421 或以上版本。 - ---- - -> **问题现象** -> -> OMS4.1.1 全量迁移 mysql 从库到 ob 链路,从库开启 binlog,但报错:读取 binlog 数据包时出错。 -> 错误原因:提示`please see the master's error log or the manual for GTID_SUBTRACT.` -> -> **可能原因** -> -> 看报错和 Gtid 参数相关。 -> -> **解决方案** -> -> 组件监控,修改 store 组件的配置:mysql2store.useGtid=false。 - ---- - -> **问题现象** -> -> 安装 OMS4.2,启动浏览器访问报错:服务器内部错误。 -> /home/admin/logs/ghana/Ghana/oms-web.log 报错:`Conversion not supported for type java.time.LocalDateTime` -> -> **可能原因** -> -> 报错是 metadb 元数据库的时间类型转换不支持,如果是 metadb 是 ob 是没问题。OMS 暂时只支持 mysql8 和 oceanbase 作为 metadb。 -> -> **解决方案** -> -> 替换 metadb,使用 mysql8 或者 ob 数据库,不支持 mysql5.x。 - ---- - -> **问题现象** -> -> OMS 多次登录失败,提示:密码已锁定,当前用户角色:ADMIN,请联系更高权限用户协助修改密码。 -> -> **可能原因** -> -> OMS 密码输入 5 次不正确会锁登录用户,半小时后可能重试。 -> -> **解决方案** -> -> 登录 OMS 的 metadb 元数据库,修改 rm 库的 oms_user 表,将 login_failure_times 和 is_locked 都设置成 0。 -> 或者进入 oms 容器:supervisorctl restart oms_console,重置登录信息。 - ---- - -> **问题现象** -> -> OMS4.1 操作 添加关联 OCP 失败,提示:查询 ocp 版本失败。 -> -> **可能原因** -> -> - 相关 OB 数据源没有使用 OCP 管理。 -> - 使用 OCP 管理,但 OCP 更新元数据可能不正确。 -> -> **解决方案** -> -> - 使用 OCP 接管数据源或者部署 config-server 服务。 -> - 可以手动拼写 ocp config_url 信息,ObRegion 是集群名称,其他参数是固定写法: -> `http://xx.xx.xx.xx:8080/services?Action=ObRootServiceInfo&User_ID=alibaba&UID=ocpmaster&ObRegion=xxxx ` - ---- - -> **问题现象** -> -> OMS4.2.0 版本创建数据源报错无法连接,报错: -> `ERROR 1045 (42000): Access denied for user 'xxx'@'xxx.xxx.xxx.xxx' (using password: YES)`。 -> 但使用相同配置黑屏或者 navicat 可以连接。 -> -> **可能原因** -> -> 可能是 OMS 数据源未使用 obproxy 连接配置。 -> -> **解决方案** -> -> OMS 配置的数据源只能用 obproxy 连接。 - ---- - -## **OBProxy 问题** - -> **问题现象** -> -> 使用 OBProxy 4.x 版本做大批量插入时候,应用报错 `Connection reset by peer`。 -> -> OBProxy 日志中看到报错:`obproxy's memroy is out of limit's 80% !!!` -> -> **可能原因** -> -> 通过报错信息看是 OBProxy 的内存超出了,但最终问题是现场插入是未做连接回收引发多种问题,可以借鉴。 -> -> **解决方案** -> -> 使用 root@proxysys 账号 2883 端口连接到 OBProxy,修改内存大小:`ALTER proxyconfig SET proxy_mem_limited = 6G;`。 - ---- - -> **问题现象** -> -> obproxy3.2.3 断开连接,obproxy.log 日志报错:`fail to produce(expected size=0, actual size=2, ret=-4016)`。 -> -> **可能原因** -> -> server 返回给 obproxy 的包太大时,会导致无法读取完整,produce 函数报错,obproxy4.1 版本修复。 -> -> **解决方案** -> -> 升级 obproxy 版本到 4.1 或以上版本。 - ---- - -## **ODC 问题** - -> **问题现象** -> -> 使用 MAC 安装 ODC V3.3.2 报错 `[com.alipay.odc.config.BeanCreateFailedAnalyzer][21]: bean create failed`。 -> -> **可能原因** -> -> 有可能是 JAVA 版本的问题。 -> -> **解决方案** -> -> 推荐使用自带 JDK 的版本。 - ---- - -> **问题现象** -> -> ODC 新建连接报错 `BadRequest exception, type=IllegalArgumentException, message=Not a valid secret key`。 -> -> **适用版本** -> -> ODC 3.x 版本。 -> -> **可能原因** -> -> 可能由于 JRE 版本过低,出现加密失败导致。 -> -> **解决方案** -> -> 可以下载最新 ODC 版本,推荐使用内置 jre 的 ODC 版本 - ---- - -> **问题现象** -> -> ODC V3.2.3 客户端报 Query timed out 错误。 -> -> 报错:`ErrorCode = 1317, SQLState = 70100, Details = (conn=405741) Query timed out` -> -> **可能原因** -> -> ODC 工具自身有个 “SQL 查询超时时间” 设置,这里是 SQL 的执行时间超过了 ODC 在驱动层设定的超时时间导致。 -> -> **解决方案** -> -> 需要在 **连接详情** 界面更改 “SQL 查询超时时间”,使其大于 SQL 的实际执行时间。 - ---- - -> **问题现象** -> -> 刚部署的 ODC(V3.2.3) 打开保存 Java 进程异常退出。 -> ![image.png](/img/FAQ/all_faq/1685864534556-2172dfa7-d407-4d33-b8cc-c409a3b2c97d.png) -> -> 报错:`Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.UncategorizedScriptException: Failed to execute database script; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is org.h2.jdbc.JdbcSQLNonTransientException: General error: "java.lang.IllegalStateException: Chunk 3233 not found [1.4.200/9]" [50000-200]` -> -> **可能原因** -> -> ODC 桌面版使用的是 h2 作为内置数据源,但是 h2 bug 比较多,不大稳定,可能会出现数据损坏的问题,这就会导致 ODC 无法打开,出现截图中问题。 -> -> 通常来说 h2 数据文件损坏是由于不正常关闭软件导致,比如 ODC 没有退出就直接关机,任务管理器直接终止进程等等。 -> -> **解决方案** -> -> 在 ODC 安装目录下删除 odc2.x.mv.db,odc2.0.trace.db,之后重启。 - ---- - -> **问题现象** -> -> ODC4.2.2 连接 OB 失败,提示访问元数据库错误,请联系管理员。 -> 日志报错: -> `java.sql.SQLException: Data too long for column 'client_ip_address' at row 1,perfLevel=P1,response=ErrorResponse(error=Error(code=DataAccessError, message=访问元数据库错误,请联系管理员, details=[]), code=DataAccessError, message=访问元数据库错误,请联系管理员)` -> -> **可能原因** -> -> 可能 odc 未使用 obproxy 连接 ob。因为 odc 只支持通过 obproxy 连接 OB 数据库。 -> -> **解决方案** -> -> 连接串使用 obproxy 的 IP 和端口。 - ---- - -> **问题现象** -> -> ODC4.2.2 连接原生 mysql 失败,界面报错:`for input string:"32-78"` -> -> **可能原因** -> -> 内部缺陷,ODC423 修复。 -> -> **解决方案** -> -> 升级 ODC4.2.3 版本。 - ---- - -## **OBDUMPER/OBLOADER 使用问题** - -> Q:如何解决使用 OBLOADER 导入时遇到 OOM 错误? -> A:首先修改 bin/obloader 脚本中的 JAVA 虚拟机内存参数。其次排除 OpenJDK GC Bug。 - -> Q:如何在调试模式下运行 OBLOADER 排查问题? -> A:直接运行 bin/obloader-debug 进行导入。 - -> Q:如何使用 OBLOADER 导入与表不同名的数据文件? -> A:命令行中的 `-f` 选项指定为数据文件的绝对路径。例如:`--table 'test' -f '/output/hello.csv'`。 - -> Q:为表配置了控制文件,导入的数据为什么没有生效? -> A:要求控制文件的名称与表名相同且大小写一致。MySQL 默认表名为小写,Oracle 默认表名为大写。 - -> Q:运行 OBLOADER 脚本时,命令行选项未被正常解析的原因是什么? -> A:可能是命令行参数值中存在特殊符号。例如:Linux 平台上运行导数工具,密码中存在大于号(注:> 是重定向符),导致运行的日志都会出现丢失。因此在不同的运行平台使用正确的引号进行处理。 -> -> - Windows 平台参数使用双引号。例如:`--table "*"`。 -> - 类 Linux 平台参数使用单引号。例如:`--table '*'`。 - -> Q:运行 OBLOADER 脚本时,何时需要对参数加单引号或者双引号? -> A:建议用户在一些字符串的参数值左右加上相应的引号。 -> -> - Windows 平台参数使用双引号。例如:`--table "*"`。 -> - 类 Linux 平台参数使用单引号。例如:`--table '*'`。 - -> Q:外部文件格式不符合要求导致导入失败,应该如何解决? -> A:导入外部文件时对格式有以下要求: -> -> - 外部文件如果为 SQL 文件,要求 SQL 文件中不能有注释和 SET 开关语句等,并且文件中只能有 INSERT 语句,每条语句不可以换行。除此以外,文件中存在 DDL 或者 DML 语句,建议使用 MySQL source 命令导入。 -> - 外部文件如果为 CSV 文件,CSV 文件需符合标准定义。要求有转义符、定界符、列分隔符和行分隔符。数据中存在定界符需要指定转义符。 - -> Q:OBLOADER 启动报错 `Access denied for user 'root'@'xxx.xxx.xxx.xxx'`。 -> A:导数工具默认依赖 root@sys 用户和密码。如果集群中已为 root@sys 用户设置的密码,请在命令行中指定 `--sys-password` 选项的参数值为 root@sys 用户设置的密码。 - -> Q:OBLOADER 运行报错 Over tenant memory limits 或者 No memory or reach tenant memory limit。 -> A:调大全局 SQL 工作区的内存比例或者减少 --thread 并发数,例如。 -> `set global ob_sql_work_area_percentage=30;` -- Default 5 - -> Q:OBLOADER 运行报错 `No tables are exists in the schema: "xxx"`。 -> A:`--table 't1,t2'` 选项指定的表名一定是数据库中已经定义的表名,且大小写需要保持一致。MySQL 模式下默认表名为小写,Oracle 模式下默认表名为大写。如果 Oracle 中定义的表名为小写,表名左右需要使用中括号。例如:`--table '[t1]'` 表示小写的表名。 - -> Q:OBLOADER 运行报错 `The xxx files are not found in the path: "xxx"`。 -> A:要求 `-f` 指定的目录中的数据文件的名称与表名相同且大小写一致。MySQL 模式下默认表名为小写,Oracle 模式下默认表名为大写。例如:`--table 't1'`目录中的数据文件须为 t1.csv 或者 t1.sql,不能是 T1.csv 或者其它的文件名。 - -> Q:OBLOADER 运行报错 `The manifest file: "xxx" is missing`。 -> A:元数据文件 MANIFEST.bin 是 OBDUMPER 导出时产生的。使用其它工具导出时没有元数据文件。通过指定 `--external-data` 选项可跳过检查元数据文件。 - -> Q:OBLOADER 导入 Delimited Text 格式时报错 `Index:0,Size:0`。 -> A:出现这种错误的原因是数据中存在回车符/换行符,请先使用脚本删除数据中的回车符/换行符后再导入数据。 - -> Q:OceanBase 数据库 MySQL 模式下,连接 ODP (Sharding) 逻辑库导入 KEY 分区表数据时,OceanBase Database Proxy (ODP) 显示内存不足且 OBLOADER 运行报错 `socket was closed by server`。 -> A:设置 proxy_mem_limited 参数的权限,确认是否有外部依赖,ODP 默认内存限制为 2GB。连接 ODP (Sharding) 逻辑库且通过 OBLOADER 导入数据时需要使用 root@proxysys 账号权限,修改逻辑库内存限制语句如下。 -> `ALTER proxyconfig SET proxy_mem_limited = xxg;` - -> Q:如何解决使用 OBDUMPER 导出时遇到 OOM 错误? -> A:首先修改 bin/obdumper 脚本中的 JAVA 虚拟机内存参数,其次排除 OpenJDK GC Bug。 - -> Q:如何在调试模式下运行 OBDUMPER 排查问题? -> A:直接运行 bin 目录下的调试脚本,例如:obdumper-debug。 - -> Q:为表配置了控制文件,导入或者导出的数据为什么没有生效? -> A:要求控制文件的名称与表名相同且大小写一致。MySQL 默认表名为小写,Oracle 默认表名为大写。 - -> Q:OBDUMPER 导出数据时,为什么空表未产生空数据文件? -> A:默认空表不会产生对应的空文件。`--retain-empty-files` 选项可保留空表所对应的空文件。 - -> Q:OBDUMPER 运行脚本时,命令行参数未被正常解析的原因是什么? -> A:可能是命令行参数中存在特殊符号。Linux 平台上运行 OBDUMPER,密码中存在大于号(> 是重定向符),导致运行的日志出现丢失。因此在不同的运行平台请使用正确的引号。 -> -> - Windows 平台参数使用双引号。例如:`--table "*"`。 -> - 类 Linux 平台参数使用单引号。例如:`--table '*'`。 - -> Q:OBDUMPER 指定 --query-sql '大查询语句' 导出数据过程中报错 `Connection reset`。 -> A:登入 sys 租户,将 OBProxy 配置参数 `client_tcp_user_timeout` 和 `server_tcp_user_timeout` 设置为 0。 - -> Q:OBDUMPER 启动报错 `Access denied for user 'root'@'xxx.xxx.xxx.xxx'`。 -> A:OBDUMPER 默认依赖 root@sys 用户和密码。如果集群中已为 root@sys 用户设置密码,请在命令行中输入 `--sys-password` 选项并指定正确的 root@sys 用户的密码。 - -> Q:OBDUMPER 运行报错 `The target directory: "xxx" is not empty`。 -> A:为防止数据覆盖,导出数据前,OBDUMPER 会检查输出目录是否为空(使用 `--skip-check-dir` 选项可跳过此检查)。 - -> Q:OBDUMPER 运行报错 `Request to read too old versioned data`。 -> A:当前查询所依赖的数据版本已经被回收,用户需要根据查询设置 UNDO 的保留时间。 -> 例如:`set global undo_retention=xxx`。默认单位:秒。 - -> Q:OBDUMPER 运行报错 `ChunkServer out of disk space`。 -> A:由于 \_temporary_file_io_area_size 参数值过小引起存储块溢出错误,可修改该系统配置参数,例如。 -> 使用 `SELECT * FROM oceanbase.__all_virtual_sys_parameter_stat WHERE name='_temporary_file_io_area_size';` 命令查询该参数值,并使用 `ALTER SYSTEM SET _temporary_file_io_area_size = 20;` 命令修改该参数值。 - -> Q:OBDUMPER 查询视图报错 `SELECT command denied to user 'xxx'@'%' for table SYS.XXX`。 -> A:由于用户无访问内部表或者视图的权限,需要运行语句 `GRANT SELECT SYS.XXX TO xxx;` 为用户进行授权。 - -> Q:obloader 怎么一次性加载多个文件导入? -> A:数据文件放置一个文件夹内,`-f` 参数指定文件夹即可。 - -> Q:load data 怎么导入多个文件? -> A:INFILE 参数后面跟多个文件路径,逗号分隔,暂时不支持通配符多匹配。 - -> Q:obdumper 导出数据不能合并成单文件? -> A:多个表的数据或者是多个表的表结构 不能合到一个文件中。--file-name 参数是针对单表合并的,一张表对应的子文件指的是 比如分区表,或者分区大表被切分 会导出多个子文件,合并是针对这个场景的。 - ---- - -> **问题现象** -> -> 使用 obloader V3.0.0 执行 `obdumper --version` 命令时报错 `Invalid usage long options max width 60. Value must not exceed width`。 -> -> **可能原因** -> -> 已知的命令行框架 bug,obloader V4.0.0 已修复。 -> -> **解决方案** -> -> 如图所示,编辑 obdumper 脚本,增加参数 `-Dpicocli.usage.width=180`。 -> ![image.png](/img/FAQ/all_faq/1673161412003-95726b07-dd81-4714-a19d-b91d42b29408.png) - ---- - -> **问题现象** -> -> 使用 obloader V3.0.0 执行 obloader 导入时遇到报错 `Invalid usage long options max width 60. Value must not exceed width(55) - 20`。 -> ![image.png](/img/FAQ/all_faq/1671345885973-1fddcabc-cd28-4140-a522-d821a9d30843.png) -> -> **可能原因** -> -> 已知的命令行框架 bug,obloader V4.0.0 已修复。 -> -> **解决方案** -> -> 可在运行脚本中添加 jvm 启动参数 `-Dpicocli.usage.width=180`。 - ---- - -> **问题现象** -> -> OBLoader4.2.6 在 openEuler 系统上执行报错: -> `The stack size specified is too small, Specify at least 456k` > `Error: Could not create the Java Virtual Machine.` > `Error: A fatal exception has occurred. Program will exit.` -> -> **可能原因** -> -> 列举 JVM -Xss 在不同的操作系统的最小值: -> 操作系统 参数 最小值 -> CentOS -Xss 228k -> Arm -Xss 352k -> Euler -Xss 456k -> -> **解决方案** -> -> 如果在其它系统上运行 obloader/obdumper 报错 -> vim 编辑 obloader/obdumper 运行脚本,修改 JVM -Xss 栈大小即可。 - ---- - -> **问题现象** -> -> 使用 obloader 报错,提示:`reason:'gbk'`。 -> -> **可能原因** -> -> obloader 默认采用 UTF-8 编码处理数据。 -> -> **解决方案** -> -> 检查数据文件的编码是不是 UTF-8,命令行选项中有 `--file-encoding 'UTF-8'` 选项,指定文件内容的读取编码。 - ---- - -> **问题现象** -> -> OBloader4.2.5 导入失败,告警: -> `[INFO] File: "/data/xxx/x1.sql" is not contained, ignore it` > `[INFO] Find 0 resources in local path: "/data/xxx/data" success. Elapsed: 204.5 ms ` > `[WARN] No subfiles are generated from path: /data/xxx/data`。 -> -> **可能原因** -> -> `Find 0 resources in local path` 说明未找到导入文件。在不显式指定 `'-f'` 为具体文件路径时(-f 为目录),obloader 将通过文件名规则匹配的方式。 -> -> **解决方案** -> -> -f 指定为目录路径,或通过 --file-regular-expression 指定需导入文件名的正则匹配规则。 - ---- - -> **问题现象** -> -> obdumper4.2.5 版本导出表字段时间为`'0000-00-00'`或`'0000-00-00 00:00:00.000'`的数据导出为 null。 -> -> **可能原因** -> -> obdumper 默认行为,因为'0000-00-00'非正常时间格式。 -> -> **解决方案** -> -> 导出增加 --preserve-zero-datetime 参数,保留时间数据原有格式。 - ---- - -> **问题现象** -> -> obdumper4.2.5 自动-D,--table 选项不生效。 -> -> **可能原因** -> -> 和--all 参数互斥。 -> -> **解决方案** -> -> --all 选项与任意的数据库对象选项之间都是互斥的,不可同时指定,如果同时指定 --all 选项与任意的数据库对象选项,则会优先执行 --all 选项命令。 - ---- - -## **其他** - -> **问题现象** -> -> 使用 OBD V1.5.0 版本执行 mysqltest 失败,报错 `mysqltest: At line 85: Version format 4 has not yet been implemented`。 -> -> **可能原因** -> -> 获取的 mysqltest 的二进制版本不是最新的,可以更新 OBClient,获取最新的 mysqltest 可执行文件。 -> -> **解决方案** -> -> 更新为最新的 OBClient。 - ---- - -> **问题现象** -> -> 使用 dbcat 迁移数据到 OceanBase 数据库(3.1.x 版本)时报错 `The table charset: ''latin1" is unsupported in OBMYSQL_2.2.50(2.2.50). Object: test.t2`。 -> -> **可能原因** -> -> OceanBase 数据库 3.x 社区版本支持的字符集非常有限,当从 MySQL 中导出数据时,要确保数据库里的字符都能正确输出到文件中。通过 vim 命令下的 `:set fileencoding` 命令可以查看文件的编码,一般建议都是 utf-8。 这样通过文件迁移 MySQL 数据时就不会出现乱码现象。 -> -> **解决方案** -> -> 社区版 4.x 版本后常见的字符集基本上都支持了,可使用最新的 OceanBase 数据库 4.x 社区版本,或者保证字符集保持一致。可修改 /etc/my.cnf 文件,参考文档:[centos 修改 mysql 字符集 - 一像素 - 博客园](https://www.cnblogs.com/onepixel/p/9154884.html)。 -> -> - 如果上述无法解决,可能是因为只修改了数据库编码,而表的默认编码没有修改,可再执行 `alter table t1 convert to charset uft8;` 命令。 -> -> - 如果仍无法解决可重新创建数据库,创建时就指定好编码,涉及命令:`create database test character set utf8 collate utf8_general_ci;`。 - ---- - -> **问题现象** -> -> oblogproxy 运行后在/usr/local/oblogproxy/run 目录下生成大量文件文件,造成磁盘爆满 -> -> **可能原因** -> -> 该日志默认是开启轮转清理,默认是 20G 或者 40 个文件。可能是磁盘太小未达到清理阀值。 -> -> **解决方案** -> -> 可手动删除日志,或者修改 obcdc 的日志个数 max_log_file_count 参数配置。 - ---- - -> **问题现象** -> -> 部署 oblogproxy 时 out.log 日志报错: -> `Failed to decrypt(final), ret:0` > `Failed to decrypt: root`。 -> -> **可能原因** -> -> conf.json 中 ob_sys_username 和 ob_sys_password 需要使用加密字符串,非明文信息。 -> -> **解决方案** -> -> 执行 `sh run.sh config_sys ${username} ${password}` 自动配置或者 `./bin/logproxy -x ${username}` 得到加密字符串后手动配置。 diff --git a/docs/user_manual/user_best_practices/about_oceanbase/_index.md b/docs/user_manual/user_best_practices/about_oceanbase/_index.md deleted file mode 100644 index 231b98416..000000000 --- a/docs/user_manual/user_best_practices/about_oceanbase/_index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 关于 OceanBase -weight: 1 ---- diff --git a/docs/user_manual/user_best_practices/about_oceanbase/overall_architecture.md b/docs/user_manual/user_best_practices/about_oceanbase/overall_architecture.md deleted file mode 100644 index ea44058fa..000000000 --- a/docs/user_manual/user_best_practices/about_oceanbase/overall_architecture.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: 整体架构 -weight: 2 ---- - -# **整体架构** - -OceanBase 集群默认是 n-n-n 的三副本架构,代表三个可用区(Zone),每个 Zone 内都有 n 个节点。 - -如下图所示是 2-2-2 的集群架构图,每个 Zone 内有两个节点,默认数据分片是三副本。应用访问默认通过 OBProxy 进行连接,OBProxy 会解析 SQL 内容,自动将请求下发到对应的 OBServer 并返回结果。 - -![image.png](/img/about_oceanbase/overall_architecture/framework1.jpeg) - -## **名词定义** - -**副本** - -指的是实际的分片数据,单分片最大的副本数量不会超过 Zone 的数量。 - -**Locality** - -用来描述一个表的副本类型以及分布位置的方式。 - -**Resource Pool** - -资源池,每个 Unit 都归属于一个资源池,每个资源池由若干个 Unit 组成,资源池是资源分配的基本单位,同一个资源池内的各个 Unit 具有相同的资源规格,即该资源池内 Unit 的物理资源大小都相同。您可参考 [创建资源池](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699432) 一文创建资源池。 - -**Server** - -通常代表 OBServer,OceanBase 的数据库服务,每个 Zone 内有 1~n 个 Server。 - -**Tablet** - -数据分片,存储层以一张表或者一个分区为粒度提供数据存储与访问,每个分区对应一个用于存储数据的 Tablet,用户定义的非分区表也会对应一个 Tablet,每个分片的副本数量由 Locality 定义。 - -**Tenant** - -租户,类似于传统数据库的数据库实例,租户通过资源池与资源关联,从而独占一定的资源配额,可以动态调整资源配额。在租户下可以创建 Database、表、用户等数据库对象。部署 OceanBase 数据库时默认创建 sys 租户,sys 租户仅做集群管理使用,业务使用或者压测建议创建自己的业务租户。 - -创建租户时必须先创建 Unit 和 Resource Pool,或者使用已有的 Unit 和 Resource Pool,您可参考 [创建用户租户](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001702215) 一文创建用户租户。 - -**Unit** - -资源单元,OceanBase 数据库按照 Unit 来管理物理资源,是 CPU、内存、存储空间、IOPS 等物理资源的集合。您可参考 [创建资源单元](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699430) 一文创建资源单元。 - -**Zone** - -集群的可用区,不同的可用区可以在同一机房,也可以在不同机房;每个 Zone 内的服务器也同样,可以在同一机房也可以不同机房,建议每个 Zone 内的服务器都部署在同一机房,并且建议 Zone 之间的网络延迟不要太高。 diff --git a/docs/user_manual/user_best_practices/about_oceanbase/overview.md b/docs/user_manual/user_best_practices/about_oceanbase/overview.md deleted file mode 100644 index e8023af4f..000000000 --- a/docs/user_manual/user_best_practices/about_oceanbase/overview.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: 概览 -weight: 1 ---- -# **概览** - -OceanBase 数据库是一款原生的分布式关系数据库,它是完全由阿里巴巴和蚂蚁集团自主研发的项目。OceanBase 数据库构建在通用服务器集群上,基于 Paxos 协议和分布式架构,提供金融级高可用和线性伸缩能力,不依赖特定硬件架构,具备高可用、线性扩展、高性能、低成本等核心技术优势。 - -OceanBase 数据库具有如下特点: - -- 高可用 - - 单服务器故障能够自愈,支持跨城多机房容灾,数据零丢失,可满足金融行业 6 级容灾标准(RPO=0,RTO<=30 秒)。 - -- 线性扩展 - - 透明扩展,自动负载均衡,应用透明的水平扩展,集群规模可超过 1500 节点,数据量可达 PB 级,单表记录万亿行。 - -- MySQL/Oracle 高度兼容 - - 社区版兼容 MySQL 协议、语法和使用习惯,MySQL 客户端工具可以直接访问 OceanBase 数据库。 - - 企业版兼容 MySQL、Oracle 协议,需要使用 OceanBase 自己的驱动才可以访问 OceanBase 数据库的 Oracle 租户。 - - > **说明** - > - > MySQL 从 5.6 开始兼容,Oracle 从 Oracle 11g 开始兼容。 - -- 高性能 - - 准内存级数据变更操作、独创的编码压缩技术,结合线性水平扩展,TPC-C 测试达到 7.07 亿 tpmC。 - -- 低成本 - - 使用 PC 服务器和低端 SSD,高存储压缩率降低存储成本,高性能降低计算成本,多租户充分利用系统资源。 - -- 多租户 - - 原生支持多租户架构,同一套数据库集群可以为多个独立业务提供服务,租户间数据隔离,降低部署和运维成本。 - -OceanBase 数据库支持支付宝的全部核心业务,以及银行、保险、证券、运营商等多个行业的数百个客户的核心业务系统。 - -## **OceanBase 社区版简介** - -OceanBase 数据库社区版使用 [MulanPubL-2.0 许可证](http://license.coscl.org.cn/MulanPubL-2.0) ,您可以免费复制及使用源代码。当您修改或分发源代码时,请遵守木兰协议。 - -OceanBase 社区版官方网站地址是:https://open.oceanbase.com - -### **下载方法** - -- 官网下载:https://www.oceanbase.com/softwarecenter - -- GitHub 下载:https://github.com/oceanbase/oceanbase/releases/ - -- 阿里云 Yum 源:https://mirrors.aliyun.com/oceanbase/community/stable/el/ - -### **适合社区版的业务场景** - -- MySQL 5.6/5.7 实例规模很大的场景。 - - MySQL 实例规模大,需要自动化运维平台。自动化运维平台在处理 MySQL 异常宕机切换和主备不一致问题时很可能需要 DBA 介入。高可用和强一致问题是 MySQL 最大的风险, OceanBase 的多租户、高可用和强一致能力可以彻底解决这个痛点。 - -- MySQL 5.6/5.7 数据量非常大、存储成本高的场景。 - - MySQL 业务数据量增长到几 T 以上时,查询和读写性能可能会下降,大表 DDL 时间变长,风险增加。单机磁盘容量可能到达扩容瓶颈。 - - OceanBase MySQL 租户的在线 DDL,数据存储高压缩比可以解决这些痛点。 - -- 业务访问压力大或者变化大的场景。 - - 业务访问压力大,基于 MySQL 改造的分布式数据库中间件产品能分担一定程度的业务压力和存储空间压力,但是缺乏跨节点的强一致性查询,以及需要分布式事务中间件协调事务,扩容的时候可能需要数据逻辑拆分(俗称拆库拆表),运维成本高,风险高。 - - OceanBase MySQL 租户提供分区表的水平拆分方案,提供原生的 SQL 和事务能力,对业务透明。并且支持在线扩容和缩容,内部数据迁移异步进行,具备高可用能力,不怕扩容和缩容过程中出现故障,可以解决上面这些痛点。 - -- 交易数据库上的复杂查询场景。 - - 交易数据库上有少量复杂的查询场景,涉及到的数据量很大,传统解决方案是通过数据同步到数据仓库进行查询。 - - OceanBase 数据库的 SQL 引擎同时满足 OLTP 和 OLAP 场景,采用经过 Oracle 复杂业务场景检验的先进的 SQL 优化器技术,能支持复杂的 SQL 优化和高效执行。因此可以在交易数据库上直接做复杂查询,减少不必要的数据同步。此外,OceanBase 还提供不同程度的读写分离技术来控制复杂查询对交易场景的影响。 - -其他更多场景待实践总结,敬请关注。 - -## **联系我们** - -欢迎 OceanBase 爱好者、用户和客户联系我们反馈问题: - -- 社区版官网论坛:https://ask.oceanbase.com/ - -- 社区版项目网站提 Issue:https://github.com/oceanbase/oceanbase/issues - -- 钉钉群:群号 33254054 diff --git a/docs/user_manual/user_best_practices/appendix/_index.md b/docs/user_manual/user_best_practices/appendix/_index.md deleted file mode 100644 index 08a3ab521..000000000 --- a/docs/user_manual/user_best_practices/appendix/_index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 附录 -bookCollapseSection: true -weight: 100 ---- diff --git a/docs/user_manual/user_best_practices/appendix/obtest_config.md b/docs/user_manual/user_best_practices/appendix/obtest_config.md deleted file mode 100644 index 5862d83bb..000000000 --- a/docs/user_manual/user_best_practices/appendix/obtest_config.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: 配置文件事例 -weight: 1 ---- - -```yaml -## Only need to configure when remote login is required -user: - username: admin - password: **** -# key_file: your ssh-key file path if need -# port: your ssh port, default 22 -# timeout: ssh connection timeout (second), default 30 -oceanbase-ce: - servers: - - name: server1 - # Please don't use hostname, only IP can be supported - ip: x.x.x.32 - - name: server2 - ip: x.x.x.33 - - name: server3 - ip: x.x.x.34 - global: - # Please set devname as the network adaptor's name whose ip is in the setting of severs. - # if set severs as "127.0.0.1", please set devname as "lo" - # if current ip is 192.168.1.10, and the ip's network adaptor's name is "eth0", please use "eth0" - devname: eth0 - # if current hardware's memory capacity is smaller than 50G, please use the setting of "mini-single-example.yaml" and do a small adjustment. - memory_limit: 12G # The maximum running memory for an observer - # The reserved system memory. system_memory is reserved for general tenants. The default value is 30G. - system_memory: 5G - cpu_count: 6 - datafile_size: 30G # Size of the data file. - log_disk_size: 20G # The size of disk space used by the clog files. - enable_syslog_wf: false # Print system logs whose levels are higher than WARNING to a separate log file. The default value is true. - enable_syslog_recycle: true # Enable auto system log recycling or not. The default value is false. - max_syslog_file_count: 4 # The maximum number of reserved log files before enabling auto recycling. The default value is 0. - # observer cluster name, consistent with obproxy's cluster_name - appname: obtest - # root_password: # root user password, can be empty - root_password: **** - # proxyro_password: # proxyro user pasword, consistent with obproxy's observer_sys_password, can be empty - proxyro_password: **** - # In this example , support multiple ob process in single node, so different process use different ports. - # If deploy ob cluster in multiple nodes, the port and path setting can be same. - server1: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone1 - server2: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone2 - server3: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone3 -obproxy-ce: - # Set dependent components for the component. - # When the associated configurations are not done, OBD will automatically get the these configurations from the dependent components. - depends: - - oceanbase-ce - servers: - - x.x.x.32 - - x.x.x.33 - - x.x.x.34 - global: - listen_port: 2883 # External port. The default value is 2883. - prometheus_listen_port: 2884 # The Prometheus port. The default value is 2884. - home_path: /home/admin/obproxy - # oceanbase root server list - # format: ip:mysql_port;ip:mysql_port. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - # rs_list: 192.168.1.2:2881;192.168.1.3:2881;192.168.1.4:2881 - enable_cluster_checkout: false - # observer cluster name, consistent with oceanbase-ce's appname. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - cluster_name: obtest - skip_proxy_sys_private_check: true - enable_strict_kernel_release: false - # obproxy_sys_password: # obproxy sys user password, can be empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - obproxy_sys_password: **** - # observer_sys_password: # proxyro user pasword, consistent with oceanbase-ce's proxyro_password, can be empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - observer_sys_password: **** - ``` diff --git a/docs/user_manual/user_best_practices/appendix/obtest_config2.md b/docs/user_manual/user_best_practices/appendix/obtest_config2.md deleted file mode 100644 index 798c3ed97..000000000 --- a/docs/user_manual/user_best_practices/appendix/obtest_config2.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: 扩容配置文件事例 -weight: 2 ---- - -```yaml -## Only need to configure when remote login is required -user: - username: admin - password: xxx -# key_file: your ssh-key file path if need -# port: your ssh port, default 22 -# timeout: ssh connection timeout (second), default 30 -oceanbase-ce: - servers: - - name: server4 - # Please don't use hostname, only IP can be supported - ip: x.x.x.35 - - name: server5 - ip: x.x.x.36 - - name: server6 - ip: x.x.x.37 - global: - # Please set devname as the network adaptor's name whose ip is in the setting of severs. - # if set severs as "127.0.0.1", please set devname as "lo" - # if current ip is 192.168.1.10, and the ip's network adaptor's name is "eth0", please use "eth0" - devname: eth0 - # if current hardware's memory capacity is smaller than 50G, please use the setting of "mini-single-example.yaml" and do a small adjustment. - memory_limit: 12G # The maximum running memory for an observer - # The reserved system memory. system_memory is reserved for general tenants. The default value is 30G. - system_memory: 5G - cpu_count: 6 - datafile_size: 30G # Size of the data file. - log_disk_size: 20G # The size of disk space used by the clog files. - enable_syslog_wf: false # Print system logs whose levels are higher than WARNING to a separate log file. The default value is true. - enable_syslog_recycle: true # Enable auto system log recycling or not. The default value is false. - max_syslog_file_count: 4 # The maximum number of reserved log files before enabling auto recycling. The default value is 0. - # observer cluster name, consistent with obproxy's cluster_name - appname: obtest - # root_password: # root user password, can be empty - root_password: xxx - # proxyro_password: # proxyro user pasword, consistent with obproxy's observer_sys_password, can be empty - proxyro_password: xxx - # In this example , support multiple ob process in single node, so different process use different ports. - # If deploy ob cluster in multiple nodes, the port and path setting can be same. - server4: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone1 - server5: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone2 - server6: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone3 - ``` diff --git a/docs/user_manual/user_best_practices/appendix/obtest_config3.md b/docs/user_manual/user_best_practices/appendix/obtest_config3.md deleted file mode 100644 index b9f296ff5..000000000 --- a/docs/user_manual/user_best_practices/appendix/obtest_config3.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: 扩容后配置文件事例 -weight: 3 ---- -```yaml -## Only need to configure when remote login is required -user: - username: admin - password: xxx -# key_file: your ssh-key file path if need -# port: your ssh port, default 22 -# timeout: ssh connection timeout (second), default 30 -oceanbase-ce: - servers: - - name: server1 - # Please don't use hostname, only IP can be supported - ip: x.x.x.32 - - name: server2 - ip: x.x.x.33 - - name: server3 - ip: x.x.x.34 - - name: server4 - # Please don't use hostname, only IP can be supported - ip: x.x.x.35 - - name: server5 - ip: x.x.x.36 - - name: server6 - ip: x.x.x.37 - global: - # Please set devname as the network adaptor's name whose ip is in the setting of severs. - # if set severs as "127.0.0.1", please set devname as "lo" - # if current ip is 192.168.1.10, and the ip's network adaptor's name is "eth0", please use "eth0" - devname: eth0 - # if current hardware's memory capacity is smaller than 50G, please use the setting of "mini-single-example.yaml" and do a small adjustment. - memory_limit: 12G # The maximum running memory for an observer - # The reserved system memory. system_memory is reserved for general tenants. The default value is 30G. - system_memory: 5G - cpu_count: 6 - datafile_size: 30G # Size of the data file. - log_disk_size: 20G # The size of disk space used by the clog files. - enable_syslog_wf: false # Print system logs whose levels are higher than WARNING to a separate log file. The default value is true. - enable_syslog_recycle: true # Enable auto system log recycling or not. The default value is false. - max_syslog_file_count: 4 # The maximum number of reserved log files before enabling auto recycling. The default value is 0. - # observer cluster name, consistent with obproxy's cluster_name - appname: obtest - # root_password: # root user password, can be empty - root_password: xxx - # proxyro_password: # proxyro user pasword, consistent with obproxy's observer_sys_password, can be empty - proxyro_password: xxx - # In this example , support multiple ob process in single node, so different process use different ports. - # If deploy ob cluster in multiple nodes, the port and path setting can be same. - server1: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone1 - server2: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone2 - server3: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone3 - server4: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone1 - server5: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone2 - server6: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /ob_data/1 - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /ob_data/log1 - zone: zone3 -obproxy-ce: - # Set dependent components for the component. - # When the associated configurations are not done, OBD will automatically get the these configurations from the dependent components. - depends: - - oceanbase-ce - servers: - - x.x.x.32 - - x.x.x.33 - - x.x.x.34 - global: - listen_port: 2883 # External port. The default value is 2883. - prometheus_listen_port: 2884 # The Prometheus port. The default value is 2884. - home_path: /home/admin/obproxy - # oceanbase root server list - # format: ip:mysql_port;ip:mysql_port. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - # rs_list: 192.168.1.2:2881;192.168.1.3:2881;192.168.1.4:2881 - enable_cluster_checkout: false - # observer cluster name, consistent with oceanbase-ce's appname. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - cluster_name: obtest - skip_proxy_sys_private_check: true - enable_strict_kernel_release: false - # obproxy_sys_password: # obproxy sys user password, can be empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - obproxy_sys_password: xxx - # observer_sys_password: # proxyro user pasword, consistent with oceanbase-ce's proxyro_password, can be empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - observer_sys_password: xxx - ``` diff --git a/docs/user_manual/user_best_practices/data_migration/_index.md b/docs/user_manual/user_best_practices/data_migration/_index.md deleted file mode 100644 index 948fe54de..000000000 --- a/docs/user_manual/user_best_practices/data_migration/_index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 数据迁移 -weight: 3 ---- diff --git a/docs/user_manual/user_best_practices/data_migration/canal.md b/docs/user_manual/user_best_practices/data_migration/canal.md deleted file mode 100644 index edb5805bb..000000000 --- a/docs/user_manual/user_best_practices/data_migration/canal.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -title: Canal -weight: 3 ---- - -# **使用 Canal 从 MySQL 数据库同步数据到 OceanBase 数据库** - -Canal 是 Alibaba 开源的一个产品,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。 - -本文档主要介绍使用 Canal 的 canal.deployer 和 canal.adapter 组件从 MySQL 数据库同步数据至 OceanBase 数据库。 - -> **说明** -> -> 您可参考官网 OceanBase 数据库文档 [使用 Canal 从 OceanBase 数据库同步数据到 MySQL 数据库](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001697230) 一文使用 Canal 和 oblogproxy 组件从 OceanBase 数据库同步数据至 MySQL 数据库。 - -## **架构原理** - -- canal deployer:Canal 的 Server 端,进行 binlog 到 CanalEntry 的转换。 - -- canal adapter:Canal 的客户端适配器,解析 CanalEntry 并将增量变动同步到目的端。 - -Canal 相关信息访问地址:[https://github.com/alibaba/canal/releases]。 - -## **操作步骤** - -### **步骤一:MySQL 相关设置** - -1. 修改 MySQL 配置文件 `my.cnf`。 - - 配置文件 `my.cnf` 位置:`/etc/my.cnf`。先开启 binlog 写入功能,配置 binlog-format 为 ROW 模式,配置后需重启 MySQL 生效。 - - 示例如下: - - ```bash - log-bin=mysql-bin # 开启 binlog - binlog-format=ROW # 选择 ROW 模式 - server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复 - ``` - -2. 创建一个 MySQL 用户。 - - 创建一个连接 MySQL 的用户,用户名为 `canal`,并为 `canal` 授予所有库的读写权限。 - - 示例如下: - - ```sql - MySQL [(none)]> CREATE USER 'canal'@'%' IDENTIFIED BY '******'; - Query OK, 0 rows affected - - MySQL [(none)]> GRANT SELECT,REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'canal'@'%'; - Query OK, 0 rows affected - - MySQL [(none)]> FLUSH PRIVILEGES; - Query OK, 0 rows affected - ``` - -3. 创建一个测试数据库。 - - 创建数据库 `test_mysql_to_ob`,表 `tbl1` 和 `tbl2`,并插入数据。 - - ```sql - MySQL [(none)]> CREATE DATABASE test_mysql_to_ob; - Query OK, 1 row affected - - MySQL [(none)]> USE test_mysql_to_ob; - Database changed - - MySQL [test_mysql_to_ob]> CREATE TABLE tbl1(col1 INT PRIMARY KEY, col2 VARCHAR(20),col3 INT); - Query OK, 0 rows affected - - MySQL [test_mysql_to_ob]> INSERT INTO tbl1 VALUES(1,'China',86),(2,'Taiwan',886),(3,'Hong Kong',852),(4,'Macao',853),(5,'North Korea',850); - Query OK, 5 rows affected - Records: 5 Duplicates: 0 Warnings: 0 - - MySQL [test_mysql_to_ob]> CREATE TABLE tbl2(col1 INT PRIMARY KEY,col2 VARCHAR(20)); - Query OK, 0 rows affected - - MySQL [test_mysql_to_ob]> INSERT INTO tbl2 VALUES(86,'+86'),(886,'+886'),(852,'+852'),(853,'+853'),(850,'+850'); - Query OK, 5 rows affected - Records: 5 Duplicates: 0 Warnings: 0 - ``` - -### **步骤二:Canal 的下载和安装** - -> **说明** -> -> 本节以 Canal V1.1.5 为例提供操作指导,仅供参考。推荐使用 Canal V1.1.5 版本。 - -1. 下载软件包。 - - 下载 [canal.deployer-1.1.5.tar.gz](https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.deployer-1.1.5.tar.gz)。 - - ```bash - wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.deployer-1.1.5.tar.gz - ``` - -2. 将压缩包解压至目录 `/Canal_Home/canal`。 - - ```bash - mkdir /Canal_Home/canal && tar -zxvf canal.deployer-1.1.5.tar.gz -C /Canal_Home/canal - ``` - -3. 修改配置文件。 - - canal.deployer 默认的配置文件为 `conf/canal.properties` 和 `conf/example/instance.properties`。这里是默认创建了一个 `instance` 叫 `example`。需要修改 `example` 的实例配置文件,修改数据库连接地址、用户名和密码。`canal.instance.connectionCharset` 代表数据库的编码方式对应到 Java 中的编码类型,比如 `UTF-8`,`GBK`,`ISO-8859-1`。 - - 示例如下: - - ```bash - vi conf/example/instance.properties - - # mysql serverId - canal.instance.mysql.slaveId = 1234 - #position info,需要改成自己的数据库信息 - canal.instance.master.address = xxx.xxx.xxx.xxx:3306 - canal.instance.master.journal.name = - canal.instance.master.position = - canal.instance.master.timestamp = - #canal.instance.standby.address = - #canal.instance.standby.journal.name = - #canal.instance.standby.position = - #canal.instance.standby.timestamp = - #username/password,需要改成自己的数据库信息 - canal.instance.dbUsername = canal - canal.instance.dbPassword = ****** - canal.instance.defaultDatabaseName = - canal.instance.connectionCharset = UTF-8 - #table regex - canal.instance.filter.regex = .*\\..* - ``` - -4. 启动 Canal Server。 - - ```bash - cd /Canal_Home/canal && sh bin/startup.sh - ``` - -5. 查看 server 日志。 - - ```bash - cat logs/canal/canal.log - ``` - -6. 查看 instance 的日志。 - - ```bash - tail -f logs/canal/canal.log - tail -f logs/example/example.log - ``` - -7. 如需停止服务可执行下述命令。 - - ```bash - cd /Canal_Home/canal && sh bin/stop.sh - ``` - -### **步骤三:部署 RDB 适配器** - -Canal Adapter 提供了对多种目标容器的支持,对于 OceanBase 来说,主要使用它的 rdb 模块,目的端容器为 OceanBase。 - -1. 下载软件包。 - - 下载 [canal.adapter-1.1.5.tar.gz](https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.adapter-1.1.5.tar.gz)。 - - ```bash - wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.adapter-1.1.5.tar.gz - ``` - -2. 将压缩包解压至目录 `/Canal_Home/adapter`。 - - ```bash - mkdir /Canal_Home/adapter && tar -zxvf canal.adapter-1.1.5.tar.gz -C /Canal_Home/adapter - ``` - -3. 修改启动器配置。 - - 修改启动器配置:`conf/application.yml`。首先指定 `adapter` 源端类型,通过 `mode` 指定,这里选择 `tcp`。后面就要指定 `canal.tcp` 相关属性,包括 `canal server` 的 IP 和端口,数据库的连接用户和密码。之后指定 `adapter` 目标端连接信息。`instance` 是源端实例名称,在 canal 部署的时候定义的。key 是自定义,名字后面有用。jdbc 相关属性是目标端 OceanBase 的连接方式,可以使用 MySQL 自带的驱动。 - - 示例如下: - - ```bash - mode: tcp #tcp kafka rocketMQ rabbitMQ - flatMessage: true - zookeeperHosts: - syncBatchSize: 1000 - retries: 0 - timeout: - accessKey: - secretKey: - consumerProperties: - # canal tcp consumer - canal.tcp.server.host: 127.0.0.1:11111 - canal.tcp.zookeeper.hosts: - canal.tcp.batch.size: 500 - canal.tcp.username: - canal.tcp.password: - canalAdapters: - - instance: example # canal instance Name or mq topic name - groups: - - groupId: g1 - outerAdapters: - - name: logger - - name: rdb - key: test_mysql_to_ob - properties: - jdbc.driverClassName: com.mysql.jdbc.Driver - jdbc.url: jdbc:mysql://xxx.xxx.xxx.xxx:2883/test_data?useUnicode=true - jdbc.username: root@mysql001#test4000 - jdbc.password: ****** - ``` - -4. RDB 映射文件。 - - 修改 `conf/rdb/mytest_user.yml` 文件。其中,`destination` 指定的是 `canal instance` 名称;`outerAdapterKey` 是前面定义的 `key`;`mirrorDb` 指定数据库级别 DDL 和 DML 镜像同步。 - - 映射有两种:一是按表映射;二是整库映射。下面以整库映射为例进行配置,注释部分为按表映射的配置: - - ```bash - [root@obce00 adapter]# cat conf/rdb/mytest_user.yml - #dataSourceKey: defaultDS - #destination: example - #groupId: g1 - #outerAdapterKey: mysql1 - #concurrent: true - #dbMapping: - # database: mytest - # table: user - # targetTable: mytest - # targetPk: - # id: id - # mapAll: true - # targetColumns: - # id: - # name: - # role_id: - # c_time: - # test1: - # etlCondition: "where c_time>={}" - # commitBatch: 3000 # 批量提交的大小 - # Mirror schema synchronize config - dataSourceKey: defaultDS - destination: example - groupId: g1 - outerAdapterKey: test_mysql_to_ob - concurrent: true - dbMapping: - mirrorDb: true - database: test_data - commitBatch: 1000 - ``` - -5. 启动 RDB。 - - > **说明** - > - > 如果使用了 OceanBase 的驱动,则将目标库 OceanBase 驱动包放入 `lib` 文件夹。 - - 启动 canal-adapter 启动器。 - - ```bash - cd /Canal_Home/adapter && sh bin/startup.sh - ``` - -6. 查看 RDB 日志。 - - ```bash - tail -f logs/adapter/adapter.log - ``` - -7. 查看数据同步情况。 - - 在 MySQL 源端写入数据,在 OceanBase 目标端查看数据同步。 - -8. 如需停止服务可执行下述命令 - - ```shell - cd /Canal_Home/adapter && sh bin/stop.sh - ``` - -## **功能限制** - -- 同步的表必须有主键。否则,源端删除无主键表的任意一笔记录,同步到目标端会导致整个表被删除。 - -- DDL 支持新建表、新增列。 diff --git a/docs/user_manual/user_best_practices/data_migration/datax.md b/docs/user_manual/user_best_practices/data_migration/datax.md deleted file mode 100644 index 0706e61d0..000000000 --- a/docs/user_manual/user_best_practices/data_migration/datax.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -title: Datax -weight: 2 ---- -# **使用 DataX 迁移 MySQL 数据到 OceanBase 数据库** - -本文仅给出使用 DataX 迁移 MySQL 数据到 OceanBase 数据库的示例,详细介绍可查看对应小节给出的参考文档。 - -## **MySQL 数据同步到 OceanBase** - -本节仅给出配置文件示例,详情可参照 OceanBase 数据库文档 [使用 DataX 迁移 MySQL 数据到 OceanBase 数据库](https://www.oceanbase.com/docs/community-observer-cn-10000000000900965) 一文。 - -配置文件如下: - -```json -{ - "job": { - "setting": { - "speed": { - "channel": 4 - }, - "errorLimit": { - "record": 0, - "percentage": 0.1 - } - }, - "content": [ - { - "reader": { - "name": "mysqlreader", - "parameter": { - "username": "tpcc", - "password": "********", - "column": [ - "*" - ], - "connection": [ - { - "table": [ - "bmsql_oorder" - ], - "jdbcUrl": ["jdbc:mysql://127.0.0.1:3306/tpccdb?useUnicode=true&characterEncoding=utf8"] - } - ] - } - }, - - "writer": { - "name": "oceanbasev10writer", - "parameter": { - "obWriteMode": "insert", - "column": [ - "*" - ], - "preSql": [ - "truncate table bmsql_oorder" - ], - "connection": [ - { - "jdbcUrl": "||_dsc_ob10_dsc_||obdemo:oboracle||_dsc_ob10_dsc_||jdbc:mysql://127.0.0.1:2883/tpcc?useLocalSessionState=true&allowBatch=true&allowMultiQueries=true&rewriteBatchedStatements=true", - "table": [ - "bmsql_oorder" - ] - } - ], - "username": "tpcc", - "password":"********", - "writerThreadCount":10, - "batchSize": 1000, - "memstoreThreshold": "0.9" - } - } - } - ] - } -} -``` - -## **加载 CSV 数据文件到 OceanBase 数据库** - -将 MySQL 数据迁移到 OceanBase 数据库时,如果源端和目标端不能同时跟 DataX 服务器网络联通,则需要通过 CSV 文件中转。本节仅给出配置文件示例,详情可参照 OceanBase 数据库文档 [使用 DataX 加载 CSV 数据文件到 OceanBase 数据库](https://www.oceanbase.com/docs/community-observer-cn-10000000000900960) 一文。 - -### **MySQL 数据导出为 CSV 文件** - -将 MySQL 数据导出为 CSV 文件。 - -配置文件如下: - -```bash -$cat job/bmsql_oorder_mysql2csv.json -{ - "job": { - "setting": { - "speed": { - "channel": 4 - }, - "errorLimit": { - "record": 0, - "percentage": 0.1 - } - }, - "content": [ - { - "reader": { - "name": "mysqlreader", - "parameter": { - "username": "tpcc", - "password": "********", - "column": [ - "*" - ], - "connection": [ - { - "table": [ - "bmsql_oorder" - ], - "jdbcUrl": ["jdbc:mysql://127.0.0.1:3306/tpccdb?useUnicode=true&characterEncoding=utf8"] - } - ] - } - }, - "writer": { - "name": "txtfilewriter", - "parameter": { - "path": "/tmp/tpcc/bmsql_oorder", - "fileName": "bmsql_oorder", - "encoding": "UTF-8", - "writeMode": "truncate", - "dateFormat": "yyyy-MM-dd hh:mm:ss" , - "nullFormat": "\\N" , - "fileFormat": "csv" , - "fieldDelimiter": "," - } - } - } - ] - } -} -``` - -### **CSV 文件导入到 OceanBase** - -将源端导出的 CSV 文件复制到目标端的 DataX 服务器上,然后导入到目标端 OceanBase 数据库中。 - -配置文件如下: - -```bash -$cat job/bmsql_oorder_csv2ob.json -{ - "job": { - "setting": { - "speed": { - "channel": 4 - }, - "errorLimit": { - "record": 0, - "percentage": 0.1 - } - }, - "content": [ - { - "reader": { - "name": "txtfilereader", - "parameter": { - "path": ["/tmp/tpcc/bmsql_oorder"], - "fileName": "bmsql_oorder", - "encoding": "UTF-8", - "column": ["*"], - "dateFormat": "yyyy-MM-dd hh:mm:ss" , - "nullFormat": "\\N" , - "fieldDelimiter": "," - } - }, - "writer": { - "name": "oceanbasev10writer", - "parameter": { - "obWriteMode": "insert", - "column": [ - "*" - ], - "preSql": [ - "truncate table bmsql_oorder" - ], - "connection": [ - { - "jdbcUrl": "||_dsc_ob10_dsc_||obdemo:oboracle||_dsc_ob10_dsc_||jdbc:mysql://127.0.0.1:2883/tpcc?useLocalSessionState=true&allowBatch=true&allowMultiQueries=true&rewriteBatchedStatements=true", - "table": [ - "bmsql_oorder" - ] - } - ], - "username": "tpcc", - "password":"********", - "writerThreadCount":10, - "batchSize": 1000, - "memstoreThreshold": "0.9" - } - } - } - ] - } -} -``` diff --git a/docs/user_manual/user_best_practices/data_migration/oblogproxy.md b/docs/user_manual/user_best_practices/data_migration/oblogproxy.md deleted file mode 100644 index a2b752321..000000000 --- a/docs/user_manual/user_best_practices/data_migration/oblogproxy.md +++ /dev/null @@ -1,828 +0,0 @@ ---- -title: OBLogProxy -weight: 4 ---- - -# **使用 Flink 同步 OceanBase 不同租户间数据** - -## **本文参照** - -[使用 Flink 同步 OceanBase 不同租户之间数据](https://ask.oceanbase.com/t/topic/35602487) - -## **概念介绍** - -- oblogproxy - - oblogproxy 是 OceanBase 数据库的增量日志代理服务,基于 liboblog,以服务的形式提供实时增量链路接入和管理能力,方便应用接入 OceanBase 增量日志。oblogproxy 能够解决在网络隔离的情况下,订阅增量日志的需求,并提供多种链路接入方式。开源项目地址:https://github.com/oceanbase/oblogproxy。 - -- Flink CDC - - Flink CDC 是一个使用 Apache License 2.0 协议的开源项目,支持从 MySQL、MariaDB、Oracle、MongoDB、SqlServer、TiDB、OceanBase 等数据库中实时地读取存量历史数据和增量变更数据。开源项目:https://github.com/ververica/flink-cdc-connectors。 - -## **使用场景** - -由于当前 OceanBase 社区版不支持跨租户访问,那么在一些用户的大数据场景下使用起来不方便,所以基于 Flink CDC 来同步不同租户的数据回写到第三个租户,最后应用访问第三个租户的数据即可,同时在同步过程中又可以进行数据的预处理,对应用层来说,数据访问更加简单。但需注意该操作也存在数据冗余的缺点。 - -本场景为 OceanBase 数据库不同租户之间的数据同步。oblogproxy 调用 liboblog,获取上游 OceanBase 数据库租户中的增量事务数据,Flink 捕获上游数据的变化,实时读取上游节点的全量数据和增量数据,进行计算和处理后发送数据给下游 OceanBase 租户。该方法同样也适用于 MySQL 与 OceanBase 以及 OceanBase 与 OceanBase 之间的数据同步。 - -## **前提条件** - -- java 1.8.0+。 - -- 部署 OceanBase 4.0 版本集群,并创建 3 个租户(本文示中为 flink_cdc_1、flink_cdc_2、flink_cdc_3)。 - -- 部署 oblogproxy。 - -- 部署 Flink-1.16.1 版本。 - -- Flink CDC - - - flink-sql-connector-oceanbase-cdc-2.3.0.jar(OceanBase 数据库写入 Flink) - - - flink-connector-jdbc-1.16.1.jar(Flink 写入 OceanBase 数据库) - -## **软件部署** - -### **部署 oblogproxy** - -1. 下载 oblogproxy 并解压。 - - 下载地址为:https://github.com/oceanbase/oblogproxy/releases4 - - ```shell - wget https://github.com/oceanbase/oblogproxy/releases/download/v1.1.0/oblogproxy-ce-for-4x-1.1.0-20221201191325.tar.gz - mkdir /home.admin/oblogproxy && tar -zxvf oblogproxy-ce-for-4x-1.1.0-20221201191325.tar.gz -C /home.admin/oblogproxy - ``` - -2. 配置环境变量。 - - ```bash - [admin@obtest004 ~]$ sudo vim /etc/profile - ## 请根据实际安装路径配置 - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/admin/oblogproxy/liboblog - [admin@obtest004 ~]$ source /etc/profile - ``` - -3. 确认 oblogproxy 依赖。 - - ```bash - [admin@obtest004 ~]$ cd oblogproxy - [admin@obtest004 oblogproxy]$ ldd ./bin/logproxy - linux-vdso.so.1 => (0x00007ffe701e8000) - libobcdc.so.4 => /home/admin/oblogproxy/liboblog/libobcdcso.4 (0x00002b3dbdb58000) - libaio.so.1 => /home/admin/oblogproxy/liboblog/libaio.so.1(0x00002b3dedc65000) - libmariadb.so.3 => /home/admin/oblogproxy/libobloglibmariadb.so.3 (0x00002b3dede67000) - libpthread.so.0 => /lib64/libpthread.so.0(0x00002b3dee0cd000) - libdl.so.2 => /lib64/libdl.so.2 (0x00002b3dee2e9000) - librt.so.1 => /lib64/librt.so.1 (0x00002b3dee4ed000) - libm.so.6 => /lib64/libm.so.6 (0x00002b3dee6f5000) - libc.so.6 => /lib64/libc.so.6 (0x00002b3dee9f7000) - /lib64/ld-linux-x86-64.so.2 (0x00002b3dbd934000) - ``` - -4. 修改配置文件。 - - 基于安全考虑,OceanBase 的 sys 租户账号和密码,需要通过加密配置给 oblogproxy。执行以下命令即可得到账号和密码的密文,您需将账号和密码的密文分别配置到 conf.json 中的 `ob_sys_username` 和 `ob_sys_password` 字段中。 - - > **注意** - > - > 此处的用户必须为 sys 租户下的用户,若使用非 root 用户,需要提前在 sys 租户中创建对应用户。 - - ```shell - ## 生成加密用户和对应的密码 - [admin@obtest004 oblogproxy]$ cd bin - [admin@obtest004 bin]$ ./logproxy -x root@sys - EA87*****************E1556E917 - - ## 请输入实际密码 - [admin@obtest004 bin]$ ./logproxy -x ******** - 8852D*****************A9D8FD52 - - ## 修改conf.json 参数 - [admin@obtest00 oblogproxy]$ cd conf/ - [admin@obtest00 conf]$ vim conf.json - { - "ob_sys_username": "EA87*****************E1556E917", - "ob_sys_password": "8852D*****************A9D8FD52", - } - ``` - -5. 启动 oblogproxy。 - - ```shell - [admin@obtest004 oblogproxy]$ ./run.sh start - work path : /usr/local/oblogproxy - is_running : (8252)/usr/local/oblogproxy logproxy is running ! - logproxy started! - ``` - -6. 查看进程,确认启动成功。 - - ```sql - [admin@obtest004 ~]$ ps -ef | grep logproxy | grep -v grep - admin 28808 28800 1 10:47 pts/2 00:00:33 ./bin/logproxy -f ./conf/conf.json - ``` - - 当有一个 Client 连接成功后会 fork 一个子进程。 - - ```sql - [admin@obtest004 ~]$ ps -ef | grep oblogreader | grep -v grep - admin 4227 28808 6 11:10 pts/2 00:01:22 oblogreader -f ./conf/conf.json - ``` - -### **部署 Flink** - -Flink 部署有集群模式和单节点模式,本次测试主要使用单节点部署。单节点部署比较简单,直接解压安装包就可以使用,不用进行其他的配置。启动成功后,访问 http://localhost:8081/#/overview,便可以对 Flink 任务进行监控管理。 - -1. 下载 Flink 并解压。 - - ```bash - [admin@obtest004 ~]$ wget https://archive.apache.org/dist/flink/flink-1.16.1/flink-1.16.1-bin-scala_2.12.tgz - [admin@obtest004 ~]$ tar -zxvf flink-1.16.1-bin-scala_2.12.tgz - ``` - -2. 编辑 flink-conf.yaml 配置文件,并根据实际安装路径配置 Java 环境变量。 - - ```shell - [admin@obtest004 ~]$ cd flink-1.16.1/ - [admin@obtest004 flink-1.16.1]$ vim conf/flink-conf.yaml - ## 添加配置 - env.java.home=env.java.home=/usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.362.b08-1.el7_9.x86_64 - ``` - -3、下载 oceanbase-cdc 和 jdbc-connector 组件。 - -```bash -## 下载 jar 包 -[admin@obtest004 flink-1.16.1]$ cd lib/ -[admin@obtest004 lib]$ wget https://repo1.maven.org/maven2/com/ververica/flink-sql-connector-oceanbase-cdc/2.3.0/ flink-sql-connector-oceanbase-cdc-2.3.0.jar -[admin@obtest004 lib]$ wget https://repo1.maven.org/maven2/org/apache/flink/flink-connector-jdbc/1.16.1/ flink-connector-jdbc-1.16.1.jar -[admin@obtest004 lib]$ ll -total 220140 --rw-r--r-- 1 admin admin 198857 Jan 19 22:20 flink-cep-1.16.1.jar --rw-r--r-- 1 admin admin 516144 Jan 19 22:21 flink-connector-files-1.16.1.jar --rw-rw-r-- 1 admin admin 248892 Jan 19 23:16 flink-connector-jdbc-1.16.1.jar --rw-r--r-- 1 admin admin 102470 Jan 19 22:23 flink-csv-1.16.1.jar --rw-r--r-- 1 admin admin 117107159 Jan 19 22:29 flink-dist-1.16.1.jar --rw-r--r-- 1 admin admin 180248 Jan 19 22:23 flink-json-1.16.1.jar --rw-r--r-- 1 admin admin 21052640 Jan 19 22:28 flink-scala_2.12-1.16.1.jar --rw-rw-r-- 1 admin admin 10737871 Jan 13 23:11 flink-shaded-zookeeper-3.5.9.jar --rw-rw-r-- 1 admin admin 18167926 Nov 9 23:18 flink-sql-connector-oceanbase-cdc-2.3.0.jar --rw-r--r-- 1 admin admin 15367504 Jan 19 22:28 flink-table-api-java-uber-1.16.1.jar --rw-r--r-- 1 admin admin 36249667 Jan 19 22:24 flink-table-planner-loader-1.16.1.jar --rw-r--r-- 1 admin admin 3133690 Jan 19 22:20 flink-table-runtime-1.16.1.jar --rw-rw-r-- 1 admin admin 208006 Jan 13 21:32 log4j-1.2-api-2.17.1.jar --rw-rw-r-- 1 admin admin 301872 Jan 13 21:32 log4j-api-2.17.1.jar --rw-rw-r-- 1 admin admin 1790452 Jan 13 21:32 log4j-core-2.17.1.jar --rw-rw-r-- 1 admin admin 24279 Jan 13 21:32 log4j-slf4j-impl-2.17.1.jar -``` - -4、启动 Flink,如下所示表示启动成功。 - -```shell -[admin@obtest004 lib]$cd ~/flink-1.16.1/bin -[admin@obtest004 bin]$ ./start-cluster.sh -## 输出如下 -Starting cluster. -[INFO] 1 instance(s) of standalonesession are already running on obtest004. -Starting standalonesession daemon on host obtest004. -[INFO] 1 instance(s) of taskexecutor are already running on obtest004. -Starting taskexecutor daemon on host obtest004. -``` - -从启动结果可以看到主要启动了两个进程 standalonesession 和 askexecutor,其中 standalonesession 进程对应的是 JobManager 类型,taskexecutor 进程对应的是 TaskManager 类型。 - -在 http://localhost:8081/#/overview 中查看 Flink 的 Web 页面。 -![image.png](/img/data_migration/oblogproxy/flink.png) - -## **数据同步测试** - -### **OceanBase > Flink** - -#### **测试目的** - -测试将 OceanBase 集群 cdc@flink_cdc_1 租户表 test1 的现有数据和增量数据能否正常同步至 Flink 中。 - -#### **测试步骤** - -1. 在租户 flink_cdc_1 中创建数据库并创建表 test1 - -2. 在 Flink Client 中创建表 products - -3. 查看 OceanBase 侧表 test1 的数据能否正常同步至 Flink 侧表 products 中 - -4. 在表 test1 中插入一条新数据,在表 products 中查看新插入数据的同步结果。 - -| **Tenant** | **DataBase** | **Table** | -| ----------- | ------------ | --------- | -| flink_cdc_1 | cdc_test1 | test1 | - -#### **操作步骤** - -1. 在上游 OceanBase 集群 cdc@flink_cdc_1 租户中创建表 test1。 - - ```sql - CREATE TABLE `test1` ( - `id` int NOT NULL, - `name` varchar(45) DEFAULT NULL, - `description` varchar(45) DEFAULT NULL, - `weight` decimal(10,3) DEFAULT NULL, - PRIMARY KEY (`id`) - ) CHARSET=utf8mb4; - ``` - - 在表中插入数据并查询 - - ```sql - MySQL [cdc_test1]> insert into test1(id,name,description,weight) values(1,'aa','haha',10); - Query OK, 1 row affected (0.006 sec) - - MySQL [cdc_test1]> insert into test1(id,name,description,weight) values(2,'bb','haha',20); - Query OK, 1 row affected (0.002 sec) - - MySQL [cdc_test1]> select * from test1; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 1 | aa | haha | 10.000 | - | 2 | bb | haha | 20.000 | - +----+------+-------------+--------+ - 2 rows in set (0.000 sec) - ``` - -2. 启动 Flink Client 。 - - ```sql - [admin@obtest004 bin]pwd - /home/admin/flink-1.16.1/bin - [admin@obtest004 bin]$./sql-client.sh - ``` - -3. 在 Flink Client 中创建表 products。 - - ```java - CREATE TABLE `products` ( - id INT NOT NULL PRIMARY KEY, - name varchar(32), - description varchar(45), - weight DECIMAL(10,3) - ) WITH ( - 'connector' ='oceanbase-cdc', - 'scan.startup.mode' = 'initial', - 'username' = 'cdc@flink_cdc_1', - 'password' = '**********', - 'tenant-name' = 'flink_cdc_1', - 'database-name' = 'cdc_test1', - 'table-name' = 'test1', - 'hostname' = 'xxx.xxx.xxx.xxx', - 'port' = '2883', - 'rootserver-list' = 'xxx.xxx.xxx.xxx:2882:2881;xxx.xxx.xxx.xxx:2882:2881;xxx.xxx.xxx.xxx:2882:2881', - 'logproxy.host' = 'xxx.xxx.xxx.xxx', - 'logproxy.port' = '2983' - ); - ``` - - 查询结果如下: - - ```sql - Flink SQL> select * from products; - [INFO] Result retrieval cancelled. - ``` - -4. 在上游 cdc@flink_cdc_1 租户表 test1 中插入一条数据。 - - ```sql - MySQL [cdc_test1]> insert into test1(id,name,description,weight) values(3,'cc','haha',30); - Query OK, 1 row affected (0.003 sec) - - MySQL [cdc_test1]> select * from test1; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 1 | aa | haha | 10.000 | - | 2 | bb | haha | 20.000 | - | 3 | cc | haha | 30.000 | - +----+------+-------------+--------+ - 3 rows in set (0.000 sec) - ``` - -5. 在 Flink Client 中查看数据的变化,可以看到刚插入进来的一条数据,说明数据可以正常从 OceanBase 集群 cdc@flink_cdc_1 租户中同步至 Flink 侧的表 products 中。 - -### **Flink > OceanBase** - -#### **测试目的** - -测试将 Flink 侧的数据能否正常同步至 OceanBase 集群 cdc@flink_cdc_2 租户的表 test2 中。 - -#### **测试步骤** - -1. 在租户 flink_cdc_2 中创建数据库并创建表 test2 - -2. 在 Flink Client 中创建表 products - -3. 向表 products 中插入数据,查看在 Flink 侧插入的数据能否正常同步至 OceanBase 侧的表 test2 中 。 - -| **Tenant** | **DataBase** | **Table** | -| ----------- | ------------ | --------- | -| flink_cdc_2 | cdc_test2 | test2 | - -#### **操作步骤** - -1. 在下游 OceanBase 集群 cdc@flink_cdc_2 租户中创建表 test2。 - - ```sql - CREATE TABLE `test2` ( - `id` int NOT NULL, - `name` varchar(45) DEFAULT NULL, - `description` varchar(45) DEFAULT NULL, - `weight` decimal(10,3) DEFAULT NULL, - PRIMARY KEY (`id`) - ) CHARSET=utf8mb4; - ``` - -2. 启动 Flink Client 测试。 - - ```sql - [admin@obtest004 bin]./sql-client.sh - ``` - -3. 在 Flink Client 中创建表 products。 - - ```sql - Flink SQL> CREATE TABLE `products` ( - id INT NOT NULL PRIMARY KEY, - name varchar(32), - description varchar(45), - weight DECIMAL(10,3) - ) WITH ( - 'connector' ='jdbc', - 'url' = 'jdbc:mysql://xxx.xxx.xxx.xxx:2883/cdc_test2', - 'username' = 'cdc@flink_cdc_2', - 'password' = '**********', - 'table-name' = 'test2' - ); - ``` - -4. 在 Flink Client 中插入两条数据。 - - ```sql - Flink SQL> insert into products(id,name,description,weight) values(4,'dd','haha',40); - [INFO] Submitting SQL update statement to the cluster... - [INFO] SQL update statement has been successfully submitted to the cluster: - Job ID: f6ec0946d654e231fc0b900e62c449f3 - - Flink SQL> insert into products(id,name,description,weight) values(5,'ee','haha',50); - [INFO] Submitting SQL update statement to the cluster... - [INFO] SQL update statement has been successfully submitted to the cluster: - Job ID: 2967d5f99c20332793d6b4e8af5ada43 - ``` - -5. 可以在下游 cdc@flink_cdc_2 租户表 test2 中查看到新插入的两条数据,说明数据可以正常从 Flink 侧同步至 OceanBase 集群的 cdc@flink_cdc_2 租户中。 - - ```sql - MySQL [cdc_test2]> select * from test2; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 4 | dd | haha | 40.000 | - | 5 | ee | haha | 50.000 | - +----+------+-------------+--------+ - 2 rows in set (0.004 sec) - ``` - -### **OceanBase(一个租户)> Flink > OceanBase** - -#### **测试目的** - -测试将 OceanBase 集群 cdc@flink_cdc_1 租户表 test1 中现有的数据和增量数据通过 Flink 同步至 OceanBase 集群 cdc@flink_cdc_2 租户表 test2 中。 - -#### **测试步骤** - -1. 在 Flink Client 中创建表 flinktest1 和 flinktest2 - -2. 通过 insert into select 将 cdc@flink_cdc_1 租户的现有数据同步到 cdc@flink_cdc_2 租户中 - -3. 在上游 OceanBase 侧表 test1 中进行 insert、delete、update,查看下游 OceanBase 表 test2 中数据的变化。 - -| **Tenant** | **DataBase** | **Table** | -| ----------- | ------------ | --------- | -| flink_cdc_1 | cdc_test1 | test1 | -| flink_cdc_2 | cdc_test2 | test2 | - -#### **操作步骤** - -1. 在 Flink Client 中创建接收上游 cdc@flink_cdc_1 租户表 test1 数据的表 flinktest1,以及向下游 cdc@flink_cdc_2 租户表 test2 同步数据的表 flinktest2。 - - ```sql - Flink SQL> CREATE TABLE `flinktest1` ( - id INT NOT NULL PRIMARY KEY, - name varchar(32), - description varchar(45), - weight DECIMAL(10,3) - ) WITH ( - 'connector' ='oceanbase-cdc', - 'scan.startup.mode' = 'initial', - 'username' = 'cdc@flink_cdc_1', - 'password' = '**********', - 'tenant-name' = 'flink_cdc_1', - 'database-name' = 'cdc_test1', - 'table-name' = 'test1', - 'hostname' = 'xxx.xxx.xxx.xxx', - 'port' = '2883', - 'rootserver-list' ='xxx.xxx.xxx.xxx:2882:2881;xxx.xxx.xxx.xxx:2882:2881;xxx.xxx.xxx.xxx:2882:2881', - 'logproxy.host' = 'xxx.xxx.xxx.xxx', - 'logproxy.port' = '2983' - ); - - Flink SQL> CREATE TABLE `flinktest2` ( - id INT NOT NULL PRIMARY KEY, - name varchar(32), - description varchar(45), - weight DECIMAL(10,3) - ) WITH ( - 'connector' ='jdbc', - 'url' = 'jdbc:mysql://xxx.xxx.xxx.xxx:2883/cdc_test2', - 'username' = 'cdc@flink_cdc_2', - 'password' = '**********', - 'table-name' = 'test2' - ); - ``` - -2. 查看表 flinktest1 现有数据。 - - ```sql - Flink SQL> select * from flinktest1; - [INFO] Result retrieval cancelled. - ``` - -3. 查看表 flinktest2 现有数据。 - - ```sql - Flink SQL> select * from flinktest2; - [INFO] Result retrieval cancelled. - ``` - -4. 执行 insert into select ,将表 flinktest1 收集的数据写入表 flinktest2。 - - ```sql - Flink SQL> insert into flinktest2 select * from flinktest1; - [INFO] Submitting SQL update statement to the cluster... - [INFO] SQL update statement has been successfully submitted to the cluster: - Job ID: 7620ac78c627eab1f3dc4d86e7a29a17 - ``` - -5. 查询下游 cdc@flink_cdc_2 租户表 test2 中的数据,可以看到上游 cdc@flink_cdc_1 租户表 test1 的数据已经被同步过来了。 - - ```sql - MySQL [cdc_test2]> select * from test2; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 1 | aa | haha | 10.000 | - | 2 | bb | haha | 20.000 | - | 3 | cc | haha | 30.000 | - | 4 | dd | haha | 40.000 | - | 5 | ee | haha | 50.000 | - +----+------+-------------+--------+ - 5 rows in set (0.000 sec) - ``` - - 您也可以在 Web 页面中查看 Job 的执行情况。 - -6. 在上游 OceanBase 集群 cdc@flink_cdc_1 租户表 test1 中插入一条数据。 - - ```sql - MySQL [cdc_test1]> insert into test1(id,name,description,weight) values(6,'ff','haha',60); - Query OK, 1 row affected (0.006 sec) - - MySQL [cdc_test1]> select * from test1; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 1 | aa | haha | 10.000 | - | 2 | bb | haha | 20.000 | - | 3 | cc | haha | 30.000 | - | 6 | ff | haha | 60.000 | - +----+------+-------------+--------+ - 4 rows in set (0.000 sec) - ``` - - 在下游 OceanBase 集群 cdc@flink_cdc_2 租户表 test2 中查看数据的变化。 - - ```sql - MySQL [cdc_test2]> select * from test2; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 1 | aa | haha | 10.000 | - | 2 | bb | haha | 20.000 | - | 3 | cc | haha | 30.000 | - | 4 | dd | haha | 40.000 | - | 5 | ee | haha | 50.000 | - | 6 | ff | haha | 60.000 | - +----+------+-------------+--------+ - 6 rows in set (0.000 sec) - ``` - -7. 在上游 OceanBase 集群 cdc@flink_cdc_1 租户表 test1 中删除 id=1 的数据。 - - ```sql - MySQL [cdc_test1]> delete from test1 where id=1; - Query OK, 1 row affected (0.005 sec) - - MySQL [cdc_test1]> select * from test1; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 2 | bb | haha | 20.000 | - | 3 | cc | haha | 30.000 | - | 6 | ff | haha | 60.000 | - +----+------+-------------+--------+ - 3 rows in set (0.000 sec) - ``` - - 在下游 OceanBase 集群 cdc@flink_cdc_2 租户表 test2 中查看数据的变化,可以看到 id=1 的一条数据已被删除。 - - ```sql - MySQL [cdc_test2]> select * from test2; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 2 | bb | haha | 20.000 | - | 3 | cc | haha | 30.000 | - | 4 | dd | haha | 40.000 | - | 5 | ee | haha | 50.000 | - | 6 | ff | haha | 60.000 | - +----+------+-------------+--------+ - 5 rows in set (0.000 sec) - ``` - -8. 在上游 OceanBase 集群 cdc@flink_cdc_1 租户表 test1 中修改 id=2 的 name 为 yy。 - - ```sql - MySQL [cdc_test1]> update test1 set name = 'yy' where id = 2; - Query OK, 1 row affected (0.004 sec) - Rows matched: 1 Changed: 1 Warnings: 0 - - MySQL [cdc_test1]> select * from test1; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 2 | yy | haha | 20.000 | - | 3 | cc | haha | 30.000 | - | 6 | ff | haha | 60.000 | - +----+------+-------------+--------+ - 3 rows in set (0.000 sec) - ``` - - 在下游 OceanBase 集群 cdc@flink_cdc_2 租户表 test2 中查看数据的变化,可以看到 id=2 的一条数据已被修改成功。 - - ```sql - MySQL [cdc_test2]> select * from test2; - +----+------+-------------+--------+ - | id | name | description | weight | - +----+------+-------------+--------+ - | 2 | yy | haha | 20.000 | - | 3 | cc | haha | 30.000 | - | 4 | dd | haha | 40.000 | - | 5 | ee | haha | 50.000 | - | 6 | ff | haha | 60.000 | - +----+------+-------------+--------+ - 5 rows in set (0.000 sec) - ``` - -9. 查看 Job 的运行情况。 - - ```sql - [admin@obtest004 bin]$ ./flink list - Waiting for response... - ------------------ Running/Restarting Jobs ------------------- - 15.02.2023 14:52:07 : 31514c80a7cc1093ce442f71f5618671 : insert-into_default_catalog.default_database.flinktest2 (RUNNING) - -------------------------------------------------------------- - No scheduled jobs. - ``` - - 通过 Job ID 取消 Job。 - - ```sql - [admin@obtest004 bin] $./flink cancel 31514c80a7cc1093ce442f71f5618671 - Cancelling job 31514c80a7cc1093ce442f71f5618671. - Cancelled job 31514c80a7cc1093ce442f71f5618671. - ``` - -### **OceanBase(两个租户) > Flink > OceanBase** - -#### **测试目的** - -测试将上游 OceanBase 集群租户 flink_cdc_1 和租户 flink_cdc_2 中的不同数据通过 Flink 进行 join 同步之后,将数据写入下游 OceanBase 集群租户 flink_cdc_3 中。 - -#### **测试步骤** - -1. 在上游的两个租户中分别创建表 flinkcdc1、flinkcdc2 并写入数据 - -2. 在下游的租户中创建表 flinkcdc3。然后在 Flink Client 中创建表 cdctest1、cdctest2、cdctest3 - -3. 将表 cdctest1 和 表 cdctest2 进行 join,并将 join 后的数据写入到表 cdctest3 中 - -4. 分别在上游 OceanBase 侧表 flinkcdc1、flinkcdc2 中进行 insert、delete、update,查看下游 OceanBase 侧表 flinkcdc3 中数据的变化。 - -| **Tenant** | **DataBase** | **Table** | -| ----------- | ------------ | --------- | -| flink_cdc_1 | cdc_test1 | flinkcdc1 | -| flink_cdc_2 | cdc_test2 | flinkcdc2 | -| flink_cdc_3 | cdc_test3 | flinkcdc3 | - -#### **操作步骤** - -1. 在上游的两个租户 flink_cdc_1 和 flink_cdc_2 中分别创建表 flinkcdc1 和 flinkcdc2 并写入数据,在下游的租户 flink_cdc_1 中创建表 flinkcdc3。 - - ```sql - CREATE TABLE `flinkcdc1` ( - `id` int NOT NULL, - `remarks` varchar(45) DEFAULT NULL, - `kg` decimal(10,3) DEFAULT NULL, - PRIMARY KEY (`id`) - ) CHARSET=utf8mb4; - - insert into flinkcdc1(id,remarks,kg) values(1,'aa',10); - - - CREATE TABLE `flinkcdc2` ( - `id` int NOT NULL, - `name` varchar(45) DEFAULT NULL, - `description` varchar(45) DEFAULT NULL, - `weight` decimal(10,3) DEFAULT NULL, - PRIMARY KEY (`id`) - ) CHARSET=utf8mb4; - - insert into flinkcdc2(id,name,description,weight) values(1,'yr','test1',10); - - - CREATE TABLE `flinkcdc3` ( - `id` int NOT NULL, - `name` varchar(45) DEFAULT NULL, - `description` varchar(45) DEFAULT NULL, - `weight` decimal(10,3) DEFAULT NULL, - `remarks` varchar(45) DEFAULT NULL, - `kg` decimal(10,3) DEFAULT NULL, - PRIMARY KEY (`id`) - ) CHARSET=utf8mb4; - ``` - -2. 在 Flink Client 中创建表 cdctest1、cdctest2、cdctest3,将表 cdctest1 和 表 cdctest2 进行 join,并将 join 后的数据写入到表 cdctest3 中。 - - ```sql - CREATE TABLE `cdctest1` ( - id int NOT NULL, - remarks varchar(45), - kg decimal(10,3) - ) WITH ( - 'connector' ='oceanbase-cdc', - 'scan.startup.mode' = 'initial', - 'username' = 'cdc@flink_cdc_1', - 'password' = '**********', - 'tenant-name' = 'flink_cdc_1', - 'database-name' = 'cdc_test1', - 'table-name' = 'flinkcdc1', - 'hostname' = 'xxx.xxx.xxx.xxx', - 'port' = '2883', - 'rootserver-list' ='xxx.xxx.xxx.xxx:2882:2881;xxx.xxx.xxx.xxx:2882:2881;xxx.xxx.xxx.xxx:2882:2881', - 'logproxy.host' = 'xxx.xxx.xxx.xxx', - 'logproxy.port' = '2983' - ); - - - CREATE TABLE `cdctest2` ( - id int NOT NULL, - name varchar(45), - description varchar(45), - weight decimal(10,3) - ) WITH ( - 'connector' ='oceanbase-cdc', - 'scan.startup.mode' = 'initial', - 'username' = 'cdc@flink_cdc_2', - 'password' = '**********', - 'tenant-name' = 'flink_cdc_2', - 'database-name' = 'cdc_test2', - 'table-name' = 'flinkcdc2', - 'hostname' = 'xxx.xxx.xxx.xxx', - 'port' = '2883', - 'rootserver-list' ='xxx.xxx.xxx.xxx:2882:2881;xxx.xxx.xxx.xxx:2882:2881;xxx.xxx.xxx.xxx:2882:2881', - 'logproxy.host' = 'xxx.xxx.xxx.xxx', - 'logproxy.port' = '2983' - ); - - - CREATE TABLE `cdctest3` ( - id int NOT NULL PRIMARY KEY, - name varchar(45), - description varchar(45), - weight decimal(10,3), - remarks varchar(45), - kg decimal(10,3) - ) WITH ( - 'connector' ='jdbc', - 'url' = 'jdbc:mysql://xxx.xxx.xxx.xxx:2883/cdc_test3', - 'username' = 'cdc@flink_cdc_3', - 'password' = '**********', - 'table-name' = 'flinkcdc3' - ); - - insert into cdctest3 select cdctest2.id,cdctest2.name,cdctest2.description,cdctest2.weight,cdctest1.remarks,cdctest1.kg from cdctest1,cdctest2 where cdctest1.id=cdctest2.id; - ``` - -3. 在下游租户的表 flinkcdc3 中查看数据同步,可以看到表 flinkcdc1 和表 flinkcdc2 进行 join 后的数据已经被同步过来了。 - - ```sql - MySQL [cdc_test3]> select * from flinkcdc3; - +----+------+-------------+--------+---------+--------+ - | id | name | description | weight | remarks | kg | - +----+------+-------------+--------+---------+--------+ - | 1 | yr | test1 | 10.000 | aa | 10.000 | - +----+------+-------------+--------+---------+--------+ - 1 row in set (0.000 sec) - ``` - - 您也可以在 Web 页面中查看 Job 的执行情况。 - -4. 分别在上游租户的表 flinkcdc1 和表 flinkcdc2 中插入一条数据。 - - ```sql - MySQL [cdc_test1]> insert into flinkcdc1(id,remarks,kg) values(2,'bb',20); - MySQL [cdc_test2]> insert into flinkcdc2(id,name,description,weight) values(2,'yr','test2',20); - ``` - - 在下游表 flinkcdc3 中查看数据变化,可以看到表 flinkcdc1 和表 flinkcdc2 刚插入的数据已经被同步过来了。 - - ```sql - MySQL [cdc_test3]> select * from flinkcdc3; - +----+------+-------------+--------+---------+--------+ - | id | name | description | weight | remarks | kg | - +----+------+-------------+--------+---------+--------+ - | 1 | yr | test1 | 10.000 | aa | 10.000 | - | 2 | yr | test2 | 20.000 | bb | 20.000 | - +----+------+-------------+--------+---------+--------+ - 2 rows in set (0.000 sec) - ``` - -5. 修改上游表 flinkcdc1 中的数据。 - - ```sql - MySQL [cdc_test1]> update flinkcdc1 set remarks='cc' where id = 2; - Query OK, 1 row affected (0.008 sec) - Rows matched: 1 Changed: 1 Warnings: 0 - - MySQL [cdc_test1]> select * from flinkcdc1; - +----+---------+--------+ - | id | remarks | kg | - +----+---------+--------+ - | 1 | aa | 10.000 | - | 2 | cc | 20.000 | - +----+---------+--------+ - 2 rows in set (0.001 sec) - ``` - - 在下游表 flinkcdc3 中查看数据变化,可以看到表中的数据已被修改。 - - ```sql - MySQL [cdc_test3]> select * from flinkcdc3; - +----+------+-------------+--------+---------+--------+ - | id | name | description | weight | remarks | kg | - +----+------+-------------+--------+---------+--------+ - | 1 | yr | test1 | 10.000 | aa | 10.000 | - | 2 | yr | test2 | 20.000 | cc | 20.000 | - +----+------+-------------+--------+---------+--------+ - 2 rows in set (0.000 sec) - ``` - -6. 删除上游表 flinkcdc2 中 id=1 的数据。 - - ```sql - MySQL [cdc_test2]> delete from flinkcdc2 where id = 1; - ``` - - 在下游表 flinkcdc3 中查看数据变化,可以看到 id=1 的一条数据已被删除。 - - ```sql - MySQL [cdc_test3]> select * from flinkcdc3; - +----+------+-------------+--------+---------+--------+ - | id | name | description | weight | remarks | kg | - +----+------+-------------+--------+---------+--------+ - | 2 | yr | test2 | 20.000 | cc | 20.000 | - +----+------+-------------+--------+---------+--------+ - ``` - -## **常见问题** - -1. Flink 的 Web 页面无法访问时,需要修改 /etc/hosts 文件。Flink 进程的监听 IP 端口是 127.0.0.1:8081,只能本机进行访问,外部服务器无法访问,因此需要将 /etc/hosts 文件中的 127.0.0.1 改为实际的 IP。 - - ```sql - ## vim /etc/hosts - 172.xxx.xxx.xxx localhost localhost.localdomain localhost4 localhost4.localdomain4 - ``` - -2. 当 oblogproxy 日志出现如下错误时,可以直接忽略。该错误为 WARNING 类型,不影响同步结果,后续版本中会进行修复。 - - ```sql - E20230215 15:43:58.279422 28808 mysql_protocol.cpp:239] Failed to query observer:Table 'oceanbase. __all_virtual_server_clog_stat' doesn't exist, unexpected column count: 0 - ``` diff --git a/docs/user_manual/user_best_practices/data_migration/oms/_index.md b/docs/user_manual/user_best_practices/data_migration/oms/_index.md deleted file mode 100644 index 5b53d56d0..000000000 --- a/docs/user_manual/user_best_practices/data_migration/oms/_index.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: OMS -weight: 1 ---- - -# **使用 OMS 进行数据迁移和数据同步** - -本文主要介绍使用 OMS 进行数据迁移和数据同步的适用版本和目前支持的源端/目标端,详细的操作过程请参见官网 [OMS 文档](https://www.oceanbase.com/docs/oms-cn)。 - -## **适用版本** - -数据迁移和数据同步功能中,OceanBase 社区版和其它数据终端的适用版本如下。 - -| 分类 | 数据迁移 | 数据同步 | -| --- | --- | --- | -| OceanBase 社区版 | V3.1.0-CE、V3.1.1-CE、V3.1.2-CE、V3.1.3-CE、V3.1.4-CE、V4.0.0-CE、V4.1.0-CE | V3.1.0-CE、V3.1.1-CE、V3.1.2-CE、V3.1.3-CE、V3.1.4-CE | -| 其它数据终端 | - MySQL:V5.5、V5.6、V5.7、V8.0
- MariaDB:V10.2
- TiDB:4.x、5.x
- PostgreSQL:10.x |- Kafka:V0.9、V1.0、V2.x
- RocketMQ:V4.7.1 | - -## **目前支持的数据源和目标端** - -其中,OceanBase 4.x 版本只能作为 MySQL 的目标端,其他的暂不支持。 - -| 数据源 | 目标端 | 数据迁移 | 数据同步 | -| --- | --- | --- | --- | -| MySQL | OceanBase | 支持 | 支持 | -| TiDB | OceanBase | 支持 | 支持 | -| PostgreSQL | OceanBase | 支持 | 支持 | -| OceanBase | OceanBase | 支持 | 支持 | -| OceanBase | MySQL | 支持 | 支持 | -| OceanBase | Kafka | \\ | 支持 | -| OceanBase | RocketMQ | \\ | 支持 | - -## **TiDB 迁移数据到 OceanBase 数据库需要注意的点** - -如需使用 OMS 做 TiDB 增量数据迁移,需要先为 TiDB 集群部署 TiCDC,并且把数据写到 kafka,之后在 OMS 创建数据源。 - -在 OMS 中创建数据源时需先创建 kafka 数据源,再创建 TiDB 数据源,并且 TiDB 数据源里需要包含创建的 kafka 数据源,这样在创建迁移项目的时候才能选择增量同步。 \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/data_migration/oms/mysql2ob.md b/docs/user_manual/user_best_practices/data_migration/oms/mysql2ob.md deleted file mode 100644 index 3c18e8e2e..000000000 --- a/docs/user_manual/user_best_practices/data_migration/oms/mysql2ob.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: MySQL 迁移到 OB -weight: 1 ---- - - -对于无主键表,现在 OMS 不支持增量同步以及数据校验,所以如果要同步的表包括主键表+无主键表,那么后续增量同步任务,需要将无主键表剔除掉,或者保证无主键表没有变更。 - -如果需要做反向增量,也就是后续切换以后,OB 数据反向同步到 MySQL,那么需要绑定 OCP,从 OCP 获取 Config Url,并且集群需要开启日志归档。用户信息需要用 sys 租户下的用户及密码。 - -如果想要直接用 OMS 做指定位点的增量同步,现在还不支持,现在只能指定某个时间戳,不能明确到某个 position 或者 GTID。 - -下面的迁移任务主要包含两种,包含无主键表+主键表的迁移和仅主键表的迁移。 - -## 创建包含无主键表的迁移任务 - -**一、创建迁移任务** - -1. 创建迁移任务,选择全部表迁移 - -![image.png](/img/data_migration/oms/mysql2ob/p1-1-1.png) - -2. 选择迁移类型 - -如果包含主键表,并且需要增量同步,可以按需选择;否则的话可以不选择增量同步和全量检验。 - -![image.png](/img/data_migration/oms/mysql2ob/p1-2-1.png) - -3. 选择迁移对象 - -![image.png](/img/data_migration/oms/mysql2ob/p1-3-1.png) - -4. 创建迁移任务 - -这里一定要注意下面的资源,选择当前机器满足的速度,比如选择了快速,但是机器内存低于8G,那么迁移任务会失败 -![image.png](/img/data_migration/oms/mysql2ob/p1-4-1.png) -![image.png](/img/data_migration/oms/mysql2ob/p1-4-2.png) - -5. 预检查,检查通过后,任务启动 - -![image.png](/img/data_migration/oms/mysql2ob/p1-5-1.png) - -**二、开始数据迁移** - -任务启动后,会按照任务顺序来操作。 -![image.png](/img/data_migration/oms/mysql2ob/p2-1.png) - -对于有主键的表,增量迁移是没有问题的。 -![image.png](/img/data_migration/oms/mysql2ob/p2-2.png) - -如果想要看到 DDL 和 DML 的统计,需要暂停任务再开启 -![image.png](/img/data_migration/oms/mysql2ob/p2-3.png) - -全量校验只会校验有主键表 -![image.png](/img/data_migration/oms/mysql2ob/p2-4.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-5.png) - -如果无主键表进行变更,那么不会做增量同步,并且任务会报错。 -![image.png](/img/data_migration/oms/mysql2ob/p2-6.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-7.png) - -解决方法,可以通过更新增量同步组件的配置,将无主键表去掉。 -![image.png](/img/data_migration/oms/mysql2ob/p2-8.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-9.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-10.png) - -去掉以后,该组件会自动重启,恢复正常,对于去除掉的无主键表的新增变更将自动忽略。 - -当确认数据同步没问题,准备应用切换,可以手动进入下一阶段。 -默认不会进行自动正向切换,需要配合应用切换手动执行 -![image.png](/img/data_migration/oms/mysql2ob/p2-11.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-12.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-13.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-14.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-15.png) - -反向增量效果验证 -![image.png](/img/data_migration/oms/mysql2ob/p2-16.png) -![image.png](/img/data_migration/oms/mysql2ob/p2-17.png) - - -## 创建主键表迁移任务 - -1. 创建迁移任务,仅支持唯一键表迁移 - -![image.png](/img/data_migration/oms/mysql2ob/p3-1.png) - -2. 按需选择迁移类型 - -![image.png](/img/data_migration/oms/mysql2ob/p3-2.png) - -中间过程同上,这里省略... - -3. 任务启动 - -如果数据库中包含唯一键表和非唯一键表,只会迁移唯一键表。 -![image.png](/img/data_migration/oms/mysql2ob/p3-3.png) - -4. 效果验证 - -![image.png](/img/data_migration/oms/mysql2ob/p3-4.png) - -后续步骤也同上,这里忽略... - diff --git a/docs/user_manual/user_best_practices/data_migration/oms/tidb2ob.md b/docs/user_manual/user_best_practices/data_migration/oms/tidb2ob.md deleted file mode 100644 index c5e9f83ae..000000000 --- a/docs/user_manual/user_best_practices/data_migration/oms/tidb2ob.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: TiDB 迁移到 OB -weight: 2 ---- - - -如果 TiDB 集群没有开启 TiCDC,那么只能做数据迁移;如果需要做数据迁移+增量同步,需要部署 TiCDC 并且创建 Kafka 的同步任务,并且表要有主键。 - -集群已经存在 TiCDC 以及 Kafka 任务可以直接在创建数据源的时候进行指定,如果当前集群并没有部署 TiCDC,可以通过后面的步骤进行添加。 - -![image.png](/img/data_migration/oms/tidb2ob/p0.png) - -## 全量迁移 - -**一、迁移任务创建** - -1. 添加数据源 - -![image.png](/img/data_migration/oms/tidb2ob/p1-1.png) - -2. 发起迁移任务 - -![image.png](/img/data_migration/oms/tidb2ob/p1-2.png) - -3. 选择源端和目标端 - -![image.png](/img/data_migration/oms/tidb2ob/p1-3.png) - -4. 选择迁移类型 - -![image.png](/img/data_migration/oms/tidb2ob/p1-4.png) - -如果需要做反向增量,也就是后续切换以后,OB 数据反向同步到 TiDB,那么需要绑定 OCP,从 OCP 获取 Config Url,并且集群需要开启日志归档。 - -这里需要用 sys 租户下的用户及密码。 - -![image.png](/img/data_migration/oms/tidb2ob/p1-5.png) - -5. 选择迁移对象 - -可以重命名,也可以多表汇合等操作 -![image.png](/img/data_migration/oms/tidb2ob/p1-6.png) - -6. 选择迁移并发以及冲突处理 - -这里一定要注意下面的资源,选择当前机器满足的速度,比如选择了快速,但是机器内存低于8G,那么迁移任务会失败 -![image.png](/img/data_migration/oms/tidb2ob/p1-7.png) - - -7. 预检查通过后,启动项目 - -![image.png](/img/data_migration/oms/tidb2ob/p1-8.png) - -**二、开始数据迁移** - -会根据迁移任务创建时选择的迁移类型的流程开始执行。 - -失败可以查看详情,修复完成后,点击恢复即可继续运行。 -![image.png](/img/data_migration/oms/tidb2ob/p2-1.png) -![image.png](/img/data_migration/oms/tidb2ob/p2-2.png) - -当前状态表示已经迁移+校验完成,等待后续应用切换后配合做正向切换。 - -![image.png](/img/data_migration/oms/tidb2ob/p2-3.png) - -进入下一个阶段,默认不会进行正向切换,需要配合应用切换手动执行 -![image.png](/img/data_migration/oms/tidb2ob/p2-4.png) -![image.png](/img/data_migration/oms/tidb2ob/p2-5.png) - - -**三、反向增量验证** - -![image.png](/img/data_migration/oms/tidb2ob/p3-1.png) - - -## 部署 TiCDC 以支持增量 - -1. 扩容 cdc - -```sql -# 创建 TiCDC 扩容配置文件 - -[tidb@iZ0jl7bmjvyd7ojkpcdiggZ ~]$ cat add-cdc.yaml -cdc_servers: - - host: xx.xx.xx.xx - port: 8300 - deploy_dir: "/tidb-deploy/cdc-8300" - data_dir: "/tidb-data/cdc-8300" - log_dir: "/tidb-deploy/cdc-8300/log" - -# 查询集群列表,获取集群信息 -tiup cluster list - -# 扩容 TiCDC -tiup cluster scale-out tidb-test add-cdc.yaml -``` - -2. 部署 Kafka,安装步骤这里省略,需要注意 Kafka 跟 TiDB 版本的对应关系。 - -3. 创建 Kafka 同步任务 - -```sql -# 需要到 TiCDC 的安装目录下执行。 -# 查询任务列表 -./cdc cli changefeed list --pd=http://172.xx.xxx.xx:2379 - -# 创建同步任务 -./cdc cli changefeed create --pd=http://172.xx.xxx.xx:2379 --sink-uri="kafka://127.0.0.1:9092/test-topic?kafka-version=2.4.0&partition-num=6&max-message-bytes=671088&replication-factor=1" --changefeed-id="simple-replication-task2" --sort-engine="unified" - -# 查询单个任务详情 -./cdc cli changefeed query --pd=http://172.xx.xxx.xx:2379 --changefeed-id=simple-replication-task2 -``` - -返回 Create changefeed successfully! 即创建成功 - -4. 新建 TiDB 数据源 -![image.png](/img/data_migration/oms/tidb2ob/p4-1.png) -![image.png](/img/data_migration/oms/tidb2ob/p4-2.png) - - -**后续操作** - -创建迁移任务的步骤跟上面相同,因为装了 TiCDC 和 Kafka,所以现在有了【增量迁移】选项 -![image.png](/img/data_migration/oms/tidb2ob/p4-3.png) - -后续任务也增加了【增量同步】步骤 - -![image.png](/img/data_migration/oms/tidb2ob/p4-4.png) - -如果组件有问题,可以先检查下组件监控 -![image.png](/img/data_migration/oms/tidb2ob/p4-5.png) - -测试增量效果 -![image.png](/img/data_migration/oms/tidb2ob/p4-6.png) - - - diff --git a/docs/user_manual/user_best_practices/deploy_oceanbase/_index.md b/docs/user_manual/user_best_practices/deploy_oceanbase/_index.md deleted file mode 100644 index 4b819b5f1..000000000 --- a/docs/user_manual/user_best_practices/deploy_oceanbase/_index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 部署 OceanBase -weight: 2 ---- diff --git a/docs/user_manual/user_best_practices/deploy_oceanbase/deploy_method.md b/docs/user_manual/user_best_practices/deploy_oceanbase/deploy_method.md deleted file mode 100644 index 08a0fd6f4..000000000 --- a/docs/user_manual/user_best_practices/deploy_oceanbase/deploy_method.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: 部署方式 -weight: 1 ---- - -目前 OB 的部署方式主要有三种,手动部署、ODB 部署以及 OCP 部署。 - -**手动部署:** - -需要自己下载 RPM 包,并且指定配置启动。手动部署对 OB 的熟悉程度要求比较高,配置变更需要修改启动命令,否则启动会有异常,不推荐。 - -**OBD部署:** - -OBD 是 OceanBase 集群安装部署工具,可以通过命令行或白屏界面的方式来部署 OB 集群。操作简单,**推荐使用**。 - -**OCP部署:** - -OceanBase 云平台,可以对 OB 集群进行管理,包括安装部署、监控告警、备份归档等等。操作简单,**强烈推荐使用**。 - -**OBD 和 OCP 的区别对比:** - -1. 集群管理能力,OBD主要功能是安装部署、简单维护;OCP 可以在这个基础上实现更多的管理能力,如果是线上集群,建议使用 OCP。 -2. 资源占用,OBD更加的轻量;OCP 因为监控、管理等能力,需要独立的元数据库和监控数据库,并且不建议跟业务集群放到一起。所以需要创建独立的 OCP 集群。如果需要快速部署,建议使用 OBD。 - -当然,OBD 部署的集群也可以通过 OCP 来管理,不过接管要求是用 admin 用户部署的集群以及只能接管 OBServer。 - diff --git a/docs/user_manual/user_best_practices/deploy_oceanbase/obd_deploy.md b/docs/user_manual/user_best_practices/deploy_oceanbase/obd_deploy.md deleted file mode 100644 index 5f574bcfb..000000000 --- a/docs/user_manual/user_best_practices/deploy_oceanbase/obd_deploy.md +++ /dev/null @@ -1,436 +0,0 @@ ---- -title: 通过 OBD 部署集群 -weight: 3 ---- - -**OBD 创建集群适用于快速部署集群用来测试,如果是线上环境,建议使用 OCP。** - -## **机器初始化(强烈建议)** - -强烈建议做机器初始化,防止后面使用过程中因为配置问题导致的数据库异常,避免类似于句柄不足、内存泄漏等问题。 - -操作需要登录到每台机器,在 root 用户下完成。 - -### **检测及安装 NTP 服务** - -1. 执行以下命令,如果输出 running 表示 NTP 服务正在运行: -``` -sudo systemctl status ntpd.service -ntpd.service - Network Time Service -Loaded: loaded (/usr/lib/systemd/system/ntpd.service; disabled; vendor preset: disabled) -Active: active (running) since 一 2017-12-18 13:13:19 CST; 3s ago -``` - - - 若返回报错信息 Unit ntpd.service could not be found.,请尝试执行以下命令,以查看与 NTP 进行时钟同步所使用的系统配置是 chronyd 还是 ntpd: -``` -sudo systemctl status chronyd.service -chronyd.service - NTP client/server -Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled) -Active: active (running) since Mon 2021-04-05 09:55:29 EDT; 3 days ago -``` -若发现系统既没有配置 chronyd 也没有配置 ntpd,则表示系统尚未安装任一服务。此时,应先安装其中一个服务,并保证它可以自动启动,默认使用 ntpd。如果你使用的系统配置是 chronyd,请直接执行步骤 3。 - -2. 执行 ntpstat 命令检测是否与 NTP 服务器同步: - -注意: Ubuntu 系统需安装 ntpstat 软件包。 - -``` -ntpstat -``` - - - 如果输出 synchronised to NTP server,表示正在与 NTP 服务器正常同步: - -``` -synchronised to NTP server (85.199.214.101) at stratum 2 -time correct to within 91 ms -polling server every 1024 s -``` - - - 以下情况表示 NTP 服务未正常同步: - ``` - unsynchronised - ``` - - - 以下情况表示 NTP 服务未正常运行: - ``` - Unable to talk to NTP daemon. Is it running? - ``` - -3. 执行 chronyc tracking 命令查看 Chrony 服务是否与 NTP 服务器同步。 - -注意:该操作仅适用于使用 Chrony 的系统,不适用于使用 NTPd 的系统。 - -``` -chronyc tracking -``` - - - 如果该命令返回结果为 Leap status : Normal,则代表同步过程正常。 -``` -Reference ID : 5EC69F0A (ntp1.time.nl) -Stratum : 2 -Ref time (UTC) : Thu May 20 15:19:08 2021 -System time : 0.000022151 seconds slow of NTP time -Last offset : -0.000041040 seconds -RMS offset : 0.000053422 seconds -Frequency : 2.286 ppm slow -Residual freq : -0.000 ppm -Skew : 0.012 ppm -Root delay : 0.012706812 seconds -Root dispersion : 0.000430042 seconds -Update interval : 1029.8 seconds -Leap status : Normal -``` - - - 如果该命令返回结果如下,则表示同步过程出错: - ``` - Leap status : Not synchronised - ``` - - - 如果该命令返回结果如下,则表示 Chrony 服务未正常运行: - ``` - 506 Cannot talk to daemon - ``` - -如果要使 NTP 服务尽快开始同步,执行以下命令。可以将 pool.ntp.org 替换为你的 NTP 服务器: -``` -sudo systemctl stop ntpd.service && \ -sudo ntpdate pool.ntp.org && \ -sudo systemctl start ntpd.service -``` - -如果要在 CentOS 7 系统上手动安装 NTP 服务,可执行以下命令: -``` -sudo yum install ntp ntpdate && \ -sudo systemctl start ntpd.service && \ -sudo systemctl enable ntpd.service -``` - -### **配置 limits.conf** - -在 /etc/security/limits.conf 配置文件中添加以下内容: -``` -root soft nofile 655350 -root hard nofile 655350 -* soft nofile 655350 -* hard nofile 655350 -* soft stack 20480 -* hard stack 20480 -* soft nproc 655360 -* hard nproc 655360 -* soft core unlimited -* hard core unlimited -``` - -退出当前会话,重新登录。执行以下命令,查看配置是否生效。 -``` -ulimit -a -``` - -### **配置 sysctl.conf** - -1. 在 /etc/sysctl.conf 配置文件中添加以下内容: -``` -# for oceanbase -## 修改内核异步 I/O 限制 -fs.aio-max-nr=1048576 - -## 网络优化 -net.core.somaxconn = 2048 -net.core.netdev_max_backlog = 10000 -net.core.rmem_default = 16777216 -net.core.wmem_default = 16777216 -net.core.rmem_max = 16777216 -net.core.wmem_max = 16777216 - -net.ipv4.ip_local_port_range = 3500 65535 -net.ipv4.ip_forward = 0 -net.ipv4.conf.default.rp_filter = 1 -net.ipv4.conf.default.accept_source_route = 0 -net.ipv4.tcp_syncookies = 0 -net.ipv4.tcp_rmem = 4096 87380 16777216 -net.ipv4.tcp_wmem = 4096 65536 16777216 -net.ipv4.tcp_max_syn_backlog = 16384 -net.ipv4.tcp_fin_timeout = 15 -net.ipv4.tcp_max_syn_backlog = 16384 -net.ipv4.tcp_tw_reuse = 1 -net.ipv4.tcp_tw_recycle = 1 -net.ipv4.tcp_slow_start_after_idle=0 - -vm.swappiness = 0 -vm.min_free_kbytes = 2097152 -# 修改进程可以拥有的虚拟内存区域数量 -vm.max_map_count = 655360 - -# 此处为 OceanBase 数据库的 data 目录 -kernel.core_pattern = /data/core-%e-%p-%t -``` -其中,kernel.core_pattern 中的 /data 为 OceanBase 数据库的 data 目录。 -注意:max_map_count 配置不合理的情况下,可能会导致严重的内存泄露。 - -2. 加载配置 -``` -sysctl -p -``` - -### **关闭防火墙** - -``` -systemctl disable firewalld -systemctl stop firewalld -systemctl status firewalld -``` - -### **关闭 SELinux** - -在 /etc/selinux/config 配置文件中修改对应配置项为以下内容: -``` -SELINUX=disabled -``` - -执行以下命令或重启服务器,使更改生效: -``` -setenforce 0 -``` - -执行以下命令,查看更改是否生效: -``` -sestatus -``` - -### **关闭透明大页:** - -对于 Red Hat 操作系统,需要运行以下命令,手动关闭透明大页: -``` -echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled -``` -对于 CentOS 操作系统,需要运行以下命令,手动关闭透明大页: -``` -echo never > /sys/kernel/mm/transparent_hugepage/enabled -``` - -### **规划目录** - -需要创建的目录(可根据自己的业务情况调整) - -- /data 为数据盘。 -- /redo 存放 redo 日志。 -- /home/admin/oceanbase 存放 OceanBase 数据库的二进制文件和运行日志。 - -其中,数据盘和日志盘建议分盘,避免相互影响;日志盘大小建议是 OB 内存的 3-4倍;磁盘空间默认会预占用,后续数据新增会自动从这里面分配。 - -### **创建 admin 用户** - -1. 执行以下命令,创建账户 admin。 -``` -useradd -U admin -d /home/admin -s /bin/bash -mkdir -p /home/admin -chown -R admin:admin /home/admin -``` - -2. 执行以下命令,为账户 admin 设置密码。 -``` -passwd admin -``` - -3. 为账户 admin 设置 sudo 权限。执行以下命令,打开 /etc/sudoers 文件,在 /etc/sudoers 文件添加以下内容: -``` -[root@test001 ~]# vim /etc/sudoers -# 添加如下内容: -## Same thing without a password -# %wheel ALL=(ALL) NOPASSWD: ALL -admin ALL=(ALL) NOPASSWD: ALL -``` - -4. 授权目录 -``` -chown -R admin:admin /data -chown -R admin:admin /redo -chown -R admin:admin /home/admin -``` - -### **中控机配置 admin 用户 SSH免密** - -admin 用户登录 OBD 所在机器 - -1. 在中控机器上运行以下命令生成 SSH 公钥和私钥: -``` -ssh-keygen -t rsa -``` - -2. 将中控机的公钥复制到目标机器的 authorized_keys 文件中: -``` -ssh-copy-id -i ~/.ssh/id_rsa.pub @ -``` - -## **创建集群** - -1. 编辑配置文件 - -在 ~/.oceanbase-all-in-one/conf 下有常用的配置模版 - -- 单机部署配置样例:mini-single-example.yaml、single-example.yaml -- 单机部署 + ODP 配置样例:mini-single-with-obproxy-example.yaml、single-with-obproxy-example.yaml -- 分布式部署配置样例:mini-distributed-example.yaml、distributed-example.yaml -- 分布式部署 + ODP 配置样例:mini-distributed-with-obproxy-example.yaml、distributed-with-obproxy-example.yaml -- 分布式部署全部组件:all-components-min.yaml、all-components.yaml - -配置文件事例: -``` -## Only need to configure when remote login is required -user: - username: admin - password: -# key_file: your ssh-key file path if need -# port: your ssh port, default 22 -# timeout: ssh connection timeout (second), default 30 -oceanbase-ce: - servers: - - name: server1 - # Please don't use hostname, only IP can be supported - ip: 192.168.1.2 - - name: server2 - ip: 192.168.1.3 - - name: server3 - ip: 192.168.1.4 - global: - production_mode: true - # Please set devname as the network adaptor's name whose ip is in the setting of severs. - # if set severs as "127.0.0.1", please set devname as "lo" - # if current ip is 192.168.1.10, and the ip's network adaptor's name is "eth0", please use "eth0" - devname: eth0 - # if current hardware's memory capacity is smaller than 50G, please use the setting of "mini-single-example.yaml" and do a small adjustment. - memory_limit: 64G # The maximum running memory for an observer - # The reserved system memory. system_memory is reserved for general tenants. The default value is 30G. - system_memory: 30G - cpu_count: 32 - datafile_size: 300G # Size of the data file. - log_disk_size: 200G # The size of disk space used by the clog files. - enable_syslog_wf: false # Print system logs whose levels are higher than WARNING to a separate log file. The default value is true. - enable_syslog_recycle: true # Enable auto system log recycling or not. The default value is false. - max_syslog_file_count: 4 # The maximum number of reserved log files before enabling auto recycling. The default value is 0. - # observer cluster name, consistent with obproxy's cluster_name - appname: obtest - # root_password: # root user password, can be empty - root_password: ********* - # proxyro_password: # proxyro user pasword, consistent with obproxy's observer_sys_password, can be empty - proxyro_password: ********* - # In this example , support multiple ob process in single node, so different process use different ports. - # If deploy ob cluster in multiple nodes, the port and path setting can be same. - server1: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /data - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /redo - zone: zone1 - server2: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /data - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /redo - zone: zone2 - server3: - mysql_port: 2881 # External port for OceanBase Database. The default value is 2881. DO NOT change this value after the cluster is started. - rpc_port: 2882 # Internal port for OceanBase Database. The default value is 2882. DO NOT change this value after the cluster is started. - # The working directory for OceanBase Database. OceanBase Database is started under this directory. This is a required field. - home_path: /home/admin/observer - # The directory for data storage. The default value is $home_path/store. - data_dir: /data - # The directory for clog, ilog, and slog. The default value is the same as the data_dir value. - redo_dir: /redo - zone: zone3 -obproxy-ce: - # Set dependent components for the component. - # When the associated configurations are not done, OBD will automatically get the these configurations from the dependent components. - depends: - - oceanbase-ce - servers: - - 192.168.1.2 - - 192.168.1.3 - - 192.168.1.4 - global: - listen_port: 2883 # External port. The default value is 2883. - prometheus_listen_port: 2884 # The Prometheus port. The default value is 2884. - home_path: /home/admin/obproxy - # oceanbase root server list - # format: ip:mysql_port;ip:mysql_port. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - # rs_list: 192.168.1.2:2881;192.168.1.3:2881;192.168.1.4:2881 - enable_cluster_checkout: false - # observer cluster name, consistent with oceanbase-ce's appname. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - cluster_name: obtest - skip_proxy_sys_private_check: true - enable_strict_kernel_release: false - # obproxy_sys_password: # obproxy sys user password, can be empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - obproxy_sys_password: ********* - # observer_sys_password: # proxyro user pasword, consistent with oceanbase-ce's proxyro_password, can be empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. - observer_sys_password: ********* -``` - -需要关注的配置项: - -【user】 - -| 参数 | 详情 | -| --- | --- | -| password | 如果已经设置免密,则为空 | - -【oceanbase-ce】 - -| 参数 | 详情 | -| --- | --- | -| production_mode | 如果给到OB的内存小于 16G,需要改成 false。 | -| memory_limit | 给到 OBServer 的内存大小。 | -| system_memory | 保留的系统内存,该参数值会占用 memory_limit 的内存,不建议给太小。 | -| cpu_count | 给到 OBServer 的 CPU 数量。 | -| datafile_size | 数据盘大小。 | -| log_disk_size | 日志盘大小,建议是内存的 3-4 倍。 | -| appname | 集群名字,跟下面 [obproxy-ce] - [cluster_name] 的定义保持一致。 | -| root_password | 建议手动定义,密码跟 [obproxy-ce] - [obproxy_sys_password] 以及 [obproxy-ce] - [observer_sys_password] 保持一致。 | -| proxyro_password | 建议跟 root_password 保持一致。 | -| home_path | 安装目录,OB 的本地配置以及运行日志都在这里。 | -| data_dir | 数据目录。 | -| redo_dir | 日志目录。 | -| zone | 逻辑概念,如果相同的话则只能单副本,不能保证可用性。建议至少3个以上。 | - - -如果需要监控、告警,可以增加 prometheus 和 obagent 配置。 - -2. 部署集群: -``` -obd cluster deploy obtest -c obtest-config.yaml -``` -注意:这里的 obtest 是指 OBD 部署集群名,可以理解为集群别名,跟配置文件中的集群名不是一个,建议保持一致防止后面弄混。 - -3. 启动集群: -``` -obd cluster start obtest -``` -如果因为配置问题导致启动失败,可以通过 obd cluster edit-config obtest 修改配置重试。 - -4. 查看集群状态 -``` -# 查看 OBD 管理的集群列表 -obd cluster list - -# 查看 obtest 集群状态 -obd cluster display obtest -``` - -## **连接集群** -通过 obd cluster display obtest 可以查询到 sys 租户 root 用户的连接串 -``` -# 通过 OBServer 连接到集群 -mysql -h192.168.1.2 -P2881 -uroot@sys -p'*********' -Doceanbase -A - -# 通过 OBProxy 连接到集群 -mysql -h192.168.1.2 -P2883 -uroot@sys#obtest -p'*********' -Doceanbase -A -``` - -至此,集群创建完成。 \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/deploy_oceanbase/ocp_deploy.md b/docs/user_manual/user_best_practices/deploy_oceanbase/ocp_deploy.md deleted file mode 100644 index 906119a48..000000000 --- a/docs/user_manual/user_best_practices/deploy_oceanbase/ocp_deploy.md +++ /dev/null @@ -1,205 +0,0 @@ ---- -title: 通过 OCP 部署&接管集群 -weight: 4 ---- - -> 当前使用版本:社区版 OCP-4.2.0 - -**创建集群主要步骤:** - -前置准备:机器初始化。 - -上传软件包:第一次创建需要。 - -添加主机:包括账号密码、机房等。 - -创建集群:定义集群信息。 - -关联 OBProxy 集群:OB 代理。 - -## **前置准备** - -### **规划目录** - -需要创建的目录(可根据自己的业务情况调整) - -- /data 为数据盘。 -- /redo 存放 redo 日志。 -- /home/admin/oceanbase 存放 OceanBase 数据库的二进制文件和运行日志。 - -其中,数据盘和日志盘建议分盘,避免相互影响;日志盘大小建议是 OB 内存的 3-4 倍;磁盘空间默认会预占用,后续数据新增会自动从这里面分配。 - -### **创建 admin 用户** - -1. 执行以下命令,创建账户 admin。 - -``` -useradd -U admin -d /home/admin -s /bin/bash -mkdir -p /home/admin -chown -R admin:admin /home/admin -``` - -2. 执行以下命令,为账户 admin 设置密码。 - -``` -passwd admin -``` - -3. 为账户 admin 设置 sudo 权限。执行以下命令,打开 /etc/sudoers 文件,在 /etc/sudoers 文件添加以下内容: - -``` -[root@test001 ~]# vim /etc/sudoers -# 添加如下内容: -## Same thing without a password -# %wheel ALL=(ALL) NOPASSWD: ALL -admin ALL=(ALL) NOPASSWD: ALL -``` - -4. 授权目录 - -``` -chown -R admin:admin /data -chown -R admin:admin /redo -chown -R admin:admin /home/admin -``` - -### **安装 MySQL Client** - -``` -centos: yum install mysql -y -ubuntu:apt-get install mariadb-client -y -``` - -## **上传软件包** - -软件包下载地址:[https://www.oceanbase.com/softwarecenter](https://www.oceanbase.com/softwarecenter) - -需要安装的软件包: - -- OceanBase 数据库 (OBServer 服务) -- 依赖库 (OceanBase Libs) -- 工具集成包 (OceanBase Utils) -- OceanBase 数据库代理 (OBProxy) -- OCP 监控工具 (OCP-Agent) - -OCP-Agent 需要到 OCP 服务所在的服务器下载,然后上传 - -``` -[root@obtest ~]# find / -name "t-oceanbase-ocp-agent*" -/root/.ocp-server-all-in-one/ocp-installer/usr/ocp-installer/rpm/t-oceanbase-ocp-agent-4.2.0-20231017100200.alios7.aarch64.rpm -/root/.ocp-server-all-in-one/ocp-installer/usr/ocp-installer/rpm/t-oceanbase-ocp-agent-4.2.0-20231017100200.alios7.x86_64.rpm -/root/ocp-server-all-in-one/rpms/t-oceanbase-ocp-agent-4.2.0-20231017100200.alios7.aarch64.rpm -/root/ocp-server-all-in-one/rpms/t-oceanbase-ocp-agent-4.2.0-20231017100200.alios7.x86_64.rpm -``` - -查看所有软件包: -![image.png](/img/deploy_oceanbase/ocp_deploy/p1.png) - -## **添加主机** - -1. 填写主机信息 - -涉及到的操作:【新增机型】、【新增机房】、【新增区域】、【新增凭据】 - -- 机房对应 IDC,区域对应 REGION,可以是实际的也可以是虚拟的; -- 凭证为用户密码,建议使用 admin 用户; -- 添加完成,建议完成主机标准化,相当于将 OBD 初始化操作集成到了这个功能,而不需要手动操作,避免因配置不正确影响线上稳定性。 - -![image.png](/img/deploy_oceanbase/ocp_deploy/p2.png) - -2. 查看主机状态 - -添加主机完成后,需要等待主机创建完成 - -- 新提交:表示添加主机任务还在进行中 -- 空闲:表示添加主机任务已经完成 -- 空闲(黄色叹号):表示未做主机标准化,**强烈建议进行主机标准化操作,进行风险检查以及自动修复。** -- 失败:添加主机任务失败,可以通过任务中心查看详情 - -![image.png](/img/deploy_oceanbase/ocp_deploy/p3.png) -![image.png](/img/deploy_oceanbase/ocp_deploy/p4.png) -![image.png](/img/deploy_oceanbase/ocp_deploy/p5.png) - -比如找不到 OCP-Agent,将上面提到的安装包上传重试即可。 -![image.png](/img/deploy_oceanbase/ocp_deploy/p6.png) - -## **创建集群** - -1. 点击创建集群 - -![image.png](/img/deploy_oceanbase/ocp_deploy/p7.png) - -2. 定义集群信息 - -![image.png](/img/deploy_oceanbase/ocp_deploy/p8.png) - -参数默认不需要设置,会自动占用 80% - 90% 的资源,如果只是测试或者不想全部占用,可以自己配置。 - -可以参考【通过 OBD 部署集群】里的参数。 - -![image.png](/img/deploy_oceanbase/ocp_deploy/p9.png) - -3. 提交表单 - -提交会让二次确认信息,没问题后则会真正的开始创建任务。可以到任务中心查看任务进度。 -![image.png](/img/deploy_oceanbase/ocp_deploy/p10.png) -![image.png](/img/deploy_oceanbase/ocp_deploy/p11.png) - -4. 创建任务完成后查看集群信息 - ![image.png](/img/deploy_oceanbase/ocp_deploy/p12.png) - -## **关联 OBProxy 集群** - -1. 点击开始创建 - -![image.png](/img/deploy_oceanbase/ocp_deploy/p13.png) - -2. 创建 OBProxy 集群,并关联 OB 集群 - -建议使用 ConfigUrl,后续 OMS 等工具需要用到。 -![image.png](/img/deploy_oceanbase/ocp_deploy/p14.png) -![image.png](/img/deploy_oceanbase/ocp_deploy/p15.png) - -3. 表单提交后可以通过任务中心查看进度 - ![image.png](/img/deploy_oceanbase/ocp_deploy/p16.png) - -## **接管已有集群** - -1. 添加主机(同上) - -2. 接管集群 - -![image.png](/img/deploy_oceanbase/ocp_deploy/p17.png) - -3. 填写集群连接信息 - -proxyro 密码,建议填写。 -![image.png](/img/deploy_oceanbase/ocp_deploy/p18.png) - -4. 预检查 - ![image.png](/img/deploy_oceanbase/ocp_deploy/p19.png) - -如果出现 IDC、REGION 不一致的报错,可以手动修改主机上的【机房】以及【区域】来对应集群实际的 IDC 和 REGION。或者修改集群的 IDC 与 REGION 信息。 - -查询集群 IDC 与 REGION 信息: - -``` -obclient [oceanbase]> select * from dba_ob_zones; -+-------+----------------------------+----------------------------+--------+-----+------------+-----------+ -| ZONE | CREATE_TIME | MODIFY_TIME | STATUS | IDC | REGION | TYPE | -+-------+----------------------------+----------------------------+--------+-----+------------+-----------+ -| zone1 | 2023-08-23 16:56:33.745432 | 2023-08-23 16:56:33.745432 | ACTIVE | | sys_region | ReadWrite | -| zone2 | 2023-08-23 16:56:33.745432 | 2023-08-23 16:56:33.745432 | ACTIVE | | sys_region | ReadWrite | -| zone3 | 2023-08-23 16:56:33.745432 | 2023-08-23 16:56:33.745432 | ACTIVE | | sys_region | ReadWrite | -+-------+----------------------------+----------------------------+--------+-----+------------+-----------+ -3 rows in set (0.095 sec) -``` - -修改 IDC 与 REGION 信息: - -``` -ALTER SYSTEM MODIFY zone zone1 SET IDC='old2',REGION='sys_region'; -``` - -5. 等待接管任务完成,可以查看集群获取集群信息 - ![image.png](/img/deploy_oceanbase/ocp_deploy/p20.png) diff --git a/docs/user_manual/user_best_practices/deploy_oceanbase/production_deployment.md b/docs/user_manual/user_best_practices/deploy_oceanbase/production_deployment.md deleted file mode 100644 index a995e6cf9..000000000 --- a/docs/user_manual/user_best_practices/deploy_oceanbase/production_deployment.md +++ /dev/null @@ -1,198 +0,0 @@ ---- -title: 生产部署最佳实践 -weight: 2 ---- - -# **生产部署最佳实践** - -## **基础介绍** -### **OceanBase4.x版本常用资源参数简介及计算方式** -| | 参数 | 解释 | 特性 | 小课堂 | -| --- | --- | --- | --- | --- | -| CPU资源参数 | cpu_count | OB可使用的CPU核数,参数为数值,例如:16,设置为0,系统自动检测并设置 | 不支持动态调整 | 修改该参数需要重启集群才能生效 | -| 内存资源参数 | memory_limit | OB可使用的内存大小,参数需要带单位,例如:32G | 支持动态调整 | 1. memory_limit没有上限边界,建议按实际内存free -m信息中的free列剩余大小进行规划设置。
2. 支持动态增大和缩小,但不能比已分配出去的内存还小。
3. memory_limit优先级大于memory_limit_percentage,即同时设置,以memory_limit生效。| -|内存资源参数| memory_limit_percentage | OB可用内存占总内存的百分比,参数为数值,例如:80(表示80%) | 支持动态调整 | | -| 内存资源参数|system_memory | OB的500租户的内存,即OB系统内部运行内存,参数需要带单位,例如:30G | 支持动态调整 | 1. system_memory取值计算方式:
-- 16G<=memory_limit <=32G,system_memory=3-5G
-- 32G<=memory_limit <=64G,system_memory=5-10G
-- memory_limit >64G,system_memory=取整数部分(3 *(memory_limit的平方根-3G))
2. system_memory和sys租户没关系,sys租户是OB部署完成由系统自建的自适应资源租户,租户ID为1,而system_memory对应的租户ID为500。| -| 磁盘资源参数 | datafile_size | OB数据目录预占用大小,参数需要带单位,例如:32G | 预占用,不支持调小 | 1. 预占用会提前申请磁盘空间,部署完成查看磁盘使用很大,属于正常现象。
2. datafile_size优先级大于datafile_disk_percentage。| -| 磁盘资源参数|datafile_disk_percentage | OB数据目录预占用目录百分比,参数为数值,例如:80(表示80%) | 预占用,不支持调小 | | -| 磁盘资源参数|log_disk_size | OB日志数据目录预占用大小,参数需要带单位,例如:32G | 预占用,支持动态调整 | 1. log_disk_size取值计算方式:log_disk_size>=memory_limit * 3
2. 预占用会提前申请磁盘空间,部署完成查看磁盘使用很大,属于正常现象。
3. log_disk_size优先级大log_disk_percentage。| -| 磁盘资源参数 |log_disk_percentage | OB数据目录预占用目录百分比,参数为数值,例如:80(表示80%) | 预占用 支持动态调整 | | -| 系统日志参数 | enable_syslog_recycle | 是否开启OB系统日志回收,建议开启 | 需要和max_syslog_file_count参数搭配使用 | | -| 系统日志参数 | max_syslog_file_count | 在回收OB系统日志文件之前最大保留OB系统日志个数 | 需要和enable_syslog_recycle参数搭配使用 | 单个系统日志256M,建议生产环境按需要日志保留天数需要,来设置系统日志个数。
注意:OB系统日志打印较大较频繁,需要关注设置个数和磁盘大小关系,防止磁盘被占满。 | - - -## **部署要求** -### **生产场景OB集群要求** -| 模块 | 要求 | 说明 | -| --- | --- | --- | -| 服务器 | 支持主流服务器和国产化适配软硬件|- 已适配基于硬件整机中科可控 H620 系列、华为 TaiShan 200 系列、长城擎天 DF720 等整机。
- 已适配支持海光 7185/7280、鲲鹏 920、飞腾 2000+ 等 CPU。
- 已适配支持麒麟 V4、V10 和 UOS V20 等国产操作系统,并适配上层中间件东方通 TongWeb V7.0、金蝶 Apusic 应用服务器软件 V9.0 等 | -| 操作系统 | 支持x86、ARM架构 | - Alibaba Cloud Linux 2/3 版本(内核 Linux 3.10.0 版本及以上)。
- Anolis OS 8.X 版本(内核 Linux 3.10.0 版本及以上)。
- Red Hat Enterprise Linux Server 7.X 版本、8.X 版本(内核 Linux 3.10.0 版本及以上)。
- CentOS Linux 7.X 版本、8.X 版本(内核 Linux 3.10.0 版本及以上)。
- Debian 9.X 版本及以上版本(内核 Linux 3.10.0 版本及以上)。Ubuntu 20.X 版本及以上版本(内核 Linux 3.10.0 版本及以上)。
- SUSE / OpenSUSE 15.X 版本及以上版本(内核 Linux 3.10.0 版本及以上)。
- KylinOS V10 版本。统信 UOS 1020a/1021a/1021e/1001c 版本。
- 科方德 NFSChina 4.0 版本及以上。浪潮 Inspur kos 5.8 版本 | -| CPU | 最低4核,推荐32核以上 | 此为分配给OB的最低核数,非服务器总核数 | -| 内存 | 最低32G,推荐256G以上 | 此为分配给OB的最低内存,非服务器总内存大小 | -| SWAP | 禁止使用SWAP功能 | SWAP交换分区会影响整个集群性能 | -| 磁盘类型 | 磁盘类型:SSD | | -| 文件系统| 文件系统:XFS、EXT4 | 不支持其他文件系统,数据大于16T,推荐使用XFS文件系统 | -| 磁盘容量 | 总磁盘容量:内存的6倍以上 | -| 分盘 |数据盘大小:最低20G日志盘大小:最低96G | - 日志盘+数据盘容量推荐是分配给OB内存大小的6倍以上,即(memory_limit * 6);
- 数据盘和日志盘一定要分盘,否则影响性能;
- 数据盘大小可根据业务数据量评估;
- 日志盘大小推荐根据分配给OB总内存 * 3 进行规划 (memory_limit * 3)| -| RAID | 支持 | RAID阵列缓存需要使用write through模式; | -| 网卡 | 最低千兆,推荐万兆 | | -| | | | - - -### **生产场景OCP配置要求** -管理 10 台业务 OBServer 所需的资源为例,即这台物理机需要有 24 个 CPU 和 64G 内存。 -其中OCP所需4C 8G,METADB所需13C 52G,剩余资源为操作系统使用。 - -| 模块 | CPU | 内存 | -| --- | --- | --- | -| ocp-server | 4 | 8G | -| metadb租户 | 4 | 8G | -| monitor租户 | 4 | 16G | -| sys_memory | 5 | 28G | - -### **常见问题-服务器初始化要求** -| 模块 | 要求 | 操作 | 说明 | -| --- | --- | --- | --- | -| 防火墙 | 关闭| #开机不自启
systemctl disable firewalld
#关闭防火墙
systemctl stop firewalld | 涉及OB以及OB生态组件节点建议关闭,如有防火墙开放需求,参考[OceanBase 服务端进程 & 生态产品默认端口号](https://ask.oceanbase.com/t/topic/35603118) | -| SELinux | 关闭 |
#将 SELINUX 的配置修改为 disabled,重启服务器生效
vim /etc/selinux/config
SELINUX=disabled
#立即生效命令
setenforce 0 | SELinux开启会占用较多系统资源,且对文件权限和访问控制进行限制等。 | -| 时钟同步 | 开启 | 参考 初始化服务器 | OB生态组件均需要进行同步(包括OCP、OCP-Express、OB、OBProxy) | - - - -### **常见问题-软件依赖要求** -| 环境包 | 要求 | 原因 | -| --- | --- | --- | -| mysql | 强依赖 | OCP部署OB依赖mysql命令 | -| nc | 强依赖 | OCP部署OB依赖nc命令 | -| python | python2.7及其以上 | OCP添加主机依赖,不能是python2命令 | -| jdk | java-1.8.0-openjdk | OBD部署OCP-EXPRESS依赖 | -| ntp/Chrony | 非强依赖 | OB集群要求时差小于100ms | -| nfs | 非强依赖 | OB远程备份介质依赖 | -| docker | 非强依赖,不支持docker-man | OCP部分版本使用docker部署ocp-server服务 | - - -## **部署场景及注意事项** -### **3台及以下服务器推荐OBD白屏部署和管理。** - -**部署规划:** - -| 3节点集群 | 部署组件 | 说明 | -| --- | --- | --- | -| 节点A | OBSERVER、OBPROXY、OBAgent、OBD | 1. 不推荐部署2节点集群,无法满足多数派,不具备高可用能力。
2. 单节点部署建议开启数据备份功能。| -| 节点B | OBSERVER、OBPROXY、OBAgent、OCP-EXPRESS | | -| 节点C | OBSERVER、OBPROXY、OBAgent | | - -**注意事项一:** - -部署用户配置:支持任意用户部署,无需sudo权限,推荐admin用户,后续OCP接管集群方便。 - -前提:目标节点已存在该用户,且有安装目录、数据目录、日志目录所属权限。 -![image.png](/img/deploy_oceanbase/production_deployment/1690464188720-1544a836-bde3-4465-8b6a-ec55e0cf0d38.png) - -**注意事项二:** - -占用模式:生产场景不可使用最小可用模式,推荐使用最大占用+自定义方式规划。 - -数据目录、日志目录:必须分在不同的磁盘上,目录可自定义。 -![image.png](/img/deploy_oceanbase/production_deployment/1690465100575-975e3b82-1f99-438e-b039-d8959ecc615e.png) - -**注意事项三:** - -更多配置:用户设置OB相关参数,其中devname默认使用本地 "lo" 网卡,需要自定义修改为实际IP对应的网卡名称,其他资源参数按需分配即可。 -![image.png](/img/deploy_oceanbase/production_deployment/1690465462599-2727c584-fa88-417b-b554-eb9643040834.png) - -### **4台及以上服务器集群推荐OCP部署和管理** -| 4节点集群 | 部署组件 | 说明 | -| --- | --- | --- | -| 节点A | OCP、OBSERVER(METADB) | 1. OCP节点服务器配置推荐24C 64G。
2. OCP建议使用单独的OB单机或集群当元数据库,不推荐把业务集群当OCP元数据库使用。| -| 节点B | OBSERVER、OBPROXY、OCP-Agent | | -| 节点C | OBSERVER、OBPROXY、OCP-Agent | | -| 节点D | OBSERVER、OBPROXY、OCP-Agent | | - -**注意事项一:** - -部署顺序推荐,先部署OCP(内含METADB),通过OCP再部署业务OB集群,同时OCP也支持接管OBD部署的OB集群。 - -**注意事项二:** - -部署OCP时关于METADB的安装配置推荐按需调整,因为METADB会默认预占用服务器剩余内存的70%,数据和日志盘默认预占用磁盘空间90%。 - -标准24C 64G服务器,配置OCP参数参考如下 -![image.png](/img/deploy_oceanbase/production_deployment/1690523425698-8adaf519-39f5-4ecd-a89f-08dc28848300.png) - -自定义METADB参数参考如下(max_syslog_file_count和devname按需设置): -![image.png](/img/deploy_oceanbase/production_deployment/1690523838581-8af2a164-80e2-42d6-b207-72bb607cac30.png) - -meta租户信息: -![image.png](/img/deploy_oceanbase/production_deployment/1690523902920-c72407bb-293c-46a4-bf5e-5b1aba2a72cc.png) - -monitor租户信息: -![image.png](/img/deploy_oceanbase/production_deployment/1690523945449-8dc39704-6253-418a-beee-b678a68247d1.png) - -ocp-server配置信息: -![image.png](/img/deploy_oceanbase/production_deployment/1690524062853-dced09f9-0dd5-4ebf-8d53-d6cfb1ae5a72.png) - -## **【重要】OB部署完成之后** - -**不能使用sys租户充当业务租户使用** -**不能使用sys租户充当业务租户使用** -**不能使用sys租户充当业务租户使用** - -### **1. 连接方式** - -obproxy代理方式: -```sql -mysql -h xxx.xxx.xxx.xxx -uroot@租户名称#集群名称 -P2883 -p -c -A oceanbase -obclient -h xxx.xxx.xxx.xxx -uroot@租户名称#集群名称 -P2883 -p -c -A oceanbase -``` - -observer直连方式(不能带#集群名称): -```sql -mysql -h xxx.xxx.xxx.xxx -uroot@租户名称 -P2881 -p -c -A oceanbase -obclient -h xxx.xxx.xxx.xxx -uroot@租户名称 -P2881 -p -c -A oceanbase -``` - -### **2. 创建一个业务租户(5C 10G)** - -**SQL语句方式:** - - - 创建资源单元 - -```sql -CREATE RESOURCE UNIT unit_name1 -max_cpu =5, min_cpu =5, -memory_size ='10G', -log_disk_size ='30G', -max_iops =10000, min_iops =10000, iops_weight =1; -``` - - - 创建资源池 - -```sql -CREATE RESOURCE POOL pool_name1 -UNIT = 'unit_name1', -UNIT_NUM = 1, -ZONE_LIST = ('zone1','zone2','zone3'); -``` - - - 创建租户 - -```sql -CREATE TENANT IF NOT EXISTS tenant_name1 -charset='utf8mb4', comment 'mysql tenant/instance', -primary_zone='RANDOM', -resource_pool_list = ('pool_name1') set ob_tcp_invited_nodes = '%'; -``` - -**OBD命令方式:** - -```sql -obd cluster tenant create 部署名称 -n 租户名称 --max-cpu=5 --memory-size=10G --log-disk-size=30G --max-iops=10000 --iops-weight=1 --unit-num=1 --charset=utf8mb4 - -#不带参数将创建一个最大剩余资源的租户 -obd cluster tenant create 部署名称 -n 租户名称 -``` - -**OCP白屏方式:** -![image.png](/img/deploy_oceanbase/production_deployment/1690526122198-61f4da88-5b00-4c52-8f1e-76b481776f5d.png) - diff --git a/docs/user_manual/user_best_practices/deploy_tools/_index.md b/docs/user_manual/user_best_practices/deploy_tools/_index.md deleted file mode 100644 index 67c4aa70e..000000000 --- a/docs/user_manual/user_best_practices/deploy_tools/_index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 生态组件部署 -weight: 2 ---- diff --git a/docs/user_manual/user_best_practices/deploy_tools/deploy_obd.md b/docs/user_manual/user_best_practices/deploy_tools/deploy_obd.md deleted file mode 100644 index 21239ea1b..000000000 --- a/docs/user_manual/user_best_practices/deploy_tools/deploy_obd.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: 安装部署工具 - OBD -weight: 1 ---- - - -## 方法一:使用 all-in-one 安装包安装 OBD(推荐) - -all-in-one 安装包是 OceanBase 社区版一键安装包,包括数据库软件和 OBD、OBProxy、OBClient、OCP Express(从4.1版本开始)、Prometheus、Grafana。可以在安装好 OBD 以后快速部署集群。 - -**在线安装:** - -``` -[admin@test001 ~]$ bash -c "$(curl -s https://obbusiness-private.oss-cn-shanghai.aliyuncs.com/download-center/opensource/oceanbase-all-in-one/installer.sh)" -[admin@test001 ~]$ source ~/.oceanbase-all-in-one/bin/env.sh -``` - -**离线安装:** - -如果机器无法连接网络,可以通过下载安装包的方式安装。 - -1. 从 [OceanBase 软件下载中心](https://www.oceanbase.com/softwarecenter) 下载最新的 all-in-one 安装包,并将其复制到中控机中 -2. 解压安装 -``` -[admin@test001 ~]$ tar -xzf oceanbase-all-in-one-*.tar.gz -[admin@test001 ~]$ cd oceanbase-all-in-one/bin/ -[admin@test001 bin]$ ./install.sh -[admin@test001 bin]$ source ~/.oceanbase-all-in-one/bin/env.sh -``` - -## 方法二:使用 RPM 包安装 - -**在线安装** -``` -[admin@test001 ~]$ sudo yum install -y yum-utils -[admin@test001 ~]$ sudo yum-config-manager --add-repo https://mirrors.aliyun.com/oceanbase/OceanBase.repo -[admin@test001 ~]$ sudo yum install -y ob-deploy -[admin@test001 ~]$ source /etc/profile.d/obd.sh -``` - -**离线安装** -若您的机器无法连接网络,您可从 [OceanBase 软件下载中心](https://www.oceanbase.com/softwarecenter) 下载所需版本的 OBD。 - -在 CentOS 或 RedHat 系统上,执行如下命令安装 OBD: - -``` -sudo yum install ob-deploy-*.rpm -``` -在 Ubuntu 或 Debian 系统上,执行如下命令安装 OBD: - -``` -sudo alien --scripts -i ob-deploy-*.rpm -``` - -**配置 OBD** - -在线部署 OceanBase 集群或通过 all-in-one 安装包部署 OceanBase 集群,无需配置。 - -1. 执行如下命令禁用远程仓库 -``` -obd mirror disable remote -``` - -2. 确认 Type=remote 对应的 Enabled变成了 False,则说明已关闭远程镜像源 -``` -obd mirror list -``` - -3. 在安装包所在目录执行如下命令将下载好的安装包上传到本地仓库 -``` -obd mirror clone *.rpm -``` - -4. 查看本地仓库的安装包列表 -``` -obd mirror list local -``` - -在输出的列表中查看到部署所需安装包即表示上传成功。 - - - - diff --git a/docs/user_manual/user_best_practices/deploy_tools/deploy_ocp.md b/docs/user_manual/user_best_practices/deploy_tools/deploy_ocp.md deleted file mode 100644 index 5cd6f6b3e..000000000 --- a/docs/user_manual/user_best_practices/deploy_tools/deploy_ocp.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: 集群管理平台 - OCP -weight: 2 ---- - -> 安装的版本是 OCP V4.0.3_CE_BP1,该版本集成为 ALL-IN-ONE 软件包,可以直接在页面安装部署集群以及服务,与其他的版本安装方式可能不同,其他版本安装可以参考官网。 - -## 启动 ocp-installer - -1. 下载并解压软件包 - -``` -tar -xf ocp-server-all-in-one-1.0.0-YYYYMMDDhhmmss.el7.x86_64.tar.gz -``` - -2. 安装部署程序 - -``` -cd ocp-server-all-in-one/bin && sh install.sh -``` - -3. 环境变量 - -``` -source ~/.ocp-server-all-in-one/bin/env.sh -``` - -4. 执行 ocp-installer install ,并根据返回的 IP 地址在浏览器中打开链接开始部署,-pxxx 可以自定义端口。 - -``` -[root@obtest bin]# ocp-installer install -Disable remote ok -start Ocp Installer install in 0.0.0.0:8680 -please open http://172.xx.xx.xx:8680 -``` - -## 开始部署 - -1. 向导页面点击开始部署 - -![image.png](/img/deploy_tools/deploy_ocp/p1.png) - -2. 先选择 OCP MetaDB 的配置方式, - -- 创建全新的 OceanBase 数据库,会在后续操作中选择机器、配置来部署集群以及创建租户; -- 使用已有的 OceanBase 数据库,则需要填写已有集群的配置信息来创建 MetaDB 租户。 - ![image.png](/img/deploy_tools/deploy_ocp/p2.png) - -3. 配置 MetaDB 集群信息 - -填写 MetaDB 集群部署的信息,包括主机(可以多台)、部署用户、路径等。等待配置检查完成。 - -
注意:root@sys 密码一定要保存好,后续登陆 sys 租户需要用这个密码,并且没办法修改和找回。
- -![image.png](/img/deploy_tools/deploy_ocp/p3.png) - -4. MetaDB 集群资源分配 - -- 内存总数为系统中 free 的内存数,如果缓存占用太多可以手动刷新一下。 -- memory_limit 为分配给集群的内存大小。 -- 日志文件是指 CLOG 文件的大小,建议是内存的 3 倍。 -- 数据文件和日志文件会先预占用分配的空间大小,所以建议合理分配空间,强烈建议日志和数据文件分盘,避免相互影响。 - -![image.png](/img/deploy_tools/deploy_ocp/p4.png) - -5. MetaDB 集群部署预检查 - -如果有一些检查没有通过,可以通过失败项内的详情,到官网或者论坛寻找解决方案。 -![image.png](/img/deploy_tools/deploy_ocp/p5.png) - -6. 集群部署 - -可以通过部署日志查看集群、链接、账密信息等。 - -如果部署失败,也可以通过这里查看报错步骤以及失败原因,修复完成后重新部署。 -![image.png](/img/deploy_tools/deploy_ocp/p6.png) - -7. OCP 配置 - -配置 OCP 服务信息,包括管理员(admin)密码、路径、端口等。 - -配置元信息租户和监控租户的账号密码,这两个租户的 root 密码实际为空(可以用配置的密码和空密码尝试)。 - -![image.png](/img/deploy_tools/deploy_ocp/p7.png) - -8. OCP 服务及租户资源分配 - -包括 OCP 服务、元信息租户以及监控租户的 CPU 及内存分配。 - -元信息租户不小于 1C2G,监控租户不小于 2C4G。 - -![image.png](/img/deploy_tools/deploy_ocp/p8.png) - -9. OCP 服务预检查 - -![image.png](/img/deploy_tools/deploy_ocp/p9.png) 10. 部署 OCP 服务 - -访问地址为页面展示的地址。 - -
管理员密码一定要记住,否则将没办法登录 OCP 平台。
- -![image.png](/img/deploy_tools/deploy_ocp/p10.png) - -11. 登录 OCP - -![image.png](/img/deploy_tools/deploy_ocp/p11.png) -![image.png](/img/deploy_tools/deploy_ocp/p12.png) - -接下来就可以做创建集群、集群管理维护等一系列的操作了。 diff --git a/docs/user_manual/user_best_practices/deploy_tools/deploy_oms.md b/docs/user_manual/user_best_practices/deploy_tools/deploy_oms.md deleted file mode 100644 index da621db89..000000000 --- a/docs/user_manual/user_best_practices/deploy_tools/deploy_oms.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: 数据迁移平台 - OMS -weight: 3 ---- - -> OMS 迁移任务使用的资源比较多,建议机器预留充足的资源,比如单个任务内存10G以上。 - -# 安装 influxdb -如果需要 OMS 收集和展示监控数据,那么需要安装部署 influxdb;不需要的话可以忽略,直接安装 OMS。 - -1. 下载 -``` -wget https://oms-images.oss-cn-shanghai.aliyuncs.com/current_branchs/influxdb_1.8.tar.gz -``` - -2. 加载镜像 -``` -docker load -i influxdb_1.8.tar.gz -``` - -3. 启动 Docker 容器 -``` -# 端口需要使用 8086 端口 -sudo docker run -dit -p 8086:8086 -p 14444:14444 \ --v {挂载数据盘}:/var/lib/influxdb \ ---env INFLUXDB_BIND_ADDRESS=127.0.0.1:14444 \ ---env INFLUXDB_HTTP_AUTH_ENABLED=true \ ---env INFLUXDB_HTTP_PING_AUTH_ENABLED=true \ ---name=oms-influxdb \ -influxdb:1.8 -``` - -4. 设置 账号密码 -``` -# 查看容器,获取对应容器 NAMES -docker ps -# 指定 NAMES 进入到容器 -docker exec -it ${INFLUXDB_NAME} bash -# 打开 InfluxDB 控制台 -cd /usr/bin -./influx -# 创建账号密码 -create user "${USER_NAME}" with password '${PASSWORD}' with all privileges -``` - -# 安装OMS - -1. 下载安装包(可以去官网下载最新版本) - -``` -wget https://obbusiness-private.oss-cn-shanghai.aliyuncs.com/download-center/opensource/oms/4.2.0-CE/oms_4.2.0-ce.tar.gz -``` - -2. 加载安装包 -``` -# 如果没有安装 docker 想要先进行安装和启动 -yum install -y docker -service docker start -docker load -i oms_4.2.0-ce.tar.gz -``` - -3. 创建 OMS Meta 租户 -``` -# 命名和资源可自定义 -create resource unit unit_oms max_cpu 4, min_cpu 4, memory_size '8G', log_disk_size '24G', max_iops 100000; -create resource pool pool_oms unit = 'unit_oms', unit_num = 1, zone_list = ('zone1'); -create tenant oms_meta replica_num = 1,primary_zone='RANDOM', resource_pool_list=('pool_oms') set ob_tcp_invited_nodes='%'; - -# 修改 root 密码 -set password for root=password('xxxx'); -``` - -4. 修改配置文件 - -``` -[root@iZ0jl7bmjvyd7ojkpcdiggZ oms]# cat config.yaml -# OMS 社区版元数据库信息 -oms_meta_host: ${oms_meta_host} -oms_meta_port: ${oms_meta_port} -oms_meta_user: ${oms_meta_user} -oms_meta_password: ${oms_meta_password} - -# 用户可以自定义以下三个数据库的名称,OMS 社区版部署时会在元信息库中创建出这三个数据库 -drc_rm_db: drc_rm_db -drc_cm_db: drc_cm_db -drc_cm_heartbeat_db: drc_cm_heartbeat_db - -# OMS 社区版集群配置 -# 单节点部署时,通常配置为当前 OMS 社区版机器 IP(建议使用内网 IP,不建议使用 127.0.0.1) -cm_url: http://172.xx.xx.xx:8088 -cm_location: 0 # 地域码,取值范围为 [0,127] -# 单节点部署时,无需设置 cm_region -cm_region: '' -# 单节点部署时,无需设置 cm_region_cn -cm_region_cn: '' -cm_is_default: true -cm_nodes: - - 172.xx.xx.xx - -# 时序数据库配置 -# 默认值为 false。如果您需要开启指标汇报功能,请设置为 true -tsdb_enabled: true -# 当 tsdb_enabled 为 true 时,请取消下述参数的注释并根据实际情况填写 -tsdb_service: 'INFLUXDB' -tsdb_url: '172.xx.xx.xx:8086' -tsdb_username: ${tsdb_user} # 上面创建的账号密码 -tsdb_password: ${tsdb_password} -``` - -5. 查看相关镜像 -``` -# 获取 IMAGE ID 作为后面 -docker images -``` - -6. 从加载的镜像中获取部署脚本 -``` -sudo docker run --name oms-config-tool bash && sudo docker cp oms-config-tool:/root/docker_remote_deploy.sh . && sudo docker rm -f oms-config-tool -``` - -7. 通过部署脚本启动部署工具 -``` -# /data/oms 为 OMS 容器挂载目录,/root/oms/config.yaml 为配置文件地址,根据实际情况填写即可 -sh docker_remote_deploy.sh -o /data/oms -c /root/oms/config.yaml -i sh docker_remote_deploy.sh -o -c <已有 config.yaml 配置文件地址> -i <本机 IP 地址> -d -``` - -根据工具提示完成部署,每次输入后,通过回车进入下一步。 - -除了这两步,其他一路回车就好,会根据配置文件作为默认配置。部署过程中需要关注是否有明显的报错,比如初始化 SQL 插入失败。 -![image.png](/img/deploy_tools/deploy_oms/p1.png) - -8. 部署完成后,如果您需要修改配置,请登录至运行的 OMS 容器中进行以下操作: - -- 登陆到容器 -``` -[root@iZ0jl7bmjvyd7ojkpcdiggZ oms]# docker ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -c51d1b1bbe2f 89bcd10c636e "/bin/sh -c '/usr/..." 10 minutes ago Up 10 minutes OMS_20231117_103102 -7a2dfd02ed79 influxdb:1.8 "/entrypoint.sh in..." 14 hours ago Up 14 hours 0.0.0.0:8086->8086/tcp, 0.0.0.0:14444->14444/tcp oms-influxdb -[root@iZ0jl7bmjvyd7ojkpcdiggZ oms]# docker exec -it c51d1b1bbe2f bash -``` - -- 修改配置 - - 1. 根据业务需求,修改 config.yaml 文件。 - 2. 执行命令 python -m omsflow.scripts.units.oms_init_manager --init-config-file。 - 3. 执行命令 supervisorctl restart oms_console oms_drc_supervisor。 - - config.yaml 文件的位置,可以执行 2 查看使用的配置。 -![image.png](/img/deploy_tools/deploy_oms/p2.png) - -9. 查看组件运行状态 -``` -[root@iZ0jl7bmjvyd7ojkpcdiggZ ~]# supervisorctl status -nginx RUNNING pid 1045, uptime 0:11:03 -oms_console RUNNING pid 1053, uptime 0:10:53 -oms_drc_cm RUNNING pid 1125, uptime 0:10:42 -oms_drc_supervisor RUNNING pid 1390, uptime 0:10:32 -sshd RUNNING pid 1684, uptime 0:10:22 -``` - -10. 登陆 OMS 页面 - -地址:http://xx.xx.xx.xx:8089/ - -默认账号/密码为:admin/aaAA11__ - -![image.png](/img/deploy_tools/deploy_oms/p3.png) - -11. 登陆完成后,即可看到页面详情 -![image.png](/img/deploy_tools/deploy_oms/p4.png) - diff --git a/docs/user_manual/user_best_practices/development_practice/_index.md b/docs/user_manual/user_best_practices/development_practice/_index.md deleted file mode 100644 index a5c4aaab5..000000000 --- a/docs/user_manual/user_best_practices/development_practice/_index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 开发实践 -bookCollapseSection: false -weight: 5 ---- diff --git a/docs/user_manual/user_best_practices/development_practice/development_specification.md b/docs/user_manual/user_best_practices/development_practice/development_specification.md deleted file mode 100644 index dee536fb3..000000000 --- a/docs/user_manual/user_best_practices/development_practice/development_specification.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: 开发规范 -weight: 1 ---- -# **开发规范** - -> **说明** -> -> 本文档规范仅供参考。 - -## **1. 总则** - -### **1.1 目标** - -为了项目组规范使用 OceanBase 分布式数据库产品,拟定关于 OceanBase 产品的开发指引及规范。 - -### **1.2 适用范围** - -本规范适用于参与各类应用实现中需使用到数据库基础数据管理功能的业务人员和技术人员,以及负责应用投产后的运维人员。 - -### **1.3 术语定义** - -**租户(tenant)** - -OceanBase 数据库是一个多租户的数据库。在一个 OceanBase 数据库集群之中,可以提供多个数据库服务的实例。每个数据库服务的实例不感知其他实例的存在。这些不同的实例,每一个实例叫做一个租户。租户拥有一组计算和存储资源,提供一套完整独立的数据库服务。 - -**NDV(Number of Distinct Values)** - -数据列所包含的不同值的数量,或称为区分度。数值越大,平均每个值过滤出来的行数越少,索引过滤效果越好。 - -**复合索引(Composite Index)** - -包含一个以上列的索引,也称为联合索引。 - -**执行计划(Execution Plan)** - -数据库 SQL 执行使用的一种可行策略,以改善性能并让 SQL 更快、更准确地处理。通过在 SQL 语句之前增加 explain 关键字,显示具体的执行计划。不同的数据库其执行计划的显示内容有所不同。 - -**表连接(Table Join)** - -多张表关联查询时进行的合并操作。数据库优化器根据表的大小和查询条件等信息,选择一个最低成本的表连接方式来进行表连接操作。常见的表连接方式有嵌套循环连接(Nested Loop Join),散列连接(Hash Join),排序合并连接(Sort Merger Join)。 - -**驱动表(Drive Table)** - -又称外层表(Outer Table),表连接中的基础表,并以此表的数据为依据,逐步获得其它表(被驱动表)的数据,直至最终查询到所有满足条件的数据的第一个表。左连接中,左表是驱动表,右表是被驱动表;右连接中,右表是驱动表,左表是被驱动表;内连接中,表数据量较小的表会由数据库自动选择作为驱动表去驱动大表。 - -**死锁(Dead Lock)** - -两个或多个事务各自占有对方的期望获得的资源,形成的循环等待,彼此无法继续正常执行的一种状态。 - -**自增列(Auto Increment Column)** - -如果创建表时需要某个数值列的值不重复并且保持递增,这就是自增列。列的类型需定义为 AUTO_INCREMEN。 - -**序列(Sequence)** - -在数据库中按照一定规则自增的一种数字序列。 - -## **2. 数据库设计** - -OceanBase 数据库对于使用者来讲,自上而下结构为:集群(cluster) -> 租户(tenant) -> 数据库(database)-> 表(table);OceanBase 数据库支持 MySQL 和 Oracle 两种模式,模式为租户级别,在创建租户的时候指定。 - -### **2.1 租户命名设计** - -1. 【推荐】普通租户名:小写 32 字符,t + 应用标识(4 位)+ 租户编号(XX,从 00 开始)。 - -2. 【推荐】单元化租户名:2 字符,t + 应用标识(4 位)+ Zone 类型(G/C/R)+ 租户编号(XX,从 00 开始)。 - -### **2.2 数据库命名设计** - -1. 【建议】数据库名:应用标识(4 位)+ 子应用名(最多 4 位,可选)+ db,例如:gcdsdb 或 gcdsamndb(Oracle 租户请忽略)。 - -### **2.3 表命名设计** - -1. 【强制】MySQL 租户表名称必须控制在 64 个字节以内。 - -2. 【强制】创建表名和列名时统一使用小写,禁止大小写混用。 - -3. 【强制】表名和列名只能使用字母、数字和下划线,并且必须以字母开头,不得使用系统保留字和特殊字符。表名禁止两个下划线中间只出现数字。 - -4. 【推荐】联机业务表和批量业务表分开存放,防止跑批时影响联机业务。 - -### **2.4 字段设计** - -1. 【强制】每张表上都必须设定主键。 - -2. 【强制】MySQL 模式小数类型使用 decimal 类型存储,禁止使用 double,float。 - -3. 【强制】对于字符类型,MySQL 模式建议采用 varchar,不建议使用 tinytext、text、mediumtext、longtext。 - -4. 【强制】禁止使用枚举列类型:enum(‘x’,’y’,’z’),需使用字符串类型替代。 - -5. 【强制】对于日期类型,有时间精度要求的业务,可以使用 DATATIME;对精度没要求的,设置为 DATE 即可;不要使用字符作为时间字段的数据类型;建议不要使用 TIMESTAMP,范围有限而且要避免 TIMESTAMP+TIME_ZONE=‘SYSTEM’ 的问题。 - -6. 【推荐】非定长字段建议使用 varchar。 - -7. 【推荐】建议表上默认添加 gmt_create 和 gmt_modified 两字段,记录创建和变更时间;gmt_create 加上 default current_timestamp 属性,gmt_modified 加上 default current_timestamp on update current_timestamp 属性。 - -8. 【强制】建议表中所有字段都配置为 NOT NULL 属性,并根据业务需要定义 DEFAULT 值。 - -9. 【强制】每个字段应加注释。 - -10. 【强制】禁止使用外键自引用且级联删除更新的表字段约束定义,否则可能导致重复删除的问题。 - -### **2.5 自增列设计** - -1. 【强制】创建序列必须指定 cache 大小,cache 值可设置为租户 tps*100*60*60*24。 - -2. 【强制】序列禁止添加 order 属性。 - -3. 【强制】自增列字段使用 bigint,禁止使用 int 类型,防止存储溢出。 - -4. 【强制】禁止使用自增列作为分区键。因为当使用自增列作为分区键时, 不保证自增列的值分区内自增,并且性能损耗较大。 - -### **2.6 分区表设计** - -OceanBase 数据库在 MySQL 模式下支持 range/range columns 分区、hash/key 分区和 list/list columns 分区,支持二级分区。 - -1. 【注意】分区表的分区规则在表创建的时候需要指定,OceanBase 数据库 4.0 以后版本才支持将非分区表在线改造成分区表的 offline DDL 操作。 - -2. 【推荐】分区表索引建议:按照本地索引 -> 全局分区索引 -> 全局索引的顺序进行选择,只有在有必要的时候才使用全局索引,原因是全局索引会降低 DML 的性能,可能会因此产生分布式事务。 - -3. 【强制】分区表的查询或修改必须带上分区键。 - -4. 【强制】对于 range 分区,需要业务自行做分区管理,定时增加分区,避免分区越界问题。 - -5. 【推荐】range 分区不建议指定 MAXVALUE ,否则后续无法新增分区。 - -6. 【推荐】在业务查询条件明确的情况下根据业务场景进行分区规划,分区目的是要利用分区裁剪的能力提高查询效率,禁止在场景不明确的情况下随意规划分区规则。如果查询条件部分场景下仅能覆盖一级分区,建议按照一级分区规划,不需要强行规划为二级分区。 - -7. 【推荐】OceanBase 数据库 4.0 以前版本,需要控制一个事务中的分区数参与数量,建议 XA 事务的分区参与数在 600 以内,普通事务的分区参与数在 1000 以内。需要注意 insert select 存在未按结果集裁剪,导致事务提交按照所有分区作为参与者的情况。 - -8. 【推荐】使用分区表时要选择合适的拆分键(列)以及拆分策略。 - -9. 【推荐】为保证 HASH 分区模式下分区间数据均衡,MySQL 模式下分数个数建议采用奇数个分区,即 3、7、15、31 这样。 - -10. 【强制】禁止使用生成列作为分区键。 - -11. 【强制】有历史数据清理的表,需要根据业务使用场景和清理周期进行分区表设计。如交易流水表,可按日分区并按日删除旧分区。 - -12. 【注意】关于分区键在多维业务查询场景下的选择,如账号和卡号同时存在情况下,需根据业务使用频率和业务重要性等维度来考虑分区键的选择。 - -### **2.7 索引设计** - -在 OceanBase 数据库中,索引可以分为两种类型:本地索引和全局索引,默认创建的是全局索引。两者之间的区别在于:本地索引与分区数据共用分区,全局索引为单独分区;创建本地的索引需要指定 local 关键字,未指定或者指定 global 关键字为全局索引。 - -1. 【推荐】分区表建议优先创建 LOCAL 索引。 - -2. 【强制】不允许创建全文索引。 - -3. 【强制】单个索引字段值的总长度不能超过 64KB。 - -4. 【强制】单个表上的索引个数建议不超过 5 个左右。 - -5. 【强制】索引名称必须控制在 64 个字符以内,主键索引命名为 pk_表名_字段名,唯一索引名为 uk_表名_字段名;普通索引名则为 idx_表名_字段名。 - -6. 【推荐】在建索引时,建议将表中可能会被查询投影、ORDER BY、GROUP BY 等操作频繁使用的列添加到索引后面,形成覆盖索引避免回表查询或排序。 - -7. 【推荐】组合索引列的个数控制在 3 个字段及以内,不能超过 5 个。 - -8. 【推荐】创建组合索引的时候,区分度(NDV)最高的在最左边,即唯一性越高的字段作为联合索引的前引导列。 - -9. 【强制】避免重复索引,冗余的索引影响数据的增删改效率,同时浪费存储成本,如索引 (a,b,c) 已创建的情况下不要再创建索引 (a) 和 (a,b)。 - -### **2.8 其它对象设计** - -1. 【强制】MySQL 模式禁止在应用程序设计阶段使用外键、临时表、存储过程以及触发器 - -2. 【强制】禁止在 OceanBase 数据库中使用 dblink,跨租户访问建议采用应用接口调用实现。 - -3. 【强制】禁止在数据库列中存放大文件、介质、图片,音视频 建议将这些数据存入共享介质中,列中存放指向共享介质的 bucket 地址。 - -## **3. SQL 编写规范** - -### **3.1 单表查询规范** - -1. 【强制】SELECT 语句必须指定具体字段名称,禁止写成 “select *”。 - -2. 【推荐】统计行数使用 count(*),会统计值为 NULL 的行。 - -3. 【推荐】建议使用 UNION ALL 替换 UNION,并且 UNION 分支尽量控制在 5 个以内。 - -4. 【强制】禁止大表查询使用全表扫描。 - -5. 【强制】SQL 语句中条件字段的数据类型保持一致,避免隐式转换。 - -6. 【强制】SQL 语句 select 投影字段中禁止使用数据库保留字。 - -7. 【推荐】建议 IN 子查询中条件常量值个数小于 100。 - -8. 【推荐】尽量避免 buffer 表场景出现,业务改造,如无法改造,考虑设置 table_mode='queuing' 来让转储线程对此类表走 buffer minor merge 合并多版本数据。 - -9. 【推荐】WHERE 条件里不建议在表字段做算术运算和函数计算。 - -10. 【强制】WHERE 条件上禁止变换恒真恒假条件,例如 SQL 中出现有 1=1,2=2 情况。 - -11. 【推荐】避免使用分页查询时深度分页 即不建议 offset 设置过大。 - -12. 【强制】对于非主键(唯一键)或主键关联查询应使用物理分页(即 SQL 中使用 limit),控制结果集返回的行数,严禁使用应用层 mybatis 数据分页,避免出现 JVM OOM。 - -13. 【推荐】SQL 查询不建议使用左模糊和全模糊,建议使用搜索引擎来解决模糊查询。 - -14. 【推荐】MySQL 模式下,通过 ANALYZE 语句进行统计信息收集。当业务发起对表数据大量删除或导入未进行合并时候,考虑通过手工方式收集统计信息。 - -15. 【注意】避免在 SQL 中对变量进行赋值,尤其是分布式处理结果赋值给变量。 - -### **3.2 增删改语句规范** - -1. 【强制】删改语句必须带 WHERE 条件,避免形成大事务。 - -2. 【推荐】全表删除建议使用 TRUNCATE TABLE 语句。 - -3. 【强制】TRUNCATE TABLE 语句执行结束需要等待 1s~3s,确认数据清空后再操作插入数据。 - -4. 【强制】禁止使用 insert ignore 语句进行插入,应使用 replace into 、insert into(适用于 OceanBase 数据库 3.x 版本)。 - -### **3.3 多表关联规范** - -1. 【推荐】多表连接查询推荐使用别名,且 SELECT 列表中要用别名引用字段。 - -2. 【推荐】TP 类场景尽量避免超过 5 个以上表关联 join,多表 join 需保证关联字段数据类型保持一致。AP 类场景按实际情况判断。 - -3. 【强制】多表关联必须有关联条件,禁止出现笛卡尔积(explain 看到 CARTESIAN 关键字)。 - -4. 【推荐】将多层子查询嵌套改写成表顺序连接。 - -5. 【推荐】冗余 SQL 多表查询考虑 CTE 优化改写。 - -6. 【强制】MySQL 模式对于 CTE recursive 语法避免递归行数超过 1000,递归深度越深效率越差。 - -### **3.4 事务规范** - -1. 【强制】批量操作数据时,程序必须有异常处理能力,以及事务失败重试机制。 - -2. 【推荐】OceanBase 2.X 版本控制事务大小,单分区事务数据量不超过 100MB。大事务场景下,建议采用批量操作并及时 commit 提交。 - -3. 【强制】应用程序中禁止设置 timezone、SQL_mode 和 isolation_level 变量。 - -4. 【强制】事务隔离级别应使用默认的 RC 读已提交,目前 RR 和 serialize 对并发限制较大。 - -5. 【强制】OBProxy 路由 SQL 规则注意如下的情况: - - a. 以下几种情况,proxy 能够将请求发送至正确的 server,但是 server 反馈的信息可能不准,不建议使用。 - - ```sql - select '1'; - select * from t1; - select '1' from dual; - ``` - - b. 以下几种情况,proxy 会强制将请求路由至上一次使用的 server,但是 server 反馈信息可能不准,不建议使用。 - - ```sql - show warnings; - select *from t1; - show count(*) errors; - select * from t1; - ``` - -6. 【强制】DDL 和 DML 不要在同一个事务里面。 - -### **3.5 DDL 规范** - -1. 【强制】TRUNCATE TABLE 语句执行结束需要等待 1s~3s,确认数据清空后再操作插入数据。 - -2. 【强制】在线 DDL 操作建议在业务低峰时段进行。 - -3. 【强制】OceanBase 数据库 3.x 之前版本 DDL 控制并发度不超过 40。 - -## **4. 字符集** - -目前 OceanBase 数据库支持 utf8mb4、gbk、gb18030 和 binary,可以在租户级、database 级、表级、字段级、session 级设置字符集,字符集选定后,需要选定 collate。以 utf8mb4 为例,支持两种 collate,分别是 utf8mb4_general_ci 和 utf8mb4_bin,两者的区别在于 utf8mb4_general_ci 为大小写不敏感,utf8mb4_bin 为大小写敏感,这会影响排序和字符的比较。 - -collate 可以指定租户级、database 级、表级、字段级;优先级为字段 > 表级 > database 级 > 租户级,如果不指定,默认从上至下继承。 - -> **说明** -> -> 建议 database 级、表级、字段级均不要指定 collate。如果有大小写敏感的要求请在创建开发阶段提出,统一在租户级别指定。 - -表关联条件的两个字段的 collation type 要保持一致,否则会出现无法正确使用到索引的情况。 - -## **5. Java 应用访问 OceanBase 数据库规范** - -OceanBase 数据库的 MySQL 租户兼容 MySQL 的连接协议,使用标准的 MySQL JDBC 可以连接 OceanBase 数据库的 MySQL 租户,驱动推荐使用 MySQL JDBC 5.1.47 版本。对于 OceanBase 数据库的 Oracle 租户,必须使用 OceanBase 自研的 oceanbase-client 驱动。 - -1. 【强制】Java 客户端(连接池 ConnectionProperties 或 JdbcUrl)需要添加对应超时和重连参数:socketTimeout、connectTimeout、useLocalSessionState。 - - | 参数 | 说明 | - | --- | --- | - | socketTimeout | 网络读超时时间,如果不设置默认是 0,使用 OS 默认超时时间,根据实际情况来设置。 | - | connectTimeout | 链接建立超时时间,如果不设置默认是 0,使用 OS 默认超时时间,根据实际情况来设置。 | - | useLocalSessionState | 是否使用 autocommit,read_only 和 transaction isolation 的内部值,默认为 false,建议为 true。 | - -2. 【强制】useLocalSessionState=true,不能使用 `set autocommit=0/set tx_isolation='read-committed/set tx_read_only=0`,需要通过 JDBC 的接口方式调用,对应的接口为 `setAutoCommit(false)/setTransactionIsolation('read-committed')/setReadOnly(false)`。 - - MySQL 租户连接示例: - - ```bash - | String url = "jdbc:oceanbase://IP:端口/database?autoReconnect=true&socketTimeout=6000000& connectTimeout=60000&useLocalSessionState=true&useUnicode=true&characterEncoding=utf-8"; - String username = "用户名@租户名#集群名"; - String password = "***"; - Connection conn = null; - try { - Class.forName("com.mysql.jdbc.Driver "); - conn = DriverManager.getConnection(url, username, password); - PreparedStatement ps = conn.prepareStatement("select to_char(sysdate,'yyyy-MM-dd HH24:mi:ss') from dual;"); - ResultSet rs = ps.executeQuery(); - rs.next(); - System.out.println("sysdate is:" + rs.getString(1)); - rs.close(); - ps.close(); - } catch (Throwable e) { - e.printStackTrace(); - } finally { - if (null != conn) { - conn.close(); - } - } | - ``` - -## **6. 附录** - -### **6.1 批量处理优化** - -建议用批量 SQL 语句,减少与数据库交互次数。批量插入与批量更新 JDBC 配置示例:`jdbc:oceanbase://IP:2883/dbname? useServerPrepStmts=false&rewriteBatchedStatements=true&allowMultiQueries=true` - -| 配置属性 | 默认值 | 说明 | -| --- | --- | --- | -| allowMultiQueries | false | 设置为 true 时,JDBC 驱动允许应用代码把多个 SQL 用分号(`;`)拼接在一起,作为一个 SQL 发给 server 端。 | -| rewriteBatchedStatements | false | 设置为 false 时,OceanBase 的 JDBC 驱动在默认情况下会无视 `executeBatch()` 语句,把批量执行的一组 SQL 语句拆散,一条一条地发给数据库,此时批量插入实际上是单条插入,直接造成较低的性能。要想实际执行批量插入,需要将该参数置为 true,驱动才会批量执行 SQL。
即使用 addBatch 方法把同一张表上的多条 insert 语句合在一起,做成一条 insert 语句里的多个 values 值的形式,提高 batch insert 的性能。必须使用 prepareStatement 方式来把每条 insert 做 prepare,然后再 addBatch,否则不能合并执行。 | -| useServerPrepStmts | false | 设置为 false 的时候采用文本协议,设置为 true 的时候会采用二进制协议。如果 rewriteBatchedStatements 设置为 true,则此选项将设置为 false。默认值:false。 | - -> **注意** -> -> 使用批量更新中,需要如果一个 Batch 中存在对相同行的更新,不能走到批量更新的优化,为了保证更新顺序,退化为单条顺序执行。且在 OceanBase 数据库 3.2.3 版本前,批量更新中的语句必须是按主键更新才能走到批量更新的优化。 - -### **6.2 结果集使用** - -数据库驱动会根据不同的参数设置选择对应的 ResultSet 实现类,分别对应三种查询方式: - -- RowDataStatic 静态结果集,默认的查询方式,普通查询。 - -- RowDataDynamic 动态结果集,流式查询。 - -- RowDataCursor 游标结果集,服务器端基于游标查询。 - -建议小数据量场景的查询使用全量结果集;大数据量情况为避免客户端出现 OOM(Out of Memory)可以从应用控制查询结果集大小(例如,分页查询),或使用游标结果集与流式结果集的方式。 - -实际使用中往往需要根据用户的需求、内存大小等选择使用。可根据以下优劣势分析与实际场景结合选择使用。 - -### **6.3 Druid 建议** - -将应用和数据库连接进行业务操作,建议使用连接池。如果是 Java 程序,推荐使用 Druid 连接池,Druid 的版本建议采用 V1.2.8 及以上版本。 - -1. 添加 Maven 依赖 - - ```bash - - com.alibaba - druid - 1.2.8 - - ``` - -2. 配置建议,关注开启探活 - - ```bash - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ``` diff --git a/docs/user_manual/user_best_practices/operation_maintenance/_index.md b/docs/user_manual/user_best_practices/operation_maintenance/_index.md deleted file mode 100644 index dbc885853..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/_index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 运维实践 -bookCollapseSection: false -weight: 4 ---- diff --git a/docs/user_manual/user_best_practices/operation_maintenance/cluster_resource_upgrade.md b/docs/user_manual/user_best_practices/operation_maintenance/cluster_resource_upgrade.md deleted file mode 100644 index 385456276..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/cluster_resource_upgrade.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: 集群资源升配 -weight: 13 ---- - -## **主机资源升配** -### **内存、CPU升配** - -**场景** - -1. OB集群主机资源均为虚拟机(其他可扩容资源的同理)。 -2. 前期规划OB业务集群时,CPU、内存、存储资源有限,后期业务量上升需要针对集群内主机进行升配。 -3. OB集群已对外提供服务,不可以停止整个集群,要做到业务无感知。 - -**注意** - -- 集群内主机必须逐台操作(强烈建议主 Zone 下的 OBServer 主机放到最后操作),完成单台所有操作后再进行下一台升配操作。 -- 如果存在业务直连 OBServer 或者 OBProxy 的情况,需要提前切换到可用的节点,否则会导致这部分请求异常。访问集群使用 OCP ConfigUrl 的业务不受影响。 -- 如果停机升配时间比较长,需要根据预估维护时间调整 server_permanent_offline_time,否则停机超过设置的时长后,OceanBase 数据库会将其踢出成员列表。 - -**单台操作流程** - -1. OCP - 集群 - 总览 - OBServer 列表部分,单击目标主机列的“停止服务”,刷新页面等待该 OBServer 处于“服务已停止状态”。可参考[官方文档](https://www.oceanbase.com/docs/community-ocp-cn-10000000000866357)。 -2. poweroff 关机。 -3. 通过虚拟化底层实现方案升级对应虚拟机 CPU、内存配置。 -4. 开启虚拟机。 -5. OCP - 主机 - 主机列表, 看到该主机处于“”离线中“”状态, 右侧下拉选择“重启OCP Agent”,刷新页面等待该主机处于“在线”状态。 -6. OCP - 集群 - 总览 - OBServer 列表可以看到该 OBServer 处于“不可用”状态,点击启动,刷新页面等待该 OBServer 处于“运行中”状态,该目标主机升配完成。 -7. 由于我们每台 OBServer 上还部署了 OBProxy,所以要去 OCP - OBProxy- OBProxy集群内 - 总览 - OBProxy 列表中执行本主机 OBProxy 重启,刷新页面等待该主机处于“在线状态”。 - -**后续操作** - -所有主机操作完毕后,可以按需调整集群级别参数 memory_limit 或 memory_limit_percentage。 - -1. OCP创建的集群默认情况下 memory_limit_percentage 为80,该参数指定最大可用内存占比百分数,只有 memory_limit 参数为0时生效。 -2. memory_limit 参数默认为0。 -3. 当主机内存非常大的时候,建议使用 memory_limit 参数设置具体数值,以提升资源利用率。 - - -### **硬盘扩容** - -同理,不过如果硬盘可以在线扩容则不需要重启操作。如果想要了解硬盘扩容如何操作,可以自行搜索"Linux磁盘在线扩容"相关文章。 - -**后续操作** - -所有主机操作完毕后,可以按需调整集群级别参数 datafile_size 或 datafile_disk_percentage。 - -其中,当 datafile_disk_percentage 与 datafile_size 同时配置时,以 datafile_size 设置的值为准。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/common_operation.md b/docs/user_manual/user_best_practices/operation_maintenance/common_operation.md deleted file mode 100644 index 3d613f7a2..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/common_operation.md +++ /dev/null @@ -1,250 +0,0 @@ ---- -title: 常用操作 -weight: 1 ---- -# **常用操作** - - -## **创建租户** - -**方法一:OCP 创建** - -1. 确认可分配资源 - -具体可以分配多少内存,可以通过【资源管理】查看各节点的剩余资源 -![image.png](/img/operation_maintenance/common_operation/p1.png) -2. 新建租户 - -![image.png](/img/operation_maintenance/common_operation/p2.png) - -3. 填写租户信息 - -![image.png](/img/operation_maintenance/common_operation/p3.png) - -zone 优先级主要是 primary_zone 设置 leader优先级,如果优先级全部相同,那么 leader 会打散到所有节点上。 - -**方法二:手动创建** - -1. 确认可分配资源 - -新租户可以分配的内存大小为 memory_limit - system_memory - sys 租户内存,CPU 数量为 cpu_count - sys 租户 cpu。 -``` -# 查询参数 -show parameters where name in ('memory_limit','system_memory','cpu_count'); -# 查询 sys 租户资源 -select * from DBA_OB_UNIT_CONFIGS; -``` - -2. 创建租户 -``` -# 创建资源规格,5C14G,日志40G,IOPS 10000000。 -create resource unit unit_1 max_cpu 5, min_cpu 5, memory_size '15G', log_disk_size '50G', max_iops 10000000; -# 创建资源池,指定 资源规格以及 zone_list。 -create resource pool pool_1 unit = 'unit_1', unit_num = 1, zone_list = ('zone1','zone2','zone3'); -# 创建租户,指定副本数量3,primary_zone,以及资源池和白名单。 -create tenant perf replica_num = 3,primary_zone='RANDOM', resource_pool_list=('pool_1') set ob_tcp_invited_nodes='%'; -``` - -3. 修改 root 用户密码 - -创建完租户,默认的 root 密码为空,如果需要可以修改密码。 -``` -# 租户的 root 用户登陆后执行修改 sql -set password for root=password('xxx'); -``` - -## **连接数据库** - -主要有两种连接方式: - -1. 通过 OBServer 直连(默认端口 2881) -``` -mysql -h xxx.xxx.xxx.xxx -uroot@sys -P2881 -p -c -A oceanbase -``` - -2. 通过 OBProxy 连接(默认端口2883) -``` -mysql -h xxx.xxx.xxx.xxx -uroot@sys#obdemo -P2883 -p -c -A oceanbase -``` - -使用 OBProxy 连接时,用户信息需要包括【用户名@租户名#集群名】;如果是 OBServer 连接,那么只需要包括【用户名@租户名】。 - -## **参数和变量** - -**区别:** - -参数(parameter)可以控制集群的负载均衡、合并时间、合并方式、资源分配和模块开关等功能,主要针对集群、Zone、OBServer 和 租户级别进行配置。 - -变量(variable)可以控制数据库系统的各种行为,如缓存大小、并发连接数等,主要是针对租户级别内 Global 和 Session 级别进行设置。 - - -**查看参数和变量** - -OBServer 参数 : - -``` -# 方法一 -show parameters like '%enable_rebalance%'; -# 方法二 -show parameters where name in ('memstore_limit_percentage','freeze_trigger_percentage','writing_throttling_trigger_percentage'); -# 方法三 -select * from oceanbase.GV$OB_PARAMETERS where NAME in ('memstore_limit_percentage','freeze_trigger_percentage','writing_throttling_trigger_percentage'); -``` - -OBProxy 参数: -``` -show proxyconfig like '%query_digest_time_threshold%'; -``` - -查看变量 -```shell -show variables like '%timeout%'; -``` - - -**修改参数和变量** - -要注意,这里的参数修改会自动变更到安装目录的 etc 下的配置文件,但是如果是 OBD 创建的集群,不会自动同步到 OBD 的配置文件中,如果后续需要使用 OBD 重启,那么需要手动修改 OBD 的配置,防止启动后配置有差异影响业务。 - -OBServer 参数 : -``` -alter system set enable_rebalance=False; -``` - -OBProxy 参数: -``` -alter proxyconfig set query_digest_time_threshold='101ms'; -``` - -修改变量: -``` -# 设置全局级别变量,当前 session 不生效,新 session 生效。 -set global ob_query_timeout=10000000; -# 设置会话级别变量,当前 session 生效,其他 session 不生效。 -set session ob_query_timeout=10000000; -``` - -## **RootService 切主** - -```sql --- 操作 -ALTER SYSTEM SWITCH REPLICA leader LS=1 SERVER='目标ip:rpc_port' TENANT ='sys'; --- 实例 -ALTER SYSTEM SWITCH REPLICA leader LS=1 SERVER ='x.x.x.x:2882' TENANT ='sys' -``` - -## **清理租户并释放空间** - -通过清理租户释放空间时需先执行 `drop tenant xxx force;` 命令删除租户,再执行 `drop resource pool xxx;` 命令删除对应的资源池,两条命令均成功执行才算释放空间。 - -> **注意** -> -> 该操作会清理租户下的所有数据。 - -## **普通用户租户查看自己租户下所有的表** - -您可通过如下命令查询表类型,之后根据表类型查询表名。 - -```sql --- 查询表类型 -select distinct(OBJECT_TYPE) from DBA_OBJECTS; --- 根据表类型查表名 -select OBJECT_NAME from DBA_OBJECTS where OBJECT_TYPE='table' ; -``` - -## **清理 OBD 管理的集群信息** - -当 OBD 管理的集群被销毁后,直接删除 OBD 中该集群的存储信息即可彻底清理该集群,有如下两种方法。 - -* 方法一:您可执行 `obd cluster list` 命令查看集群所在的目录,通过 rm 命令清理目录即可,示例如下。 - - ```shell - [admin@obtest ~]$ obd cluster list - +----------------------------------------------------------------------+ - | Cluster List | - +-------------+--------------------------------------+-----------------+ - | Name | Configuration Path | Status (Cached) | - +-------------+--------------------------------------+-----------------+ - | test1 | /home/admin/.obd/cluster/test1 | destroyed | - | myoceanbase | /home/admin/.obd/cluster/myoceanbase | running | - +-------------+--------------------------------------+-----------------+ - - [admin@obtest ~]$ rm -rf /home/admin/.obd/cluster/test1 - ``` - -* 方法二:您可通过 `cd ~/.obd/cluster` 命令进入到 OBD 的集群信息目录下,删除对应的 Name 目录,示例如下。 - - ```shell - [admin@obtest ~]$ cd ~/.obd/cluster/ - [admin@obtest cluster]$ ll - total 12 - drwxr-xr-x 2 admin admin 4096 Jun 1 12:04 myoceanbase - drwxr-xr-x 2 admin admin 4096 May 26 14:22 test1 - - [admin@obtest ~]$ rm -rf test1 - ``` - -## **日志打印相关** - -### **调整日志文件以及记录数量** - -```sql --- 开启日志回收 -alter system set enable_syslog_recycle=true --- 关闭 wf 日志打印 -alter system set enable_syslog_wf=false --- 限制日志个数,按需调整(单个日志 256M) -alter system set max_syslog_file_count=10 --- 设置系统日志级别 -syslog_level (DEBUG/TRACE/INFO/WARN/USER_ERR/ERROR) -``` - -### **调整慢查询日志记录配置** - -SQL 执行时间默认情况下超过 1s 就会记录到 observer.log 日志或者在 OCP 白屏监控有记录。 - -该配置受租户参数 `trace_log_slow_query_watermark` 控制,可通过如下命令修改配置。 - -```sql --- 查看参数配置 -show parameters like '%trace_log_slow_query_watermark%'; --- 修改参数 -alter system set trace_log_slow_query_watermark='2s'; -``` - -### **OBProxy 日志打印** - -您可通过系统租户(root@sys)或者 root@proxysys 连接集群修改 OBProxy 日志打印相关配置。 - -```sql --- 查询参数 -show proxyconfig like '%log_file_percentage%'; --- 修改参数 -alter proxyconfig set log_file_percentage=75; -``` - -OBProxy 日志相关参数如下表所示。 - -| 参数 | 默认值 | 取值范围 | 解释 | -| --- | --- | --- | --- | -| **log_file_percentage** | 80 | [0, 100] | OBProxy 日志百分比阈值,超过阈值即进行日志清理。 | -| **log_dir_size_threshold** | 64GB | [256MB, 1T] | OBProxy 日志大小阈值。超过阈值即进行日志清理。 | -| **max_log_file_size** | 256MB | [1MB, 1G] | 单个日志文件的最大尺寸。 | -| **syslog_level** | INFO | DEBUG, TRACE, INFO, WARN, USER_ERR, ERROR | 日志级别。 | - -## **SQL 并行执行** - -您可通过增加并行加快索引创建,具体命令如下所示。 - -```sql --- 增加 SQL 执行内存百分比 -SET GLOBAL OB_SQL_WORK_AREA_PERCENTAGE = 30; --- 设置 DDL 并行度 -SET SESSION _FORCE_PARALLEL_DDL_DOP = 32; --- 并行度增加需要设置该参数,比所有并行度和大即可 -SET GLOBAL PARALLEL_SERVERS_TARGET = 64; -``` - -> **说明** -> -> 持续更新中,敬请期待... diff --git a/docs/user_manual/user_best_practices/operation_maintenance/common_parameter.md b/docs/user_manual/user_best_practices/operation_maintenance/common_parameter.md deleted file mode 100644 index 89ad48471..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/common_parameter.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: 常用参数配置 -weight: 1 ---- -# **常用参数配置** - -## **租户关键配置** - -- **min_cpu** - -参数说明:租户最小可用 CPU 数量(所有租户 min_cpu 之和 <= cpu_count) - -推荐值:cpu_count/2 - -社区文档:[https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699430](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699430) - -- **max_cpu** - -参数说明:租户最大可用 CPU 数量(所有租户 max_cpu 之和 <= cpu_count * resource_hard_limit / 100) - -推荐值:cpu_count/2 ~ cpu_count(或者和 min_cpu 设成一样) - -社区文档:[https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699430](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699430) - -- **cpu_quota_concurrency** - -参数说明:租户每个 CPU 可以并发的线程数(max_cpu * cpu_quota_concurrency = 最大业务线程数) - -推荐值:4(一般建议默认值不改,CPU 密集的压测可以改为 2) - -社区文档:[https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699378](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699378) - -- **memory_size** - -参数说明:资源单元能够提供的 Memory 的大小 - -推荐值:总内存的 50%~80%(如果要创建多个资源单元,则需要灵活分配) - -- **log_disk_size** - -参数说明:日志盘大小 - -推荐值:够用就行,比如设置为 40~80GB,如果数据量很大则可以设置更大的数值。 - -- **primary_zone** - -参数说明:主副本分配到 Zone 内的优先级,逗号两侧优先级相同,分号左侧优先级高于右侧。比如 zone1,zone2;zone3 - -推荐值: - -1. 三台机器规格相同且网络延迟相近,可以让 leader 均匀分布,以提高性能:RANDOM。 -2. 如果其中一台性能更好,或者是为了方便统计和观察数据,也可以指定该 Zone,比如:'zone1'。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/_index.md b/docs/user_manual/user_best_practices/operation_maintenance/common_sql/_index.md deleted file mode 100644 index fe5f7658b..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/_index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 常用 SQL -bookCollapseSection: true -weight: 12 ---- diff --git a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/cluster.md b/docs/user_manual/user_best_practices/operation_maintenance/common_sql/cluster.md deleted file mode 100644 index e7b093c06..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/cluster.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: 集群常用SQL -weight: 3 ---- -# **集群状态常用 SQL** - -- 查看集群中各 OBServer 节点状态、启动时间、版本等 - - ```sql - select * from DBA_OB_SERVERS; - ``` - -- 查看各个 Zone 状态、IDC、Region、TYPE 等信息 - - ```sql - select * from DBA_OB_ZONES; - ``` - - ![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/65656351/1684296013159-3f43260f-756c-4d58-bee5-d2d8cde8327e.png#clientId=uab26b267-0ee0-4&from=paste&height=136&id=u271a5307&originHeight=272&originWidth=1526&originalType=binary&ratio=2&rotation=0&showTitle=false&size=61376&status=done&style=none&taskId=uacdcf21f-4801-47a4-b253-da789f62cf3&title=&width=763) - -- 查看数据库版本 - - ```sql - show variables like 'version_comment'; - ``` - - ![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/65656351/1684295609226-259e79a9-ef3b-451f-9087-a460103e174a.png#clientId=uab26b267-0ee0-4&from=paste&height=104&id=u4ad3eb19&originHeight=208&originWidth=1890&originalType=binary&ratio=2&rotation=0&showTitle=false&size=34240&status=done&style=none&taskId=ubbeb30c4-8673-40fa-b428-f9f7acf3d8c&title=&width=945) - -- 查看 RootService 主节点 - - ```sql - select svr_ip as RootService from DBA_OB_SERVERS where with_rootserver='yes'; - ``` diff --git a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/cluster_resource.md b/docs/user_manual/user_best_practices/operation_maintenance/common_sql/cluster_resource.md deleted file mode 100644 index 501709dbd..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/cluster_resource.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: 集群资源常用SQL -weight: 4 ---- -# **集群资源常用 SQL** - -本文介绍查询集群资源的常用 SQL,主要用于查询集群当前分配情况,以及各租户资源使用情况。 - -- 查看 OceanBase 集群 CPU 分配总量 - - ```sql - show parameters where name="cpu_count"; - ``` - -- 查看 OceanBase 集群内存分配总量 - - ```sql - show parameters where name in ('memory_limit','memory_limit_percentage','system_memory'); - ``` - -- 查看 OceanBase 集群数据和日志分配总量 - - ```sql - show parameters where name in ('log_disk_size','log_disk_percentage','datafile_size','datafile_disk_percentage'); - ``` - -- 查看 OceanBase 集群 CPU、内存、CLOG、DATA 等总量和分配情况 - - ```sql - select zone,concat(SVR_IP,':',SVR_PORT) observer, - cpu_capacity_max cpu_total,cpu_assigned_max cpu_assigned, - cpu_capacity-cpu_assigned_max as cpu_free, - round(memory_limit/1024/1024/1024,2) as memory_total, - round((memory_limit-mem_capacity)/1024/1024/1024,2) as system_memory, - round(mem_assigned/1024/1024/1024,2) as mem_assigned, - round((mem_capacity-mem_assigned)/1024/1024/1024,2) as memory_free, - round(log_disk_capacity/1024/1024/1024,2) as log_disk_capacity, - round(log_disk_assigned/1024/1024/1024,2) as log_disk_assigned, - round((log_disk_capacity-log_disk_assigned)/1024/1024/1024,2) as log_disk_free, - round((data_disk_capacity/1024/1024/1024),2) as data_disk, - round((data_disk_in_use/1024/1024/1024),2) as data_disk_used, - round((data_disk_capacity-data_disk_in_use)/1024/1024/1024,2) as data_disk_free - from gv$ob_servers; - ``` - - ```sql - select c.tenant_name,b.tenant_id,a.name as unit_config_name, - concat(b.svr_ip,':',b.svr_port) as observer, - b.status,b.resource_pool_id, b.zone, - b.unit_config_id,b.max_cpu,b.min_cpu, - CAST(b.memory_size/1024/1024/1024 as DECIMAL(15,2)) memory_GB, - CAST(b.log_disk_size/1024/1024/1024 as DECIMAL(15,2)) log_disk_size_GB, - b.max_iops,b.min_iops,b.iops_weight - from __all_unit_config a, DBA_OB_UNITS b, DBA_OB_TENANTS c - where a.unit_config_id = b.unit_config_id - and c.tenant_id = b.tenant_id - and b.tenant_id=1; - ``` - -- 查看各租户资源分配情况 - - ```sql - select t1.name resource_pool_name, t2.`name` unit_config_name, - t2.max_cpu, t2.min_cpu, - round(t2.memory_size/1024/1024/1024,2) mem_size_gb, - round(t2.log_disk_size/1024/1024/1024,2) log_disk_size_gb, t2.max_iops, - t2.min_iops, t3.unit_id, t3.zone, concat(t3.svr_ip,':',t3.`svr_port`) observer, - t4.tenant_id, t4.tenant_name - from __all_resource_pool t1 - join __all_unit_config t2 on (t1.unit_config_id=t2.unit_config_id) - join __all_unit t3 on (t1.`resource_pool_id` = t3.`resource_pool_id`) - left join __all_tenant t4 on (t1.tenant_id=t4.tenant_id) - order by t1.`resource_pool_id`, t2.`unit_config_id`, t3.unit_id; - ``` - -- 查看租户磁盘使用细节 - - ```sql - select a.svr_ip,a.svr_port,a.tenant_id,b.tenant_name, - CAST(a.data_disk_in_use/1024/1024/1024 as DECIMAL(15,2)) data_disk_use_G, - CAST(a.log_disk_size/1024/1024/1024 as DECIMAL(15,2)) log_disk_size, - CAST(a.log_disk_in_use/1024/1024/1024 as DECIMAL(15,2)) log_disk_use_G - from __all_virtual_unit a,dba_ob_tenants b - where a.tenant_id=b.tenant_id; - ``` diff --git a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/compaction.md b/docs/user_manual/user_best_practices/operation_maintenance/common_sql/compaction.md deleted file mode 100644 index eec507b18..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/compaction.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: 合并转储相关SQL -weight: 1 ---- -# **合并转储相关 SQL** - -- 查看全局合并是否开启 - - ```sql - SHOW PARAMETERS LIKE 'enable_major_freeze'; - ``` - -- 手动发起所有租户合并 - - ```sql - ALTER SYSTEM MAJOR FREEZE TENANT=ALL; - ``` - -- 手动发起单个租户合并,以租户名为 obtest 为例 - - ```sql - ALTER SYSTEM MAJOR FREEZE TENANT=obtest; - ``` - -- 查看所有租户合并情况 - - ```sql - SELECT * FROM oceanbase.CDB_OB_ZONE_MAJOR_COMPACTION; - ``` - - ![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/65656351/1684814629058-117ce5cb-441f-4d9b-9fc8-1dc089113d79.png#clientId=u0f7d1ad4-4356-4&from=paste&height=441&id=uab8c9c6f&originHeight=882&originWidth=1918&originalType=binary&ratio=2&rotation=0&showTitle=false&size=245583&status=done&style=none&taskId=u651af399-c2f5-4af0-a7f4-9eaf99b57f7&title=&width=959) - -- 查看合并剩余表数量 - - ```sql - select * from GV$OB_COMPACTION_PROGRESS - where UNFINISHED_TABLET_COUNT != 0; - ``` diff --git a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/partitions.md b/docs/user_manual/user_best_practices/operation_maintenance/common_sql/partitions.md deleted file mode 100644 index 8641a4f4e..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/common_sql/partitions.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: 分区相关SQL -weight: 2 ---- -# **分区相关 SQL** - -> **说明** -> -> 本文 SQL 均需在用户租户下执行。 - -- 查看 test 库下 t1 表 Leader 分布 - - ```sql - select * from oceanbase.DBA_OB_TABLE_LOCATIONS - where DATABASE_NAME='test' and TABLE_NAME='t1' - and ROLE='LEADER'; - ``` - -- 计算 test 库下分区个数(包含索引分区) - - ```sql - select count(*) from oceanbase.DBA_OB_TABLE_LOCATIONS - where DATABASE_NAME='test' and ROLE='LEADER'; - ``` - -- 计算 test 库在某个 OBServer 节点上分区个数 - - ```sql - select count(*) from oceanbase.DBA_OB_TABLE_LOCATIONS - where SVR_IP='xxx.xxx.xxx.xxx' - and database_name='test' - and ROLE='LEADER'; - ``` diff --git a/docs/user_manual/user_best_practices/operation_maintenance/connect_tanent.md b/docs/user_manual/user_best_practices/operation_maintenance/connect_tanent.md deleted file mode 100644 index 463adf57e..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/connect_tanent.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: 连接租户 -weight: 6 ---- -# **连接租户** - -OceanBase 数据库开源版仅兼容 MySQL 租户,连接协议兼容 MySQL 5.6。因此使用 MySQL 命令行客户端或者图形化工具理论上也能连接 OceanBase 数据库的租户。此外,OceanBase 数据库也提供专属的命令行客户端工具 OBClient 和图形化客户端工具 ODC。 - -## **客户端连接** - -OceanBase 数据库 MySQL 租户支持传统 MySQL 客户端以及 OBClient 客户端连接,跟传统 MySQL 不一样的地方是用户名的格式。 - -> **说明** -> -> OceanBase 数据库当前版本支持的 MySQL 客户端版本包括 V5.5、V5.6 和 V5.7。 - -连接示例如下: - -```bash -mysql -h xxx.xxx.xxx.xxx -uroot@sys#obdemo -P2883 -p -c -A oceanbase - -obclient -h xxx.xxx.xxx.xxx -uroot@sys#obdemo -P2883 -p -c -A oceanbase -``` - -说明: - -- -h:提供 OceanBase 数据库连接 IP,通常是一个 ODP 地址。 - -- -u:提供租户的连接账户,通过 ODP 连接时格式有四种:用户名@租户名#集群名、集群名:租户名:用户名、集群名-租户名-用户名、集群名.租户名.用户名,推荐使用 用户名@租户名#集群名。如果是直连 OBServer 节点,用户名需去掉集群名。MySQL 租户的管理员用户名默认是 root。 - -- -P:提供 OceanBase 数据库连接端口,通过 ODP 连接时为 listen_port 配置项的值,默认是 2883;通过 OBServer 节点直连时为 mysql_port 配置项的值,默认为 2881。连接端口均可在部署 OceanBase 数据库时自定义。 - -- -p:提供账户密码,为了安全可以不提供,改为在后面提示符下输入,密码文本不可见。 - -- -c:表示在 MySQL 运行环境中不要忽略注释。 - -- -A:表示在 MySQL 连接数据库时不自动获取统计信息。 - -- oceanbase:访问的数据库名,可以改为业务数据库。 - -新创建的业务租户的管理员(root)密码默认为空,需要修改密码。 - -```bash -mysql -h x.x.x.x -uroot@obmysql#obdemo -P2883 -p -c -A oceanbase - -MySQL [oceanbase]> alter user root identified by 'b******t' ; -Query OK, 0 rows affected (0.118 sec) -``` - -## **OceanBase 连接驱动(JDBC)** - -OceanBase 数据库目前支持的应用主要是 Java 和 C/C++ 。 - -- Java 语言 - - - MySQL 官方 JDBC 驱动下载地址:[MySQL Connector/J 5.1.46](https://downloads.mysql.com/archives/c-j/) - - - OceanBase 官方 JDBC 驱动下载地址:[OceanBase-Client](https://help.aliyun.com/document_detail/212815.html) - -- C 语言 - - - 具体驱动说明请参考官网文档:[OceanBase Connector/C 简介](https://www.oceanbase.com/docs/community-connector-c-cn-10000000000017244) - - - 下载地址:[OceanBase Connector/C 下载](https://github.com/oceanbase/obconnector-c) - -## **DBeaver 客户端连接** - -DBeaver 是一款通用的数据库客户端工具,其原理是使用各个数据库提供的 JDBC 驱动连接数据库,支持常见的关系型数据库、非关系型数据库、分布式数据库等等。使用 OceanBase 提供的 JDBC 驱动或者 MySQL 官方驱动,DBeaver 也可以连接 OceanBase 数据库的 MySQL 租户。 - -官方下载地址:https://dbeaver.io/download/ - -DBeaver 连接 OceanBase 数据库时可选择 MySQL 数据库类型,第一次使用会自动下载官方 MySQL 驱动。详细操作可参考 OceanBase 数据库文档 [通过 DBeaver 连接数据库](https://www.oceanbase.com/docs/community-observer-cn-10000000001879671) 一文。 - -## **ODC 客户端连接** - -OceanBase 提供官方图形化客户端工具 OceanBase Developer Center,简称 ODC,是目前对 OceanBase 数据库适配性最好的客户端工具。该工具的详细信息请参考官网文档 [OceanBase 开发者中心](https://www.oceanbase.com/docs/enterprise-odc-doc-cn-10000000000833893) 。 - -ODC 下载地址:[下载客户端版 ODC](https://help.aliyun.com/document_detail/212816.html?spm=a2c4g.11186623.6.848.2cb5535fzdJK9X) 。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/connection_management.md b/docs/user_manual/user_best_practices/operation_maintenance/connection_management.md deleted file mode 100644 index fbaa2d7e3..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/connection_management.md +++ /dev/null @@ -1,654 +0,0 @@ ---- -title: 连接管理 -weight: 16 ---- - -# **连接管理** - -连接 OceanBase 集群有两种方法: - -- 直连 OceanBase 集群节点。任意一个 OBServer 节点,只要没有脱离集群(掉线),都可以用来连接 OceanBase 集群。业务连接 OceanBase 集群不适合用这个方法,因为节点比较多,且可能会变化。 - -- 通过 OBProxy 连接 OceanBase 集群。OBProxy 和 OceanBase 集群保持联系,能感知集群节点状态变化,您可以使用多个 OBProxy 连接 OceanBase 集群。OBProxy 之间彼此独立工作,互不影响。 - -## **OBProxy 连接原理** - -OBProxy 是个单进程程序,默认监听端口 2883,实现了 MySQL 连接协议。客户端和 OBProxy 建立连接后,访问 OceanBase 集群数据时,OBProxy 会自动判断要访问的数据的主副本在哪个 OBServer 节点上,然后自动和该 OBServer 建立一个长连接。 - -通常我们把客户端和 OBProxy 建立的连接称为前端连接,把相应的 OBProxy 和后端 OBServer 节点之间的连接称之为后端连接。每个前端连接可能对应 1-N 个后端连接。N 是 OceanBase 租户所在的节点总数(包括备副本所在 OBServer 节点)。不同前端连接对应的后端连接是不复用的。 - -OBProxy 前端连接数受 OBProxy 参数 max_connections 和 client_max_connections 限制。 - -```sql -MySQL [(none)]> show proxyconfig like '%connection%'; -+-----------------------------------------+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------------+ -| name | value | info | need_reboot | visible_level | -+-----------------------------------------+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------------+ -| max_connections | 60000 | max fd proxy could use | false | SYS | -| client_max_connections | 8192 | client max connections for one obproxy, [0, 65535] | false | USER | -| enable_client_connection_lru_disconnect | False | if client connections reach throttle, true is that new connection will be accepted, and eliminate lru client connection, false is that new connection will disconnect, and err packet will be returned | false | SYS | -+-----------------------------------------+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------------+ -3 rows in set (0.004 sec) - -MySQL [(none)]> alter proxyconfig set client_max_connections=20000; -Query OK, 0 rows affected (0.005 sec) -``` - -## **如何管理连接** - -### **直连 OBProxy 管理连接** - -- 查看并直接 KILL 前端连接 - - `show processlist;` 命令只能查看当前客户端连接,`show proxysession;` 命令只能查看当前 OBProxy 的连接。 - - ```sql - MySQL [(none)]> show processlist; - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | Id | Tenant | User | Host | db | trans_count | svr_session_count | state | tid | pid | - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | 5 | proxysys | root | x.x.x.x:32108 | NULL | 0 | 0 | MCS_ACTIVE_READER | 49646 | 49646 | - | 524299 | sys | root | x.x.x.x:32082 | oceanbase | 0 | 1 | MCS_ACTIVE_READER | 49651 | 49646 | - | 1048583 | obmysql | root | x.x.x.x:32120 | sysbenchdb | 0 | 2 | MCS_ACTIVE_READER | 49652 | 49646 | - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - 3 rows in set (0.007 sec) - - MySQL [(none)]> show proxysession; - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | proxy_sessid | Id | Cluster | Tenant | User | Host | db | trans_count | svr_session_count | state | tid | pid | - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | 0 | 5 | obce-3zones | proxysys | root | x.x.x.x:32108 | NULL | 0 | 0 | MCS_ACTIVE_READER | 49646 | 49646 | - | 15 | 524299 | obce-3zones | sys | root | x.x.x.x:32082 | oceanbase | 0 | 1 | MCS_ACTIVE_READER | 49651 | 49646 | - | 19 | 1048583 | obce-3zones | obmysql | root | x.x.x.x:32120 | sysbenchdb | 0 | 2 | MCS_ACTIVE_READER | 49652 | 49646 | - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - 3 rows in set (0.005 sec) - - MySQL [(none)]> kill proxysession 1048583; - Query OK, 0 rows affected (0.009 sec) - - MySQL [(none)]> show processlist; - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | Id | Tenant | User | Host | db | trans_count | svr_session_count | state | tid | pid | - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | 5 | proxysys | root | x.x.x.x:32108 | NULL | 0 | 0 | MCS_ACTIVE_READER | 49646 | 49646 | - | 524299 | sys | root | x.x.x.x:32082 | oceanbase | 0 | 1 | MCS_ACTIVE_READER | 49651 | 49646 | - | 1048584 | obmysql | root | x.x.x.x:32124 | sysbenchdb | 0 | 2 | MCS_ACTIVE_READER | 49652 | 49646 | - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - 3 rows in set (0.046 sec) - - MySQL [(none)]> show proxysession; - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | proxy_sessid | Id | Cluster | Tenant | User | Host | db | trans_count | svr_session_count | state | tid | pid | - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | 0 | 5 | obce-3zones | proxysys | root | x.x.x.x:32108 | NULL | 0 | 0 | MCS_ACTIVE_READER | 49646 | 49646 | - | 15 | 524299 | obce-3zones | sys | root | x.x.x.x:32082 | oceanbase | 0 | 1 | MCS_ACTIVE_READER | 49651 | 49646 | - | 20 | 1048584 | obce-3zones | obmysql | root | x.x.x.x:32124 | sysbenchdb | 0 | 2 | MCS_ACTIVE_READER | 49652 | 49646 | - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - 3 rows in set (0.045 sec) - - MySQL [(none)]> kill 1048584; - Query OK, 0 rows affected (0.030 sec) - ``` - - 从示例中可以看出,`show proxysession;` 命令输出里的 ID 就是 `show processlist;` 命令输出中的 ID,可以使用 `kill [id];` 或 `kill proxysession [id];` 命令杀前端连接。当前端连接被杀后,对应的后端连接也一并中断。不支持 kill query 语法。 - -- 查看并直接 KILL 后端连接 - - ```sql - MySQL [(none)]> show processlist; - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | Id | Tenant | User | Host | db | trans_count | svr_session_count | state | tid | pid | - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | 5 | proxysys | root | x.x.x.x:32108 | NULL | 0 | 0 | MCS_ACTIVE_READER | 49646 | 49646 | - | 524299 | sys | root | x.x.x.x:32082 | oceanbase | 0 | 1 | MCS_ACTIVE_READER | 49651 | 49646 | - | 1048585 | obmysql | root | x.x.x.x:32130 | sysbenchdb | 0 | 3 | MCS_ACTIVE_READER | 49652 | 49646 | - +---------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - 3 rows in set (0.074 sec) - - MySQL [(none)]> show proxysession; - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | proxy_sessid | Id | Cluster | Tenant | User | Host | db | trans_count | svr_session_count | state | tid | pid | - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - | 0 | 5 | obce-3zones | proxysys | root | x.x.x.x:32108 | NULL | 0 | 0 | MCS_ACTIVE_READER | 49646 | 49646 | - | 15 | 524299 | obce-3zones | sys | root | x.x.x.x:32082 | oceanbase | 0 | 1 | MCS_ACTIVE_READER | 49651 | 49646 | - | 21 | 1048585 | obce-3zones | obmysql | root | x.x.x.x:32130 | sysbenchdb | 0 | 3 | MCS_ACTIVE_READER | 49652 | 49646 | - +--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ - 3 rows in set (0.200 sec) - - MySQL [(none)]> show proxysession attribute 1048585; - +----------------------------------+---------------------+----------------+ - | attribute_name | value | info | - +----------------------------------+---------------------+----------------+ - | proxy_sessid | 21 | cs common | - | cs_id | 1048585 | cs common | - | cluster | obce-3zones | cs common | - | tenant | obmysql | cs common | - | user | root | cs common | - | host_ip | x.x.x.x | cs common | - | host_port | 32130 | cs common | - | db | sysbenchdb | cs common | - | total_trans_cnt | 0 | cs common | - | svr_session_cnt | 3 | cs common | - | active | false | cs common | - | read_state | MCS_ACTIVE_READER | cs common | - | tid | 49652 | cs common | - | pid | 49646 | cs common | - | idc_name | | cs common | - | modified_time | 0 | cs stat | - | reported_time | 0 | cs stat | - | hot_sys_var_version | 1 | cs var version | - | sys_var_version | 3 | cs var version | - | user_var_version | 0 | cs var version | - | last_insert_id_version | 0 | cs var version | - | db_name_version | 1 | cs var version | - | server_ip | x.x.x.x | last used ss | - | server_port | 2881 | last used ss | - | server_sessid | 3221634194 | last used ss | - | ss_id | 34 | last used ss | - | state | MSS_KA_CLIENT_SLAVE | last used ss | - | transact_count | 2 | last used ss | - | server_trans_stat | 0 | last used ss | - | hot_sys_var_version | 1 | last used ss | - | sys_var_version | 3 | last used ss | - | user_var_version | 0 | last used ss | - | last_insert_id_version | 0 | last used ss | - | db_name_version | 1 | last used ss | - | is_checksum_supported | 1 | last used ss | - | is_safe_read_weak_supported | 0 | last used ss | - | is_checksum_switch_supported | 1 | last used ss | - | checksum_switch | 1 | last used ss | - | enable_extra_ok_packet_for_stats | 1 | last used ss | - | server_ip | x.x.x. | ss pool [0] | - | server_port | 2881 | ss pool [0] | - | server_sessid | 3222242117 | ss pool [0] | - | ss_id | 36 | ss pool [0] | - | state | MSS_KA_SHARED | ss pool [0] | - | transact_count | 2 | ss pool [0] | - | server_trans_stat | 0 | ss pool [0] | - | hot_sys_var_version | 1 | ss pool [0] | - | sys_var_version | 2 | ss pool [0] | - | user_var_version | 0 | ss pool [0] | - | last_insert_id_version | 0 | ss pool [0] | - | db_name_version | 1 | ss pool [0] | - | is_checksum_supported | 1 | ss pool [0] | - | is_safe_read_weak_supported | 0 | ss pool [0] | - | is_checksum_switch_supported | 1 | ss pool [0] | - | checksum_switch | 1 | ss pool [0] | - | enable_extra_ok_packet_for_stats | 1 | ss pool [0] | - | server_ip | x.x.x.x | ss pool [1] | - | server_port | 2881 | ss pool [1] | - | server_sessid | 3221895002 | ss pool [1] | - | ss_id | 35 | ss pool [1] | - | state | MSS_KA_SHARED | ss pool [1] | - | transact_count | 1 | ss pool [1] | - | server_trans_stat | 0 | ss pool [1] | - | hot_sys_var_version | 1 | ss pool [1] | - | sys_var_version | 2 | ss pool [1] | - | user_var_version | 0 | ss pool [1] | - | last_insert_id_version | 0 | ss pool [1] | - | db_name_version | 1 | ss pool [1] | - | is_checksum_supported | 1 | ss pool [1] | - | is_safe_read_weak_supported | 0 | ss pool [1] | - | is_checksum_switch_supported | 1 | ss pool [1] | - | checksum_switch | 1 | ss pool [1] | - | enable_extra_ok_packet_for_stats | 1 | ss pool [1] | - +----------------------------------+---------------------+----------------+ - 73 rows in set (0.081 sec) - ``` - - 从示例中可以看出,通过 `show proxysession attribute [id];` 命令可以查看前端连接对应的后端连接。通过指定前端连接标识和后端连接标识,可以针对性的杀后端连接。 - - - `{proxy_sessid、cs_id、host_ip、host_port}` 为前端连接元组。其中 cs_id 是 OBProxy 内部标识的前端连接(客户端连接)的 ID 号,跟 show processlist 的 ID 列一致。 - - - `{server_ip、server_port、server_sessid、ss_id }` 组成后端连接元组。其中 ss_id 是 OBProxy 内部标识的后端连接(OBProxy 跟 OBServer 连接)的 ID 号。server_sessid 是 OBServer 上的客户端连接 ID。 - -### **通过 OBProxy 连接 OceanBase 集群管理连接** - -使用 OBProxy 连接 OceanBase 集群。 - -```bash -obclient -h x.x.x.x -uroot@sys#obce-3zones -P2883 -p****** -c -A oceanbase -``` - -通过 OBProxy 连接时,`show processlist;` 和 `show proxysession;` 命令只能查看当前 OBProxy 的客户端连接,并且 `show proxysession;` 命令能看到其他 OceanBase 集群的连接(如果这个 OBProxy 还可以为其他集群提供路由服务),`show full processlist;` 命令能看到 OceanBase 集群的全部后端连接,包括通过其他 OBProxy 的连接。 - -在这个连接里可以针对性的 KILL 当前 OBProxy 的后端连接(不能 KILL 其他 OBProxy 的后端连接),但是前端连接只能 KILL 自己,不能 KILL 其他连接。支持 KILL CONNECTION 和 KILL QUERY。 - -```sql -MySQL [oceanbase]> show proxysession; -+--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ -| proxy_sessid | Id | Cluster | Tenant | User | Host | db | trans_count | svr_session_count | state | tid | pid | -+--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ -| 23 | 524301 | obce-3zones | sys | root | x.x.x.x:32214 | oceanbase | 0 | 1 | MCS_ACTIVE_READER | 49651 | 49646 | -| 21 | 1048585 | obce-3zones | obmysql | root | x.x.x.x:32130 | sysbenchdb | 0 | 3 | MCS_ACTIVE_READER | 49652 | 49646 | -| 0 | 6 | obce-3zones | proxysys | root | x.x.x.x:32192 | NULL | 0 | 0 | MCS_ACTIVE_READER | 49646 | 49646 | -+--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ -3 rows in set (0.001 sec) - -MySQL [oceanbase]> show full processlist; -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+ -| Id | User | Tenant | Host | db | Command | Time | State | Info | Ip | Port | Proxy_sessid | -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+ -| 3222242175 | root | obmysql | x.x.x.x:12714 | sysbenchdb | Sleep | 204 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -| 3222242191 | root | sys | x.x.x.x:12736 | oceanbase | Query | 0 | ACTIVE | show full processlist | x.x.x.x | 2881 | 23 | -| 3221895066 | root | obmysql | x.x.x.x:4454 | sysbenchdb | Sleep | 200 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -| 3221611294 | proxyro | sys | x.x.x.x:55222 | oceanbase | Sleep | 13 | SLEEP | NULL | x.x.x.x | 2881 | 3 | -| 3221634194 | root | obmysql | x.x.x.x:55346 | sysbenchdb | Sleep | 219 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+ -5 rows in set (0.026 sec) - -MySQL [oceanbase]> kill 3222242191; -ERROR 1317 (70100): Query execution was interrupted -MySQL [oceanbase]> show full processlist; -ERROR 2013 (HY000): Lost connection to MySQL server during query -MySQL [oceanbase]> show full processlist; -ERROR 2006 (HY000): MySQL server has gone away -No connection. Trying to reconnect... -Connection id: 524302 -Current database: oceanbase - -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+ -| Id | User | Tenant | Host | db | Command | Time | State | Info | Ip | Port | Proxy_sessid | -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+ -| 3221659683 | root | sys | x.x.x.x:55448 | oceanbase | Query | 0 | ACTIVE | show full processlist | x.x.x.x | 2881 | 24 | -| 3221611294 | proxyro | sys | x.x.x.x:55222 | oceanbase | Sleep | 5 | SLEEP | NULL | x.x.x.x | 2881 | 3 | -| 3221634194 | root | obmysql | x.x.x.x:55346 | sysbenchdb | Sleep | 231 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -| 3221895066 | root | obmysql | x.x.x.x:4454 | sysbenchdb | Sleep | 211 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -| 3222242175 | root | obmysql | x.x.x.x:12714 | sysbenchdb | Sleep | 216 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+1048585 -5 rows in set (0.060 sec) - -MySQL [oceanbase]> kill 3221634194; -ERROR 1094 (HY000): sqln thread id: 3221634194 -``` - -如果要 KILL 特定后端连接,需先通过后端 ID 找到 proxysess_id,然后找到对应的前端连接 ID,再通过命令 `show proxysession attribute [id];` 命令找到后端连接的 ss_id。 - -```sql -MySQL [oceanbase]> show proxysession; -+--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ -| proxy_sessid | Id | Cluster | Tenant | User | Host | db | trans_count | svr_session_count | state | tid | pid | -+--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ -| 24 | 524302 | obce-3zones | sys | root | x.x.x.x:32218 | oceanbase | 0 | 1 | MCS_ACTIVE_READER | 49651 | 49646 | -| 21 | 1048585 | obce-3zones | obmysql | root | x.x.x.x:32130 | sysbenchdb | 0 | 3 | MCS_ACTIVE_READER | 49652 | 49646 | -| 0 | 6 | obce-3zones | proxysys | root | x.x.x.x:32192 | NULL | 0 | 0 | MCS_ACTIVE_READER | 49646 | 49646 | -+--------------+---------+-------------+----------+------+---------------+------------+-------------+-------------------+-------------------+-------+-------+ -3 rows in set (0.019 sec) - -MySQL [oceanbase]> show proxysession attribute 1048585; -+----------------------------------+---------------------+----------------+ -| attribute_name | value | info | -+----------------------------------+---------------------+----------------+ -| proxy_sessid | 21 | cs common | -| cs_id | 1048585 | cs common | -| cluster | obce-3zones | cs common | -| tenant | obmysql | cs common | -| user | root | cs common | -| host_ip | x.x.x.x | cs common | -| host_port | 32130 | cs common | -| db | sysbenchdb | cs common | -| total_trans_cnt | 0 | cs common | -| svr_session_cnt | 3 | cs common | -| active | false | cs common | -| read_state | MCS_ACTIVE_READER | cs common | -| tid | 49652 | cs common | -| pid | 49646 | cs common | -| idc_name | | cs common | -| modified_time | 0 | cs stat | -| reported_time | 0 | cs stat | -| hot_sys_var_version | 1 | cs var version | -| sys_var_version | 3 | cs var version | -| user_var_version | 0 | cs var version | -| last_insert_id_version | 0 | cs var version | -| db_name_version | 1 | cs var version | -| server_ip | x.x.x.x | last used ss | -| server_port | 2881 | last used ss | -| server_sessid | 3221895066 | last used ss | -| ss_id | 38 | last used ss | -| state | MSS_KA_CLIENT_SLAVE | last used ss | -| transact_count | 1 | last used ss | -| server_trans_stat | 0 | last used ss | -| hot_sys_var_version | 1 | last used ss | -| sys_var_version | 3 | last used ss | -| user_var_version | 0 | last used ss | -| last_insert_id_version | 0 | last used ss | -| db_name_version | 1 | last used ss | -| is_checksum_supported | 1 | last used ss | -| is_safe_read_weak_supported | 0 | last used ss | -| is_checksum_switch_supported | 1 | last used ss | -| checksum_switch | 1 | last used ss | -| enable_extra_ok_packet_for_stats | 1 | last used ss | -| server_ip | x.x.x.x | ss pool [0] | -| server_port | 2881 | ss pool [0] | -| server_sessid | 3222242175 | ss pool [0] | -| ss_id | 37 | ss pool [0] | -| state | MSS_KA_SHARED | ss pool [0] | -| transact_count | 2 | ss pool [0] | -| server_trans_stat | 0 | ss pool [0] | -| hot_sys_var_version | 1 | ss pool [0] | -| sys_var_version | 3 | ss pool [0] | -| user_var_version | 0 | ss pool [0] | -| last_insert_id_version | 0 | ss pool [0] | -| db_name_version | 1 | ss pool [0] | -| is_checksum_supported | 1 | ss pool [0] | -| is_safe_read_weak_supported | 0 | ss pool [0] | -| is_checksum_switch_supported | 1 | ss pool [0] | -| checksum_switch | 1 | ss pool [0] | -| enable_extra_ok_packet_for_stats | 1 | ss pool [0] | -| server_ip | x.x.x.x | ss pool [1] | -| server_port | 2881 | ss pool [1] | -| server_sessid | 3221634194 | ss pool [1] | -| ss_id | 34 | ss pool [1] | -| state | MSS_KA_SHARED | ss pool [1] | -| transact_count | 3 | ss pool [1] | -| server_trans_stat | 0 | ss pool [1] | -| hot_sys_var_version | 1 | ss pool [1] | -| sys_var_version | 3 | ss pool [1] | -| user_var_version | 0 | ss pool [1] | -| last_insert_id_version | 0 | ss pool [1] | -| db_name_version | 1 | ss pool [1] | -| is_checksum_supported | 1 | ss pool [1] | -| is_safe_read_weak_supported | 0 | ss pool [1] | -| is_checksum_switch_supported | 1 | ss pool [1] | -| checksum_switch | 1 | ss pool [1] | -| enable_extra_ok_packet_for_stats | 1 | ss pool [1] | -+----------------------------------+---------------------+----------------+ -73 rows in set (0.001 sec) - -MySQL [oceanbase]> kill proxysession 1048585 34 ; -Query OK, 0 rows affected (0.010 sec) -``` - -> **说明** -> -> 客户端感知不到后端连接被 KILL。再次查询时 OBProxy 会自动建立新的后端连接。 - -### **直连 OBServer 管理连接** - -在 OBProxy 的连接里查看后端连接的命令是 `show full processlist;`,输出的 Id 列就是 OBServer 里客户端连接 ID。可以在 OBServer 内部直接使用 KILL QUERY 或 KILL CONNECTION。 - -```sql -obclient -hx.x.x.x -uroot@sys#obce-3zones -P2883 -p****** -c -A oceanbase - -MySQL [oceanbase]> show full processlist; -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+ -| Id | User | Tenant | Host | db | Command | Time | State | Info | Ip | Port | Proxy_sessid | -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+ -| 3221659683 | root | sys | x.x.x.x:55448 | oceanbase | Query | 0 | ACTIVE | show full processlist | x.x.x.x | 2881 | 24 | -| 3221611294 | proxyro | sys | x.x.x.x:55222 | oceanbase | Sleep | 16 | SLEEP | NULL | x.x.x.x | 2881 | 3 | -| 3221666993 | root | obmysql | x.x.x.x:45726 | sysbenchdb | Sleep | 3 | SLEEP | NULL | x.x.x.x | 2881 | NULL | -| 3221663293 | root | obmysql | x.x.x.x:55458 | sysbenchdb | Sleep | 281 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -| 3221895066 | root | obmysql | x.x.x.x:4454 | sysbenchdb | Sleep | 291 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -| 3222242175 | root | obmysql | x.x.x.x:12714 | sysbenchdb | Sleep | 285 | SLEEP | NULL | x.x.x.x | 2881 | 21 | -+------------+---------+---------+---------------+------------+---------+------+--------+-----------------------+---------+------+--------------+ -6 rows in set (0.010 sec) -``` - -各列信息介绍如下。 - -- Id:后端连接的客户端 ID。整个 OceanBase 集群内部唯一。 -- Host:前端连接的客户端。IP:PORT 发起数据库连接的客户端信息。 -- Ip:后端连接的服务端 IP。即 OBServer 节点的 IP。 -- Proxy_sessid:前端连接的 OBProxy 内部 ID,该 ID 在 OBProxy 内部唯一,不同 OBProxy 的 Proxy_sessid 会重复。如果为 NULL,表示是直连 OBServer 节点建立的连接。 - -如果您直连到 OBServer,执行以上命令,查询结果会有点不一样。 - -```sql -mysql -h x.x.x.x -P2881 -uroot@obmysql -p****** -c -A sysbenchdb - -mysql> show processlist; -+------------+------+---------------+------------+---------+------+--------+------------------+ -| Id | User | Host | db | Command | Time | State | Info | -+------------+------+---------------+------------+---------+------+--------+------------------+ -| 3222242175 | root | x.x.x.x:12714 | sysbenchdb | Sleep | 312 | SLEEP | NULL | -| 3221895066 | root | x.x.x.x:4454 | sysbenchdb | Sleep | 318 | SLEEP | NULL | -| 3221666993 | root | x.x.x.x:4572 | sysbenchdb | Query | 0 | ACTIVE | show processlist | -| 3221663293 | root | x.x.x.x:55458 | sysbenchdb | Sleep | 308 | SLEEP | NULL | -+------------+------+---------------+------------+---------+------+--------+------------------+ -4 rows in set (0.05 sec) -``` - -Host 列为客户端连接信息(IP:PORT),通常为 OBProxy 地址或者发起连接的客户端。 - -```sql -mysql> show full processlist; -+------------+------+---------+------------------+------------+---------+------+--------+-----------------------+---------+------+ -| Id | User | Tenant | Host | db | Command | Time | State | Info | Ip | Port | -+------------+------+---------+------------------+------------+---------+------+--------+-----------------------+---------+------+ -| 3221666993 | root | obmysql | 127.0.0.1:45726 | sysbenchdb | Query | 0 | ACTIVE | show full processlist | x.x.x.x | 2881 | -| 3221663293 | root | obmysql | x.x.x.x:55458 | sysbenchdb | Sleep | 312 | SLEEP | NULL | x.x.x.x | 2881 | -| 3221895066 | root | obmysql | x.x.x.x:4454 | sysbenchdb | Sleep | 321 | SLEEP | NULL | x.x.x.x | 2881 | -| 3222242175 | root | obmysql | x.x.x.x:12714 | sysbenchdb | Sleep | 315 | SLEEP | NULL | x.x.x.x | 2881 | -+------------+------+---------+------------------+------------+---------+------+--------+-----------------------+---------+------+ -4 rows in set (0.00 sec) - -mysql> show proxysession ; -ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your OceanBase version for the right syntax to use near 'proxysession' at line 1 -``` - -各列信息介绍如下。 - -- Host:前端连接的客户端,即发起连接的客户端信息(IP:PORT)。通常为 OBProxy 地址或者发起连接的客户端。 -- Ip:后端连接的服务端地址,通常为 OBServer 地址。 -- Id:后端连接的 ID 标识,整个 OceanBase 集群内部唯一。可以直接 KILL QUERY 或 KILL CONNECTION。 - -直连 OBServer 不支持 OBProxy 的管理命令。 - -```sql -mysql> kill 3221663293; -Query OK, 0 rows affected (0.02 sec) - -mysql> kill 3221895066; -Query OK, 0 rows affected (0.14 sec) - -mysql> kill 3222242175; -Query OK, 0 rows affected (0.02 sec) -``` - -跟前面一样,如果后端连接被 KILL,客户端重新查询时,OBProxy 将自动和 OBServer 建立后端连接。 - -## **视图 \_\_all_virtual_processlist** - -视图 \_\_all_virtual_processlist 能查看到 OceanBase 集群的所有连接,包括通过 OBProxy 连接的后端连接以及直连集群 OBServer 节点的连接。 - -```sql -SELECT id, host, command, sql_id, time, state, info, svr_ip, proxy_sessid, user_client_ip, trans_id ,thread_id, trace_id -FROM __all_virtual_processlist -where 1=1 -; -``` - -输出如下所示。 - -```sql -+------------+---------------------+---------+----------------------------------+------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+--------------+----------------+----------------------+-----------+-------------------------------+ -| id | host | command | sql_id | time | state | info | svr_ip | proxy_sessid | user_client_ip | trans_id | thread_id | trace_id | -+------------+---------------------+---------+----------------------------------+------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+--------------+----------------+----------------------+-----------+-------------------------------+ -| 3222242518 | x.x.x.x :19440 | Query | 17115A49B6D58958854D9B2E58CB821A | 0 | ACTIVE | SELECT id, host, command, sql_id, time, state, info, svr_ip, proxy_sessid, user_client_ip, trans_id ,thread_id, trace_id -FROM __all_virtual_processlist -where 1=1 | x.x.x.x | 26 | x.x.x.x | 0 | 19240 | YB42AC14F933-0005CCDFC90D9A46 | -| 3221603549 | 127.0.0.1:52488 | Sleep | | 324 | SLEEP | NULL | x.x.x.x | NULL | 127.0.0.1 | 0 | 0 | NULL | -+------------+---------------------+---------+----------------------------------+------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+--------------+----------------+----------------------+-----------+-------------------------------+ -11 rows in set (0.011 sec) -``` - -这个视图的结果跟命令 `show full processlist;` 的结果基本一致。视图说明如下: - -- proxy_sessid:如果为 NULL,表示直连 OBServer 节点的连接;如果不为 NULL,表示通过 OBProxy 建立的连接。 - -- host:连接的客户端。通常是 OBProxy 地址,如果是直连,则为发起连接的实际客户端地址。 - -- svr_ip:连接的服务端。通常是 OBServer 节点地址。 - -- user_client_ip:是连接的实际客户端地址,即发起连接的客户端地址。 - -- state:后端连接的状态,通常状态是 Sleep。如果连接正在执行 SQL,状态是 Active。 - -- time:后端连接当前状态持续时间,单位为秒。如果状态发生变化,就重新计时。 - -- info:连接正在执行的 SQL。如果 SQL 很快,则很难捕捉到。 - -- sql_id:连接正在执行的 SQL 的 SQLID,可以根据 SQLID 去查执行计划。 - -- trans_id:连接的事务的 TRANS_ID,这个不准确,仅供参考。如果连接开启了事务,这个是真正的事务 ID。事务提交之后,在开启新事务之前,连接的 TRANS_ID 不一定会变化。 - -- trace_id:连接使用的 TRACE_ID,可以在 OBServer 的运行日志中根据 TRACE_ID 追踪相关日志。 - -## **分析连接的事务超时问题** - -事务有两个超时时间,分别通过参数 ob_trx_idle_timeout 和 ob_trx_timeout 控制。前者是事务空闲超时,后者是事务未提交超时。 -为了研究这两个参数的影响,以下示例有意把两个参数的值差异拉大。 - -### **事务空闲超时** - -客户端 1 查看空闲超时时间。事务空闲超时设置为 120 秒,事务未提交超时设置为 1000 秒,前者先超时。事务空闲实际超时时间在 [120 s, 120+10 s) 。 - -```sql -set global ob_trx_idle_timeout=120000000; -set global ob_trx_timeout=1000000000; - -show global variables where variable_name in ('ob_trx_idle_timeout','ob_trx_timeout'); -+---------------------+------------+ -| Variable_name | Value | -+---------------------+------------+ -| ob_trx_idle_timeout | 120000000 | -| ob_trx_timeout | 1000000000 | -+---------------------+------------+ -2 rows in set (0.040 sec) -``` - -客户端 2 新建连接开启事务。 - -```sql -MySQL [test]> begin; update t1 set c1=now() where id=3; -Query OK, 0 rows affected (0.002 sec) - -Query OK, 1 row affected (0.023 sec) -Rows matched: 1 Changed: 1 Warnings: 0 - -MySQL [test]> select * from t1; -+----+---------------------+ -| id | c1 | -+----+---------------------+ -| 1 | 2021-10-03 09:22:20 | -| 2 | 2021-09-29 21:16:05 | -| 3 | 2021-10-03 10:41:59 | -| 4 | 2021-09-29 21:16:06 | -| 5 | 2021-09-29 21:16:06 | -| 6 | 2021-09-29 21:16:18 | -| 7 | 2021-10-03 09:35:18 | -| 8 | 2021-09-29 21:16:19 | -+----+---------------------+ -8 rows in set (0.029 sec) -``` - -客户端 2 停止操作一段时间。客户端 1 查看连接状态。 - -```sql -MySQL [oceanbase]> SELECT id, host, command, sql_id, time, state, info, svr_ip, proxy_sessid, user_client_ip, trans_id ,thread_id, trace_id FROM __all_virtual_processlist where 1=1 and id=3222242958 order by id; -+------------+--------------+---------+--------+------+-------+------+----------+--------------+----------------+---------------------+-----------+----------+ -| id | host | command | sql_id | time | state | info | svr_ip | proxy_sessid | user_client_ip | trans_id | thread_id | trace_id | -+------------+---------------+---------+--------+------+-------+------+---------+--------------+----------------+---------------------+-----------+----------+ -| 3222243128 | x.x.x.x:19954 | Sleep | | 121 | SLEEP | NULL | x.x.x.x | 45 | x.x.x.x | 6678987957559799424 | 0 | NULL | -+------------+---------------+---------+--------+------+-------+------+---------+--------------+----------------+---------------------+-----------+----------+ -1 row in set (0.007 sec) -``` - -time 列是连接空闲时间。 - -观察客户端 2 连接。 - -```sql -MySQL [test]> select * from t1; -ERROR 6002 (25000): transaction needs rollback -MySQL [test]> -``` - -可见,OceanBase 3.1 版本后对于事务空闲超时的处理行为变为不中断连接,而是提示事务要回滚。此时不管使用 COMMIT 还是 ROLLBACK ,都可以清理事务状态。 - -示例: - -```sql -MySQL [test]> commit; -ERROR 6002 (40000): Transaction rollbacked -MySQL [test]> select * from t1; -+----+---------------------+ -| id | c1 | -+----+---------------------+ -| 1 | 2021-10-03 09:22:20 | -| 2 | 2021-09-29 21:16:05 | -| 3 | 2021-09-29 21:16:05 | -| 4 | 2021-09-29 21:16:06 | -| 5 | 2021-09-29 21:16:06 | -| 6 | 2021-09-29 21:16:18 | -| 7 | 2021-10-03 09:35:18 | -| 8 | 2021-09-29 21:16:19 | -+----+---------------------+ -8 rows in set (0.003 sec) -``` - -> **注意** -> -> 不管是哪种超时,连接事务所持有的锁都会释放,只是事务的状态需要客户端主动清理一下。对于应用而言,就是需要捕捉事务超时报错,然后发起 ROLLBACK 。 - -### **事务未提交超时** - -客户端 1 查看事务未提交超时时间。 - -未提交超时设置为 100 秒,事务空闲超时设置为 1200 秒。前者先超时,事务未提交实际超时时间会在一个范围内 [100s, 100+10 s) 。 - -```sql -set global ob_trx_idle_timeout=1200000000; -set global ob_trx_timeout=100000000; - -show variables where variable_name in ('ob_trx_idle_timeout','ob_trx_timeout'); -+---------------------+------------+ -| Variable_name | Value | -+---------------------+------------+ -| ob_trx_idle_timeout | 1200000000 | -| ob_trx_timeout | 100000000 | -+---------------------+------------+ -2 rows in set (0.003 sec) -``` - -客户端 2 新建连接开启事务。 - -```sql -MySQL [test]> begin; update t1 set c1=now() where id=3; -Query OK, 0 rows affected (0.001 sec) - -Query OK, 1 row affected (0.005 sec) -Rows matched: 1 Changed: 1 Warnings: 0 -``` - -客户端 2 停止操作一段时间。 - -```sql -MySQL [test]> select * from t1; -ERROR 4012 (25000): Transaction is timeout -``` - -此时连接处于事务超时状态。需要用 COMMIT 或者 ROLLBACK 清除状态。下面示例使用 COMMIT 清除状态,但是事务修改早已经回滚了。 - -```sql -MySQL [test]> commit; -ERROR 4012 (25000): Transaction is timeout -MySQL [test]> select * from t1; -+----+---------------------+ -| id | c1 | -+----+---------------------+ -| 1 | 2021-10-03 09:22:20 | -| 2 | 2021-09-29 21:16:05 | -| 3 | 2021-09-29 21:16:05 | -| 4 | 2021-09-29 21:16:06 | -| 5 | 2021-09-29 21:16:06 | -| 6 | 2021-09-29 21:16:18 | -| 7 | 2021-10-03 09:35:18 | -| 8 | 2021-09-29 21:16:19 | -+----+---------------------+ -8 rows in set (0.014 sec) -``` - -> **注意** -> -> 不管是哪种超时,连接事务所持有的锁都会释放,只是事务的状态需要客户端主动清理。对于应用而言,就是需要捕捉事务超时报错,然后发起 ROLLBACK。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/dic_struct.md b/docs/user_manual/user_best_practices/operation_maintenance/dic_struct.md deleted file mode 100644 index 6603b0e2a..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/dic_struct.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: 安装目录结构 -weight: 1 ---- - - -admin 用户部署的 OBServer 默认安装目录为 /home/admin/oceanbase,OBProxy 默认安装目录为 /home/admin/obproxy。 - -## OB 安装目录结构 - -``` -[root@iZ0jlih98gpa0qilgrps38Z oceanbase]# tree -d -. -├── admin -├── audit -├── bin -├── cgroup -> /sys/fs/cgroup/cpu/oceanbase -├── etc -├── lib -├── log -├── run -├── store -│ └── test_cluster -│ ├── clog -> /data/log1/test_cluster/clog -│ ├── slog -> /data/1/test_cluster/slog -│ └── sstable -> /data/1/test_cluster/sstable -└── wallet -``` - -**admin 目录** - -主要是一些系统包的构建 SQL,包名对应 SQL 文件前缀。 - -**bin 目录** - -- observer:OB 服务启动二进制文件。 -- ob_admin 是 OceanBase 数据库的配套运维工具(ocp 部署集群会自动安装),ob_admin 提供了 slog_tool、log_tool、dumpsst 和 dump_backup 功能,主要用于排查数据不一致、丢数据、错误数据等问题。 -- ob_error 是 OceanBase 数据库的一个错误码解析工具,ob_error 可以根据您输入的错误码返回相对应的原因和解决方案。 - -**etc 目录** - -这个目录主要关注 observer.config.bin。 - -observer.config.bin 是二进制加密文件,所以如果想要查看完整内容需要 strings observer.config.bin,并且不能手动修改文件,只能通过修改变量以及启动时 -o 指定变量修改,否则会导致 observer 起不来。 - -**lib 目录** - -OBServer 依赖的 lib 库。 - -**log** - -observer 运行日志文件目录。 -- observer.log:observer 运行日志,后续问题排障都需要依赖这个日志。 -- rootservice.log:RS 日志,RS 主要负责处理集群管理操作。 -- election.log:选举日志。 -- .wf 日志:每种日志 WARN 级别以上的日志会复制到对应的 wf 日志文件。 -- 带日期的日志:每种日志的归档日志,是否自动回收以及保留数量可以通过 enable_syslog_recycle 和 max_syslog_file_count 来控制。 - -**run 目录** - -服务运行的临时文件,包括 socket 以及 pid 文件。 - -**store 目录** - -对应的是集群的 sstable、slog、clog 做的软连接。 - -- sstable :数据目录 -- slog:一些全局信息变更操作(如新增租户、分区创建、新增 SSTable 等)的 redolog -- clog:事务日志,数据变更产生的日志都在这里 - -## OBProxy 安装目录结构 -``` -[root@iZ0jlih98gpa0qilgrps38Z obproxy]# tree -d -. -├── bin -├── control-config -├── etc -├── lib -├── log -└── sharding-config -``` - -bin、etc、lib 这三个目录跟 OBServer 目录相似。 - -**log 目录** - -- obproxy.log:obproxy 运行日志,后续问题排障都需要依赖这个日志。 -- obproxy_error.log:错误日志,会记录执行错误的请求,包括 ODP 自身错误和 OBServer 返回错误。 -- obproxy_digest.log:审计日志,记录执行时间大于参数 query_digest_time_threshold 阈值(默认 100ms)的请求和错误响应请求。 -- obproxy_slow.log:慢日志,记录执行时间大于 slow_query_time_threshold 阈值(默认 500ms)的请求。 -- obproxy_stat.log:统计日志,默认每分钟(monitor_stat_dump_interval 参数控制)输出一次。通过该日志可以查看 ODP 一分钟内 SQL 的执行情况。 -- obproxy_limit.log:OBProxy 限流日志,如果发生限流,被限流的请求将打印到该日志中。 -- obproxy_xflush.log: 是对 WARN 和 ERROR 日志的详细补充。 -- obproxy_diagnosis.log: 是连接诊断接入监控系统的日志。 - diff --git a/docs/user_manual/user_best_practices/operation_maintenance/export.md b/docs/user_manual/user_best_practices/operation_maintenance/export.md deleted file mode 100644 index 1aece7ea5..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/export.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: 旁路导入 -weight: 8 ---- -# **旁路导入** - -## **简单介绍** - -目前的 insert 语句插入数据会通过 SQL、事务、存储等诸多模块。因为这些模块要考虑的事情非常多,导致执行路径相对较长,为了快速的导入数据,旁路导入跳过了 SQL、事务、MemTable 等模块,直接把数据写入到 SSTable。 - -现在的旁路导入是利用 DDL 来实现的,可以认为是一种类型的 DDL,所以在事务中使用旁路导入会提交开启事务。 -旁路导入过程中不影响转储和合并。 - -## **用法** - -**load data 加 direct(true, 0)hint** - -```sql -load data /*+ parallel(10) direct(true, 0) */ infile '/home/xxx' into table test1 fields terminated by '|' enclosed by '' lines starting by '' terminated by '\n'; -``` - -direct(true/false,0),true 和 false 是设定是否对导入文件排序,hint 中的 0 处是指定一个 max error count,目标是解决导入数据有问题,不会因为一行数据的出错,导致整个导入挂掉。这个 count 数允许少于等于这个 count 的行数出错还能继续导入。如果超过了这个 max error count,那么导入就会挂掉。 - -对重复主键处理三种模式:insert, replace, ignore;insert 表示只要发生重复都算出错;replace 表示有重复主键出现,那么替换掉原有的数据;ignore 表示有重复出现就忽略。replace, ignore 只支持 mysql 租户。 -replace 和 ignore 举例: - -```sql -load data /*+ parallel(10) direct(true, 0) */ infile '/home/mxxx' replace/ignore into table test1 fields terminated by '|' enclosed by '' lines starting by '' terminated by '\n'; -``` - -**旁路导入文件可在 oss 上,具体命令** - -```sql -load data /*+ parallel(1) direct(false,0)*/ remote_oss infile 'oss://xxxxxxx?host=xxxxxxxx&access_id=XXXXX&access_key=XXXXXX' into table test1 fields terminated by '|' enclosed by '' lines starting by '' terminated by '\n'; -``` - -**insert into select** - -```sql -insert /*+ enable_parallel_dml parallel(2) append */ into t7 select * from t8; -``` - -insert into select 要走旁路的话,需要加 append hint。 - -## **控制排序使用内存** - -```sql -set global ob_sql_work_area_percentage = 50; -``` - -表示控制排序能用租户多少内存。 - -## **虚拟表和视图** - -__all_virtual_load_data_stat 原 load data 虚拟表可在此表看旁路导入状态。 - -| **字段** | **说明** | -| --- | --- | -| job_id | 原表的 table id。 | -| job_type | 是否走旁路导入,direct 表明走了旁路导入 | -| store_status | 旁路导入状态,一般是 none ->loading->merging->merged->commit;出错删除任务是 abort。 | -| store_trans_status | 同上 | - -视图:v$session_longops。 - -## **旁路导入注意事项** - -1. 现在 load data 还没有支持 load data local 语法。导致csv数据文件必须放在 observer 端; -2. 小数据量、索引表不适合使用旁路导入,旁路导入需要重新建索引,重构外键,耗时较长; -3. 原表有大数据的表,但是导入数据量小的,不适合使用旁路导入,旁路导入会建个 hidden table,把原表数据和导入数据一起重新写入; -4. 使用旁路导入可能报错 4013(一般不会撞到),大概率是临时文件分配内存失败,可以先看下日志4013报错是不是和临时文件相关,此问题临时文件已经优化;现临时文件那边给出一个内存和并行度的建议值,如果未超过建议值报错 4013 需要找临时文件 RD 排查是否问题,内存和并行度计算方式如下: - - 目前建议最大 parallel=租户内存 *0.01 / 2M,比如 10G* 0.01 / 2M =5,此租户超过 5 并行度,报错 4013 可认为符合预期,另外如果分区数少的话,内部并行度是 parallel* 分区数,分区数多的话,就是 parallel; - -5. 数据放大问题,设计预期加上排序,会是原文件数据大小的两倍; -6. 旁路导入是 ddl 操作,不允许对两个表同时进行旁路导入,导入加表锁,会报错; -7. 旁路导入中,RS 机器重启,再次重试任务需要在 5 分钟后,否则RS未清除上次任务,会报错; -8. 旁路导入和 load data 处理 csv 文件会有些不同,比如文件较表多列或少列,mysql 模式会报错 -1525(预期),所以 load data 导入正常的文件,旁路导入报错,需先检查下数据文件; -9. 一行数据不支持超过 2M; -10. 支持带自增列表的导入; -11. 除了索引和外键,trigger,constraint 等都不支持; -12. 支持lob,但是性能比较差,lob 会走原来的路径; -13. 为了和 mysql 行为一致,csv 文件对应bit类型为数字时,导入 bit 类有问题,比如文件是 8,导入后是 56,符合预期; -14. 开发对旁路导入的 parallel 大小做了限制,上限是租户 cpu 数,限制可以在虚拟表 parallel 列体现。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/hand_deploy_prometheus.md b/docs/user_manual/user_best_practices/operation_maintenance/hand_deploy_prometheus.md deleted file mode 100644 index 2d292b8de..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/hand_deploy_prometheus.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: 通过Prometheus监控数据库(手动) -weight: 10 ---- - -# **手动部署 Prometheus** - -> **说明** -> -> OBD 可以直接部署 OBAgent 和 Prometheus, 详情可以参考 [配置文件](https://github.com/oceanbase/obdeploy/blob/master/example/prometheus/distributed-with-obagent-and-prometheus-example.yaml) - -本文档帮助大家独立部署 obagent 和 prometheus。 - -## **OBAgent 安装部署** - -### **什么是 OBAgent** - -OBAgent 是一个监控采集框架。OBAgent 支持推、拉两种数据采集模式,可以满足不同的应用场景。OBAgent 默认支持的插件包括主机数据采集、OceanBase 数据库指标的采集、监控数据标签处理和 Prometheus 协议的 HTTP 服务。要使 OBAgent 支持其他数据源的采集,或者自定义数据的处理流程,您只需要开发对应的插件即可。 - -### **部署 OBAgent** - -可参照官网,选择合适的部署方式:[部署文档](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001700702) - -#### **需要注意的地方** - -目前集群初始未安装 OBAgent 不会创建 ocp_monitor 用户,而 OBAgent 连接 OceanBase 数据库获取信息正是通过这个用户来的,所以部署 OBAgent 前需要先确认是否已经有了这个用户,如果没有的话需要创建 - -```sql -# 确认是否存在 ocp_monitor -select user,host from mysql.user; - -# 如果不存在则需要手动创建 -GRANT SELECT ON `oceanbase`.* TO 'ocp_monitor'@'%' identified by '******'; -``` - -如果是启动 OBAgent 后续添加的账号,那么需要重启 OBAgent 才能识别。 - -**配置文件** -http_basic_auth_user:后续 prometheus 访问 OBAgent 的账号 -http_basic_auth_password:后续 prometheus 访问 OBAgent 的密码 -monitor_user:默认是 ocp_monitor,修改不生效 -monitor_password:需要填写 ocp_monitor 的密码 - -**验证** -账号密码替换为自己设置的 - -```bash -curl -H "Content-Type: application/json" -X GET --user admin:root http://ip:8088/metrics/ob/basic -curl -H "Content-Type: application/json" -X GET --user admin:root http://ip:8088/metrics/ob/extra -curl -H "Content-Type: application/json" -X GET --user admin:root http://ip:8088/metrics/node/host -``` - -**日志** - -`${ocp.agent.home.path}/log**/\***` - -## Prometheus 安装部署 - -1. 下载 [Prometheus 软件](https://prometheus.io/download/)。 -2. 解压并安装 Prometheus 软件。 - - ```bash - sudo tar zxvf Prometheus-2.30.3.linux-amd64.tar.gz -C /usr/local/ - ``` - -3. 复制 OBAgent 生成的 Prometheus 配置文件到 Prometheus 安装目录中。 - - **说明** - OBAgent 携带了 Prometheus 配置文件的模版,使用 OBD 部署 OBAgent, 会自动填充模版中的内容。该配置文件被放在 OBAgent 安装目录下,如 /home/admin/obagent/conf/ Prometheus_config/。这个配置文件可以供 Prometheus 软件直接使用。OBAgent 的安装部署,请参考 [OBAgent](https://www.oceanbase.com/docs/community-observer-cn-10000000001879804) - - ```bash - sudo mv Prometheus_config/ /usr/local/Prometheus-2.30.3.linux-amd64/ - ``` - -4. 新建 Prometheus 服务文件。 - - ```bash - sudo mkdir /var/lib/Prometheus - sudo vim /etc/systemd/system/Prometheus.service - - [Unit] - Description=Prometheus Server - Documentation=https://Prometheus.io/docs/introduction/overview/ - After=network-online.target - - [Service] - Restart=on-failure - ExecStart=/usr/local/Prometheus-2.30.3.linux-amd64/Prometheus --config.file=/usr/local/Prometheus-2.30.3.linux-amd64/Prometheus_config/Prometheus.yaml --storage.tsdb.path=/var/lib/Prometheus --web.enable-lifecycle --web.external-url=http://x.x.x.x:9090 - - [Install] - WantedBy=multi-user.target - ``` - -5. 启动 Prometheus 服务。 - - ```bash - sudo systemctl daemon-reload - - sudo systemctl start Prometheus - - sudo systemctl status Prometheus - - [admin@**** ~]$ sudo systemctl status Prometheus - ● Prometheus.service - Prometheus Server - Loaded: loaded (/etc/systemd/system/Prometheus.service; disabled; vendor preset: disabled) - Active: active (running) since 一 2023-05-15 11:39:14 CST; 8s ago - Docs: https://Prometheus.io/docs/introduction/overview/ - Main PID: 13003 (prometheus) - Tasks: 13 - Memory: 23.4M - CGroup: /system.slice/Prometheus.service - └─13003 /usr/local/prometheus-2.37.8.linux-amd64/prometheus --config.file=/usr/local/prometheus-2.37.8.linux-amd64/prometheus_config/ prometheus.yaml --storage.tsdb.path=/var/lib/Prometheus --web.enable-lifecycle --we... - ``` - -6. 确认 Prometheus 是否启动成功。 - - ```bash - [admin@**** ~]$ sudo netstat -ntlp | grep 9090 - tcp6 0 0 :::9090 :::* LISTEN 902555/Prometheus - ``` - -## **Prometheus 使用** - -使用浏览器访问:http://172.20.xx.xx:9090/graph - -> **说明** -> -> 此处链接中的 IP 为示例中配置 Prometheus 的服务器 IP,根据实际情况将其转换为自身配置 Prometheus 的服务器 IP。 - -## **Prometheus 配置文件说明** - -```yaml -# OBAgent 的 RPM 包中包含 Prometheus 的配置模版,您可以根据实际情况修改。 -# 要开启基础认证,您需要配置 {http_basic_auth_user} 和 {http_basic_auth_password},要按照 OBAgent 设置的对应账密来。 -# {target} 替换成监控目标主机的 IP 和端口号(默认都是8088), 如果有多个监控目标,需要配置多行,每个监控目标一行。比如 xx.xx.xx.xx:8088 -# rules 目录包含两个报警配置模版,分别是默认的主机和 OceanBase 数据库的报警配置。如需自定义报警项,您可以参考此目录。 - -# 全局配置 -global: - # 抓取间隔 - scrape_interval: 1s - # 评估规则间隔 - evaluation_interval: 10s - -# 报警规则配置 -# Prometheus 将根据这些信息,推送报警信息至 alertmanager。 -rule_files: - - 'rules/*rules.yaml' - -# 抓取配置 -# 用来配置 Prometheus 的数据采集。 -scrape_configs: - - job_name: prometheus - metrics_path: /metrics - scheme: http - static_configs: - - targets: - - 'localhost:9090' - - job_name: node - basic_auth: - username: { http_basic_auth_user } - password: { http_basic_auth_password } - metrics_path: /metrics/node/host - scheme: http - static_configs: - - targets: - - { target } - - job_name: ob_basic - basic_auth: - username: { http_basic_auth_user } - password: { http_basic_auth_password } - metrics_path: /metrics/ob/basic - scheme: http - static_configs: - - targets: - - { target } - - job_name: ob_extra - basic_auth: - username: { http_basic_auth_user } - password: { http_basic_auth_password } - metrics_path: /metrics/ob/extra - scheme: http - static_configs: - - targets: - - { target } - - job_name: agent - basic_auth: - username: { http_basic_auth_user } - password: { http_basic_auth_password } - metrics_path: /metrics/stat - scheme: http - static_configs: - - targets: - - { target } -``` - -## **Grafana 展示** - -### **Dashboard 模版** - -[oceanbase-metrics](https://grafana.com/grafana/dashboards/15215-oceanbase-metrics/) - -[host-metrics](https://grafana.com/grafana/dashboards/15216-host-metrics/) - -[hnew-dashboard-copy](https://grafana.com/grafana/dashboards/15354-new-dashboard-copy/) - -导入 dashboard 模版,并且在 Grafana 配置数据源信息就可以了。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/how_to_find_traceid.md b/docs/user_manual/user_best_practices/operation_maintenance/how_to_find_traceid.md deleted file mode 100644 index 98ade24f9..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/how_to_find_traceid.md +++ /dev/null @@ -1,555 +0,0 @@ ---- -title: 如何快速定位SQL问题 -weight: 14 ---- -# **如何快速定位SQL问题** - -大家在使用数据库的过程中,经常遇到慢sql,或者执行错误的sql,有些sql是很容易判断出来错误,以及sql运行比较慢的原因,但是有些sql就很难判断出来,如果遇到这种情况,我们该怎么处理,怎么判断SQL出错原因,以及是SQL需要优化,数据库本身配置是否设置好等,接下来我就跟大家简单介绍下,如何快速定位SQL问题。在开始之前,我们先来了解下一条SQL,在进入OceanBase数据库中执行时都经历了哪些模块。 -## **访问路径** -首先我们从SQL的访问路径开始介绍,一条SQL从应用发出之后,一般会经过负载均衡(根据情况选择是否需要),然后到OBProxy,然后再到OBServer。大概流程如下图所示: -![image.png](/img/operation_maintenance/how_to_find_traceid/a.png) -OBProxy主要起到一个路由转发的作用,以及快速解析,根据OceanBase数据库中数据副本分布情况,直接将请求快速发送到对应的OBServer上。 -SQL在进入OBServer之后,还会经历数据库内部很多模块,具体如下: -![image.png](/img/operation_maintenance/how_to_find_traceid/b.png) - -SQL的在数据库内部的执行过程,这些模块具体的含义,可以参考官网文档:[SQL 请求执行流程](https://www.oceanbase.com/docs/common-oceanbase-database-1000000000033661),在整个SQL执行过程中,会首先根据SQL的ip、port、自增序列号等信息生成一个全局唯一的trace_id,后续的sql跟踪,我们都使用这个trace_id来对sql进行定位追踪。 - -## **SQL错误** -这种问题一般都比较好处理,大多都是sql写的格式不对,或者库、表、列名等不存在,或者OceanBase语法不支持等,一般情况,根据返回的错误信息,就大致可以判断出错误原因。 -但也有一些很隐蔽的错误,一时很难判断出具体是哪里错了,这个时候就要借助错误日志来帮助我们进行判断了,这里就举一个例子,这是在一个用户那里实实在在遇到的问题,用户的场景比较复杂,拿OceanBase用来做数据的清洗,将多张表进行关联计算,然后将数据写入到结果表中,而在执行其中一条SQL过程中,收到了这样一个报错: -```sql -ERROR 1292 (22007): Incorrect value -``` -当时用户收到这个报错后很茫然,因为用户的SQL长达200多行,并且有多层嵌套子查询和多张表关联查询,所以一时间很难判断出来是哪里出现了错误,SQL大致如下(脱敏后): -```sql -REPLACE INTO abc.table1( - m_id, - st_id, - con_id, - `date`, - t_ord, - t_role, - tag, - con_pri, - con_qu, - con_in, - update_time -) -SELECT - tmp5.m_id, - tmp5.st_id, - tmp5.con_id, - tmp5.`date`, - tmp5.t_ord_96, - tmp5.t_role, - tmp5.tag, - cast(sum(tmp5.con_pri) as decimal(20,6)) as con_pri, - cast(sum(tmp5.con_qu) as decimal(20,6)) as con_qu, - cast(sum(tmp5.con_in) as decimal(20,6)) as con_in, - current_timestamp -FROM - ( - SELECT - tmp4.m_id, - tmp4.st_id, - tmp4.con_id, - tmp4.`date`, - tmp4.t_ord, - dstl2.t_ord_96, - tmp4.t_role, - tmp4.tag, - case - when tmp4.t_ord = dstl2.t_ord_96 then con_pri - else 0 - end as con_pri, - case - when tmp4.t_ord = dstl2.t_ord_96 then con_qu - else 0 - end as con_qu, - case - when tmp4.t_ord = dstl2.t_ord_96 then con_in - else 0 - end as con_in - FROM - ( - SELECT - tmp3.m_id, - tmp3.st_id, - tmp3.con_id, - tmp3.`date`, - tmp3.t_ord_96 as t_ord, - tmp3.t_role, - tmp3.tag, - tmp3.con_pri, - cast(tmp3.con_qu as decimal(20,6)) as con_qu, - cast(tmp3.con_qu * tmp3.con_pri as decimal(20,6)) as con_in - FROM - ( - SELECT - tmp2.m_id, - tmp2.st_id, - tmp2.con_id, - tmp2.`date`, - tmp2.t_ord_96, - case - when tmp2.con_name like '%xxx%' then 1 - when tmp2.con_name like '%x%' then 2 - when tmp2.con_name like '%xx%' then 3 - else - case - when tmp2.contract_type_name like '%xx%' then 4 - else 5 - end - end as tag, - case - when tmp2.con_name like '%xxxxx%' then tmp2.vendee_price - else tmp2.sale_price - end as con_pri, - case - when tmp2.con_name like '%xxxxx%' then tmp2.vendee_energy - else tmp2.sale_energy - end as con_qu, - tmp2.t_role - FROM - ( - select - tmp.m_id, - tmp.st_id, - tmp.st_name, - tmp.member_id, - tmp.con_id, - tmp.con_name, - dd.`date`, - tmp.t_ord_96, - tmp.sale_energy / (datediff(tmp.timepart_endtime, tmp.timepart_starttime) + 1) / tmp.re as sale_energy, - tmp.sale_price, - tmp.vendee_energy / (datediff(tmp.timepart_endtime, tmp.timepart_starttime) + 1) / tmp.re as vendee_energy, - tmp.vendee_price, - tmp.timepart_starttime, - tmp.timepart_endtime, - tmp.seller_member_name, - tmp.contract_type_name, - tmp.t_role - from - ( - select - tmp1.*, - dstl.t_ord_96, - count(1) over(PARTITION by member_id, con_id, time_division_range, timepart_endtime, timepart_starttime) as re - from - ( - select - distinct - spcdp.m_id, - ds.st_id, - ds.st_name, - spcdp.member_id, - spcdp.con_id, - dc.con_name, - spcdp.sale_energy, - spcdp.sale_price, - spcdp.vendee_energy, - spcdp.vendee_price, - spcdp.time_division_code, - spcdp.time_division_name, - case - when dc.con_name like '%xx%' then - case - when substr(date_add(cast(concat('2022-10-01 ', right(spcdp.time_division_range, 5), ':00') as datetime), interval 1 hour), 12, 5) = '00:00' then concat(left(spcdp.time_division_range, 6), '24:00') - else concat(left(spcdp.time_division_range, 6), substr(date_add(cast(concat('2022-10-01 ', right(spcdp.time_division_range, 5), ':00') as datetime), interval 1 hour), 12, 5)) - end - else spcdp.time_division_range - end as time_division_range, - spcdp.timepart_endtime, - spcdp.timepart_starttime, - dc.seller_member_name, - dc.contract_type_name, - case - when locate('x',dc.con_name) > 0 then 1 - when locate('x',dc.con_name) > 0 then 2 - else if(dc.con_qu>0,2,1) - end as t_role - from - (select distinct m_id, member_id, con_id, sale_energy, sale_price, vendee_energy, vendee_price, time_division_code, time_division_name, time_division_range, timepart_endtime, timepart_starttime from - select - DISTINCT - if(spcdp.m_id is null,spcdpa.m_id,spcdp.m_id) as m_id, - if(spcdp.member_id is null,spcdpa.member_id,spcdp.member_id) as member_id, - if(spcdp.con_id is null,spcdpa.con_id,spcdp.con_id) as con_id, - if(spcdp.sale_energy is null,spcdpa.sale_energy,spcdp.sale_energy) as sale_energy, - if(spcdp.sale_price is null,spcdpa.sale_price,spcdp.sale_price) as sale_price, - if(spcdp.vendee_energy is null,spcdpa.vendee_energy,spcdp.vendee_energy) as vendee_energy, - if(spcdp.vendee_price is null,spcdpa.vendee_price,spcdp.vendee_price) as vendee_price, - if(spcdp.time_division_code is null,spcdpa.time_division_code,spcdp.time_division_code) as time_division_code, - if(spcdp.time_division_name is null,spcdpa.time_division_name,spcdp.time_division_name) as time_division_name, - if(spcdp.time_division_range is null,spcdpa.time_division_range,spcdp.time_division_range) as time_division_range, - if(spcdp.timepart_endtime is null,spcdpa.timepart_endtime,spcdp.timepart_endtime) as timepart_endtime, - if(spcdp.timepart_starttime is null,spcdpa.timepart_starttime,spcdp.timepart_starttime) as timepart_starttime, - if(spcdp.creator is null,spcdpa.creator,spcdp.creator) as creator, - if(spcdp.creation_time is null,spcdpa.creation_time,spcdp.creation_time) as creation_time, - if(spcdp.modifier is null,spcdpa.modifier,spcdp.modifier) as modifier, - if(spcdp.modification_time is null,spcdpa.modification_time,spcdp.modification_time) as modification_time - from - ( - select * from ods.table2 where modification_time >= date_format(date_sub(current_date, interval 1 day), '%Y-%m-%d %T') - ) spcdp - full join - ( - select * from ods.table2_xxx where modification_time >= date_format(date_sub(current_date, interval 1 day), '%Y-%m-%d %T') - ) spcdpa - on spcdp.m_id = spcdpa.m_id and spcdp.member_id = spcdpa.member_id and spcdp.con_id = spcdpa.con_id - and spcdp.time_division_code = spcdpa.time_division_code and spcdp.timepart_starttime = spcdpa.timepart_starttime - and spcdp.timepart_endtime = spcdpa.timepart_endtime - ) - where m_id = 'PXBGS' and time_division_range <> '') spcdp - inner join (select * from abc.table3 where service_provider_id = 1) ds on spcdp.member_id = ds.counterparty_code - inner join (select * from abc.table4 where is_deleted <> 1) dc on spcdp.con_id = dc.con_id and ds.st_id = dc.st_id - and date(spcdp.timepart_starttime) between dc.contract_start_date and dc.contract_end_date - ) tmp1 - left join - ( - select - t_ord_96, - CASE - when end_time_96 = '00:00:00' then '24:00' - else left(end_time_96, 5) - END as end_time_96 - from - abc.dim_spot_time_list - ) dstl on dstl.end_time_96 > left(tmp1.time_division_range, 5) and dstl.end_time_96 <= right(tmp1.time_division_range, 5) - ) as tmp - left join abc.dim_date dd on dd.`date` BETWEEN date(tmp.timepart_starttime) and date(timepart_endtime) - ) as tmp2 - ) as tmp3 - where tmp3.t_ord_96 is not null and tmp3.con_id <> 'xxxxxxxxxxxx' - ) as tmp4 - left join abc.dim_spot_time_list dstl2 on 1 = 1 - ) as tmp5 - group by tmp5.m_id, tmp5.st_id, tmp5.con_id, tmp5.`date`, tmp5.t_ord_96, tmp5.t_role, tmp5.tag; - -``` -从这个SQL文本来看,整体还是比较复杂,想快速找到问题根因还是比较困难,这个时候就需要我们根据SQL执行过程中的trace_id,来一步步追踪问题的根因。 -这里先说结论,通过对日志过滤排查,我们发现是字段类型强转,导致转出来的日期字段是无效的,所以报除了这个错误,当时获取到的日志如下(部分截取): - -```markdown -[2023-07-19 14:24:27.757645] WARN [LIB.TIME] str_to_ob_time_with_date (ob_time_convert.cpp:1938) [7526][T1004_TNT_L0_G0][T1004][YB42AC116182-00060043C87B7F7D-0-0] [lt=16] datetime is invalid or out of range(ret=-4219, str=2022-10-01 24:00:00, ob_time={mode_:3, parts:[2022, 10, 1, 24, 0, 0, 0, 0, 0, 0, 0], tz_name:"", tzd_abbr:"", time_zone_id:-1, transition_type_id:-1, is_tz_name_valid:false}, date_sql_mode={allow_invalid_dates:0, no_zero_date:0}, lbt()=0xb553efb 0xb4b501f 0xb4a0a96 0xb4a0731 0x80360d3 0x807ed03 0x8098f84 0x61dc801 0x8098f84 0x804c103 0x8098f84 0x7e3b29f 0x8098f84 0x686dcc7 0x8098f84 0x6669ef4 0x8098f84 0x7373827 0x7373e77 0x74e80f3 0x755c04b 0x756110c 0x7563293 0x737face 0x6eb659d 0x6eb7931 0x6ebf317 0x737face 0x743726d 0x737face 0x69a057d 0x737face 0x69ed74b 0x699f2b4 0x69df421 0x39cbc45 0x7a5d60f 0x7a5cd06 0x7a5b37d 0x7a94139 0x7a48812 0x7a2b8cb 0x7a2a815 0x3bbaed5 0x392f401 0x4602029 0x39277e4 0x46025c7 0xb5380c7 0xb53303a 0x7f1759d17ea5 0x7f1759a40b0d) -[2023-07-19 14:24:27.757663] WARN [LIB.TIME] str_to_datetime (ob_time_convert.cpp:398) [7526][T1004_TNT_L0_G0][T1004][YB42AC116182-00060043C87B7F7D-0-0] [lt=18] failed to convert string to datetime(ret=-4219) -[2023-07-19 14:24:27.757667] WARN [SQL] common_string_datetime (ob_datum_cast.cpp:1104) [7526][T1004_TNT_L0_G0][T1004][YB42AC116182-00060043C87B7F7D-0-0] [lt=4] str_to_datetime failed(ret=-4219, in_str=2022-10-01 24:00:00) -[2023-07-19 14:24:27.757671] WARN [SQL] string_datetime (ob_datum_cast.cpp:2406) [7526][T1004_TNT_L0_G0][T1004][YB42AC116182-00060043C87B7F7D-0-0] [lt=3] fail to exec common_string_datetime(expr, ObString(child_res->len_, child_res->ptr_), ctx, res_datum)(ret=-4219) -[2023-07-19 14:24:27.757673] WARN [SQL] anytype_anytype_explicit (ob_datum_cast.cpp:6263) [7526][T1004_TNT_L0_G0][T1004][YB42AC116182-00060043C87B7F7D-0-0] [lt=3] inner cast failed(ret=-4219) -[2023-07-19 14:24:27.757676] WARN [SQL] eval_param_value (ob_expr.h:944) [7526][T1004_TNT_L0_G0][T1004][YB42AC116182-00060043C87B7F7D-0-0] [lt=2] evaluate parameter failed(ret=-4219, param_index=0) -[2023-07-19 14:24:27.757679] WARN [SQL.ENG] calc_date_adjust (ob_expr_date_add.cpp:144) [7526][T1004_TNT_L0_G0][T1004][YB42AC116182-00060043C87B7F7D-0-0] [lt=2] eval param value failed -[2023-07-19 14:24:27.757682] WARN [SQL] datetime_string (ob_datum_cast.cpp:3524) [7526][T1004_TNT_L0_G0][T1004][YB42AC116182-00060043C87B7F7D-0-0] [lt=2] eval arg failed(ret=-4219, ctx={batch_idx:8, batch_size:136, max_batch_size:256, frames_:0x7f12f57f0f90}) -........ -``` - -从日志的第一行,我们就可以看到datetime is invalid or out of range的报错,在这里就基本可以判断是datetime类型有问题,第二行的提示则更明显:failed to convert string to datetime,所以我们很快就可以定位到是在做string转datetime类型的时候,出现了错误,通过回看SQL可以找到,有两处使用cast函数来做字符串类型转时间类型的转换,然后根据用户提供的表数据,我们对问题进行复现,确信就是这里的错误。SQL中类型转换如下: -```sql -cast(concat('2022-10-01 ', right(spcdp.time_division_range, 5), ':00') as datetime) -``` - -那我们是怎么快速找到这条日志的呢,下面我把问题简化,重新复现报错现场,看下是怎么一步步找到关键日志,发现问题的。具体如下,简单创建两张表: -t1表: -```sql -CREATE TABLE `t1` ( - `a_id` int(11) DEFAULT NULL, - `a_name` varchar(10) DEFAULT NULL, - `a_time` varchar(10) DEFAULT NULL -) -``` -t2表: -```sql -CREATE TABLE `t2` ( - `t_id` int(11) DEFAULT NULL, - `t_name` varchar(10) DEFAULT NULL, - `t_time` datetime DEFAULT NULL -) -``` -其中t2表,是目标写入表,t1表是源数据表,t1表和t2表区别主要是第三个字段的类型不一样,一个是varchar,一个是datetime类型,源数据表t1中数据如下: -```sql -obclient [test]> select * from t1; -+------+--------+--------+ -| a_id | a_name | a_time | -+------+--------+--------+ -| 1 | test | 24:00 | -+------+--------+--------+ -1 row in set (0.011 sec) -``` -模拟数据清洗并写入 -```sql -obclient [test]> insert into t2 select a_id, a_name,cast(concat('2022-10-10 ', right(tb.a_time,5), ':00') as datetime) from t1 tb; -ERROR 1292 (22007): Incorrect value -``` -可以看到出现了类似的报错,接下来我们就需要去日志里定位到这个报错,因为OceanBase的日志打印比较多,如果直接去找是很难快速定位到跟这条SQL相关的日志,这里就需要用到trace_id。前面我们说到每条SQL进入到数据库,都会产生一个唯一的trace_id,那这个trace_id是怎么获取呢,这里有几种方式: -方式一: -在SQL执行完成之后,通过select last_trace_id();获取 -```sql -obclient [test]> select last_trace_id(); -+-----------------------------------+ -| last_trace_id() | -+-----------------------------------+ -| YB42AC18FF11-0005FE3D398CC982-0-0 | -+-----------------------------------+ -1 row in set (0.001 sec) -``` -方式二: -通过查询 GV$OB_SQL_AUDIT 审计视图查询获取,根据query_sql字段过滤查询的关键字,获取对应的trace_id,关于审计视图的各字段含义,可参考官方文档:[GV$OB_SQL_AUDIT](https://www.oceanbase.com/docs/common-oceanbase-database-1000000000035692) -```sql -obclient [test]> select SVR_IP,SVR_PORT,TRACE_ID,,TENANT_NAME,SQL_ID,QUERY_SQL - -> from oceanbase.gv$ob_sql_audit - -> where query_sql like "%right(tb.a_time,5)%"\G; -*************************** 1. row *************************** - SVR_IP: 172.xx.xx.17 - SVR_PORT: 2882 - TRACE_ID: YB42AC18FF11-0005FE3D398CC982-0-0 - TENANT_NAME: obtest - SQL_ID: D8F4A48653895C3AAACE903CA04EDD21 - QUERY_SQL: insert into t2 select a_id, a_name,cast(concat('2022-10-10 ', right(tb.a_time,5), ':00') as datetime) from t1 tb -1 rows in set (0.224 sec) -``` -方式三: -开启enable_rich_error_msg参数,不过这个是集群级别的,需要在sys租户下开启 -开启方式: -```sql -alter system set enable_rich_error_msg=true; -``` -在开启之后,如果SQL执行报错,会自动返回trace_id和ip信息 -```sql -obclient [test]> insert into t2 select a_id, a_name,cast(concat('2022-10-10 ', right(tb.a_time,5), ':00') as datetime) from t1 tb; -ERROR 1292 (22007): Incorrect value -[172.xx.xx.17:2882] [2023-08-03 20:42:36.361996] [YB42AC18FF11-0005FE3D398CC986-0-0] -``` - -以上三种方式,我们一般会采用第二种方式,方式一只能获取到trace_id,但是获取不到OBServer的IP信息,因为OceanBase为分布式数据库,一套集群一般会有多个OBServer节点,如果没有IP信息,我们很难得知这条SQL是在哪个OBServer节点上执行的。所以是需要这个IP信息方便我们拿着trace_id直接去机器上过滤日志。方式三需要开启集群级别的参数,有的租户并不一定需要这个,并且展示信息相对较少。 -通过获取到的trace_id,以及SVR_IP信息,我们直接到172.xx.xx.17这台机器的/home/admin/oceanbase/log目录下,过滤observer.log,得到如下日志: - -```markdown -[root@ob1 log]# grep "YB42AC18FF11-0005FE3D398CC982-0-0" observer.log -[2023-08-03 20:36:01.943682] WDIAG [LIB.TIME] str_to_ob_time_with_date (ob_time_convert.cpp:1948) [24791][T1012_L0_G0][T1012][YB42AC18FF11-0005FE3D398CC982-0-0] [lt=14][errcode=-4219] datetime is invalid or out of range(ret=-4219, str=2022-10-10 24:00:00, ob_time={mode_:3, parts:[2022, 10, 10, 24, 0, 0, 0, 0, 0, 0, 0], tz_name:"", tzd_abbr:"", time_zone_id:-1, transition_type_id:-1, is_tz_name_valid:false}, date_sql_mode={allow_invalid_dates:0, no_zero_date:0}, lbt()=0xf0bba8c 0xec2b439 0xec175b5 0xec17245 0xb24a08c 0xb2c0cb8 0x41329cc 0xb23d520 0xb2d4359 0x3fbefb0 0xa55f81d 0x3fbe901 0x411b7eb 0x9f87f2e 0x411b290 0x3fb8c7d 0x3ffbe9a 0x7f29409 0x3fb334e 0x3fb0562 0x3fabed9 0x3fa9d9a 0x3fa6768 0x6a0ad14 0xf3666c7 0xf35faca 0x7fc02405fea5 0x7fc023d88b0d) -[2023-08-03 20:36:01.943700] WDIAG [LIB.TIME] str_to_datetime (ob_time_convert.cpp:397) [24791][T1012_L0_G0][T1012][YB42AC18FF11-0005FE3D398CC982-0-0] [lt=19][errcode=-4219] failed to convert string to datetime(ret=-4219) -[2023-08-03 20:36:01.943705] WDIAG [SQL] common_string_datetime (ob_datum_cast.cpp:1190) [24791][T1012_L0_G0][T1012][YB42AC18FF11-0005FE3D398CC982-0-0] [lt=4][errcode=-4219] str_to_datetime failed(ret=-4219, in_str=2022-10-10 24:00:00) -[2023-08-03 20:36:01.943709] WDIAG [SQL] string_datetime (ob_datum_cast.cpp:2838) [24791][T1012_L0_G0][T1012][YB42AC18FF11-0005FE3D398CC982-0-0] [lt=4][errcode=-4219] fail to exec common_string_datetime(expr, ObString(child_res->len_, child_res->ptr_), ctx, res_datum)(ret=-4219) -[2023-08-03 20:36:01.943712] WDIAG [SQL] anytype_anytype_explicit (ob_datum_cast.cpp:8389) [24791][T1012_L0_G0][T1012][YB42AC18FF11-0005FE3D398CC982-0-0] [lt=3][errcode=-4219] inner cast failed(ret=-4219) -[2023-08-03 20:36:01.943715] WDIAG [SQL] cast_eval_arg_batch (ob_datum_cast.cpp:2306) [24791][T1012_L0_G0][T1012][YB42AC18FF11-0005FE3D398CC982-0-0] [lt=3][errcode=-4219] fail to eval one row(ret=-4219, i=0) -....... -``` - -从日志的前两行中,可以看到这次的报错,和用户日志中过滤出来的报错是一样的,因此我们就可以判断是这里的cast转换出现了错误。 -这里为什么这样转换是不行的呢,我们可以看到在t1表中,a_time字段是varchar类型,具体数值是 24:00 ,通过concat('2022-10-10 ', right(tb.a_time,5), ':00') 拼接之后,组成的字符串是 "2022-10-10 24:00:00" ,乍一看貌似没有什么问题,但为什么会转换失败?实际我们细心观察就会发现,2022-10-10 24:00:00 在计算机中是一个非法时间,2022-10-10 24:00:00 其实已经到 2022-10-11 00:00:00,所以这块我们建议用户对这个字符串做下修改,不要使用24:00:00。 -修改之后我们再来看一下执行情况。 -首先t1表内容 -```sql -obclient [test]> select * from t1; -+------+--------+--------+ -| a_id | a_name | a_time | -+------+--------+--------+ -| 1 | test | 00:00 | -+------+--------+--------+ -1 row in set (0.001 sec) -``` -再次执行写入 -```sql -obclient [test]> insert into t2 select a_id, a_name,cast(concat('2022-10-11 ', right(tb.a_time,5), ':00') as datetime) from t1 tb; -Query OK, 1 row affected (0.003 sec) - -obclient [test]> select * from t2; -+------+--------+---------------------+ -| t_id | t_name | t_time | -+------+--------+---------------------+ -| 1 | test | 2022-10-11 00:00:00 | -+------+--------+---------------------+ -1 row in set (0.001 sec) -``` -可以看到执行写入成功,问题得以解决。 -在实际生产过程中,我们还会遇到其他很多类型的错误,大多都是可以通过这种方式快速定位和解决问题。另外还有一种情况,就是我们遇到了慢SQL,导致整个数据库系统变卡,也影响到其他SQL的执行。下面就来看下如何快速定位慢SQL。 - -## **慢SQL** -慢SQL对于DBA来说,一直是一个比较头疼的地方,当遇到慢SQL的时候,总是需要想各种办法去优化慢SQL,如果不快速优化,就会引发一系列问题,那么在OceanBase中我们如何判断是慢SQL,以及如何发现慢SQL呢。 -OceanBase中有一个参数 trace_log_slow_query_watermark 用来设置慢SQL的阈值,当SQL的执行超过这个阈值,那么OceanBase就会认为这是一条慢SQL,然后被记录,这个值默认是 1 秒。 - -| 属性 | 描述 | -| --- | --- | -| 参数类型 | 时间类型 | -| 默认值 | 1s | -| 取值范围 | [1ms, +∞) | -| 是否重启 OBServer 生效 | 否 | - -那么,这些慢SQL信息具体记录在哪些地方呢,我们如何快速找到它呢? -方式一: -首先,我们前面提到所有的SQL的执行都会记录在observer.log的日志里,慢SQL也不例外,在observer.log日志里,我们可以过滤 slow query 关键字,就能获取到执行超过 1 秒的慢SQL,例如下面这条日志 - -```markdown -[root@ob1 log]# grep "slow query" observer.log -[2023-08-04 11:05:36.254730] TRACE [TRACE] after_process (obmp_base.cpp:142) [14092][T1_L0_G0][T1][YB42AC18FF11-0005FE3E568CA6E9-0-0] [lt=14] -[slow query](TRACE=begin_ts=1691118333740472 2023-08-04 03:05:33.740472|[process_begin] u=0 in_queue_time:15, receive_ts:1691118333740456, enqueue_ts:1691118333740457 -|[start_sql] u=0 addr:{ip:"127.0.0.1", port:23382}|[query_begin] u=1 trace_id:YB42AC18FF11-0005FE3E568CA6E9-0-0|[before_processor_run] u=3 -|[session] u=2 sid:3221725883, tenant_id:1|[calc_partition_location_begin] u=48 |[plc_serialize_begin] u=4 |[plc_serialize_end] u=5 -|[tl_calc_part_id_end] u=36 |[get_location_cache_begin] u=0 |[calc_partition_location_end] u=2 |[get_plan_type_end] u=1 |[pc_choose_plan] u=2 -|[check_priv] u=4 |[plan_id] u=1 plan_id:18101|[do_open_plan_begin] u=2 plan_id:18101|[sql_start_stmt_begin] u=0 |[sql_start_stmt_end] u=0 -|[exec_plan_begin] u=0 |[exec_plan_end] u=4 |[do_open_plan_end] u=0 |[get_row] u=1672 |[close_plan_begin] u=2512423 |[start_end_stmt] u=14 -|[end_stmt] u=0 |[close_plan_end] u=0 |[affected_rows] u=1 affected_rows:-1|[store_found_rows] u=0 found_rows:0, return_rows:296 -|[auto_end_plan_begin] > u=0 |[auto_end_plan_end] u=2 |[query_end] u=19 |[process_end] u=10 run_ts:1691118333740476|total_timeu=2514256) -``` - -这条日志其实记录了很多信息,包括SQL的trace_id,以及执行总时间total_time和各个模块的执行时间。 - -方式二: -在OBProxy的日志目录里,专门有一个 obproxy_slow.log 的日志,这个日志也会记录所有的慢SQL信息,我们可以直接查看这个日志,发现执行慢的SQL - -```markdown -2023-08-01 17:44:51.779606,odp,,,,obcluster:obtest:oceanbase,OB_MYSQL,,,OB_MYSQL_COM_QUERY,SELECT,success,,select /*+ ob_querytimeout(10000000000) */ sleep(5),5000864us,120us,0us,5000571us,Y0-00007FCC9D7BC3A0,YB42AC18FF13-0005FE3D2CBF2C76-0-0,,,0,172.xx.xx.19:2881 -2023-08-03 14:30:47.370874,odp,,,,obcluster:obtest:test,OB_MYSQL,,,OB_MYSQL_COM_QUERY,SELECT,success,,select sleep(1),1011179us,86us,0us,1010966us,Y0-00007FCC9DBBD320,YB42AC18FF13-0005FE3D2CBF2CE3-0-0,,,0,172.xx.xx.19:2881 -2023-08-03 16:41:08.255751,odp,,,,obcluster:obtest:test,OB_MYSQL,,,OB_MYSQL_COM_QUERY,DROP,success,,drop table chat_req_records,620960us,113us,0us,620687us,Y0-00007FCC9D7BD3E0,,,,0,172.xx.xx.19:2881 -``` - -obproxy的慢SQL日志中总共有24个字段,根据这些字段,我们也可以获取到一些信息,有些字段为暂时留空,这些字段依次是 - -| 日志打印时间 | 2023-08-01 17:44:51.779606, | -| --- | --- | -| 当前应用名 | odp | -| TraceId | YB42AC18FF13-0005FE3D2CBF2C76-0-0 | -| RpcId | - | -| 逻辑数据源名称  | - | -| 物理库信息(cluster:tenant:database) | - | -| 数据库类型(OB/RDS) | obcluster:obtest:oceanbase | -| 逻辑表名 | OB_MYSQL | -| 物理表名 | | -| SQL 命令(COM_QUERY、COM_STMT_PREPARE等) | | -| SQL 类型(CRUD) | SELECT | -| 执行结果(success/failed) | success | -| 错误码(succ时为空) | | -| SQL | select /*+ ob_querytimeout(10000000000) */ sleep(5) | -| 执行总耗时(ms,包括内部 SQL 执行耗时) | 5000864us | -| 预执行时间 | 120us | -| 链接建立时间 | 0us | -| 数据库执行时间 | 5000571us | -| 当前线程名(odp的内部线程ID) | Y0-00007FCC9D7BC3A0 | -| 系统穿透数据(系统灾备信息等) | YB42AC18FF13-0005FE3D2CBF2C76-0-0 | -| 穿透数据 | | -| DBKey 名称 | - | -| 是否使用 BeyondTrust(version>= 2.0.20 1 是,0 否) | 0 | -| 后端 Server IP | 172.xx.xx.19:2881 | - -方式三: -如果系统当前时段就很慢,可以查询正在执行的比较慢的SQL,通过视图 __all_virtual_processlist 根据 time 字段排序,获取当前系统中,正在执行的慢SQL。 -查询SQL如下 -```sql -SELECT USER, - tenant, - sql_id, - concat(time, 's') as time, - info, - svr_ip, - svr_port, - trace_id -FROM __all_virtual_processlist -WHERE STATE = 'ACTIVE' -ORDER BY time DESC LIMIT 1 -``` -现在模拟一个慢SQL, select /*+ query_timeout(100000000) */ sleep(100); 查询结果如下 -```sql -+------+--------+----------------------------------+------+---------------------------------------------------+---------------+----------+-----------------------------------+ -| USER | tenant | sql_id | time | info | svr_ip | svr_port | trace_id | -+------+--------+----------------------------------+------+---------------------------------------------------+---------------+----------+-----------------------------------+ -| root | obtest | DE47F6BC20D6E36C14AA4D90BDE3B083 | 2s | select /*+ query_timeout(100000000) */ sleep(100) | 172.xx.xx.19 | 2882 | YB42AC18FF13-0005FE3D2CBF2D57-0-0 | -+------+--------+----------------------------------+------+---------------------------------------------------+---------------+----------+-----------------------------------+ -1 row in set (0.003 sec) -``` - -方式四: -如果有OCP的话,也是可以在OCP上直接查看慢SQL,进到具体的租户下面,然后进到SQL诊断中,有慢SQL页面,这里的SQL文本,都是有相同SQL_ID的SQL -![image.png](/img/operation_maintenance/how_to_find_traceid/c.png) -这里再解释下SQL_ID,SQL_ID是每条SQL进入数据库之后,会根据SQL字符串生成一个md5值,所以执行相同的SQL 会有相同的SQL_ID,但是trace_id是不一样的。 -想要查看某条SQL具体是哪次执行慢了,可以点击SQL文本进到下一个页面,拿到执行慢的那一次的 SQL 的trace_id。 -![image.png](/img/operation_maintenance/how_to_find_traceid/d.png) - -通过这几种方式,我们就可以很快获取到系统中执行的慢SQL,不过前两种方式和第四种方式只能获取到trace_id,而第三种方式可以直接获取到执行的SQL,以及SQL_ID。对于前两种方式,想要看具体的内容,还需要再进一步查询。这里又用到我们的 GV$OB_SQL_AUDIT 视图,可以根据视图中的trace_id直接定位到对应的SQL内容: -```sql -obclient [oceanbase]> select SVR_IP,SVR_PORT,TRACE_ID,TENANT_NAME,SQL_ID,QUERY_SQL,PLAN_ID - -> from gv$ob_sql_audit - -> where trace_id = "YB42AC18FF13-0005FE3D2CBF2D57-0-0"\G; -*************************** 1. row *************************** - SVR_IP: 172.xx.xx.19 - SVR_PORT: 2882 - TRACE_ID: YB42AC18FF13-0005FE3D2CBF2D57-0-0 - TENANT_NAME: obtest - SQL_ID: DE47F6BC20D6E36C14AA4D90BDE3B083 - QUERY_SQL: select /*+ query_timeout(100000000) */ sleep(100) - PLAN_ID: 2518 -1 row in set (0.146 sec) -``` - -通过这个查询,我们就知道了具体是哪条SQL执行慢了,并且获取到了SQL执行计划的PLAN_ID,接着就可以根据PLAN_ID去查找这条SQL的执行计划,根据执行计划再进一步排查 -获取执行计划相关信息 -```sql -obclient [oceanbase]> SELECT tenant_id, - -> svr_ip, - -> svr_port, - -> sql_id, - -> plan_id, - -> last_active_time, - -> first_load_time, - -> outline_data - -> FROM GV$OB_PLAN_CACHE_PLAN_STAT - -> WHERE TENANT_ID = 1012 - -> AND SQL_ID = 'DE47F6BC20D6E36C14AA4D90BDE3B083' - -> AND SVR_IP = '172.xx.xx.19' - -> AND SVR_PORT = 2882; -*************************** 1. row *************************** - tenant_id: 1012 - svr_ip: 172.xx.xx.19 - svr_port: 2882 - sql_id: DE47F6BC20D6E36C14AA4D90BDE3B083 - plan_id: 2518 -last_active_time: 2023-08-04 11:42:47.834366 - first_load_time: 2023-08-04 11:42:47.834366 - outline_data: /*+BEGIN_OUTLINE_DATA QUERY_TIMEOUT(100000000) OPTIMIZER_FEATURES_ENABLE('4.0.0.0') END_OUTLINE_DATA*/ -1 row in set (0.019 sec) -``` - -得到这个查询的执行计划的编号(plan_id),这个执行计划第一次生成的时间(first_load_time)以及最后一次被使用的时间(last_active_time) - -接着,就可以根据plan_id来获取这条SQL的物理执行计划 -```sql -obclient [oceanbase]> SELECT OPERATOR, NAME, ROWS, COST FROM GV$OB_PLAN_CACHE_PLAN_EXPLAIN - -> WHERE TENANT_ID = 1012 AND - -> SVR_IP = '172.xx.xx.19' AND - -> SVR_PORT = 2882 AND - -> PLAN_ID = 2518; -+-----------------+------+------+------+ -| OPERATOR | NAME | ROWS | COST | -+-----------------+------+------+------+ -| PHY_EXPR_VALUES | NULL | 1 | 0 | -+-----------------+------+------+------+ -1 row in set (0.001 sec) -``` - -如果想要获取SQL的逻辑执行计划,可以直接使用 explain SQL 获取 -```sql -obclient [test]> explain select /*+ query_timeout(100000000) */ sleep(100); -+--------------------------------------------------------------+ -| Query Plan | -+--------------------------------------------------------------+ -| ========================================== | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ------------------------------------------ | -| |0 |EXPRESSION| |1 |1 | | -| ========================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([sleep(cast(100, DECIMAL(3, 0)))]), filter(nil) | -| values({sleep(cast(100, DECIMAL(3, 0)))}) | -+--------------------------------------------------------------+ -9 rows in set (0.007 sec) -``` -接着就可以根据执行计划,来判断问题的原因。执行计划解读,可参考官网文档:[执行计划](https://www.oceanbase.com/docs/common-oceanbase-database-1000000000033850),定位到问题之后,我们也有很多优化的方式,这些后续我们再展开来将,其中包括 hint, outline绑定等。 -另外,在OceanBase 4.0版本之后,我们也推出了全链路追踪的功能,依靠全链路追踪,可以更快速的定位到一条SQL具体是哪里执行慢。相关全链路追踪文档,可以参考这里:[OceanBase 4.0 解读:全链路追踪要解决什么问题?从一条慢SQL说起](https://open.oceanbase.com/blog/1775421184) - - - - - diff --git a/docs/user_manual/user_best_practices/operation_maintenance/how_to_split_read_write.md b/docs/user_manual/user_best_practices/operation_maintenance/how_to_split_read_write.md deleted file mode 100644 index 03815d56c..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/how_to_split_read_write.md +++ /dev/null @@ -1,233 +0,0 @@ ---- -title: OceanBase如何实现读写分离 -weight: 15 ---- - -# **OceanBase如何实现读写分离** - -在我们实际业务场景中,经常会遇到一类业务场景,既有OLTP类的在线业务,又有OLAP类的分析业务,两种类型的业务同时跑在一套数据库集群上,这对数据库的配置等要求就相对较高,因此我们一般会采用读写分离的方式,将一部分的读请求,路由到 Follower 副本上,从而降低复杂分析计算对资源的侵占,影响在线业务的响应延迟。 -OceanBase数据库也提供了读写分离的能力,通过多种配置方式可以轻松实现,在配置之前,先简单介绍下OceanBase的架构和路由策略 -## **OBProxy路由策略** -在部署OceanBase集群时,需要部署一个OBProxy来做请求的路由转发,如下图,所有业务的请求,经过OBProxy转发之后,会自动访问到数据的 Leader 副本上。 -![image.png](/img/operation_maintenance/how_to_split_read_write/a.png) -这里的OBProxy路由策略其实遵循以下三个策略: - -- Primary Zone 路由:第一优先级 -- LDC 路由:第二优先级 -- 随机路由:第三优先级 -### **Primary Zone 路由** -默认情况,即会将租户请求发送到租户的 primary zone 所在的机器上,OBProxy 在主副本路由时,存在找不到表或者分区 Leader 副本的情况(首次连接、缓存失效、分区计算失败、无法获取表名等原因),通过 Primary Zone 路由可以尽量发往主副本,方便快速寻找 Leader 副本。 -OBProxy 配置项 enable_primary_zone 控制是否启用Primary zone路由,默认是开启的。 -关于Primary Zone相关概念以及配置方式,可参考官方文档介绍:[副本管理](https://www.oceanbase.com/docs/common-oceanbase-database-1000000000036283) -### **LDC 路由** -LDC 路由是基于地理位置的路由,其中有两个重要的概念: - -- IDC:表示逻辑机房概念 -- Region:表示城市的概念。 - -OBProxy 和 OBServer 都可以设置 LDC 信息。通过 LDC 信息,OBProxy 可以确定和 OceanBase 数据库的位置关系。当设置了 LDC 信息后,OBProxy 就会默认使用 LDC 路由。 -LDC路由策略:是指OBProxy按照根据城市/机房等信息就近路由访问OBServer。 -OBProxy 配置项 proxy_idc_name 控制是否开启 LDC 路由策略。 -### **随机路由** -通过优先级路由后,如果还有多个副本,则进行随机路由。如未开启 Primary Zone 路由或者未设置 LDC 路由,就会直接使用随机路由。 -关于OBProxy路由详细信息,可参考官网文档介绍:[数据路由](https://www.oceanbase.com/docs/enterprise-odp-enterprise-cn-10000000001715896) -## **读写分离配置** -要实现真正的读写分离,还需要进一步设置,因为默认情况下,所有的请求都是发送到数据的 Leader 副本上,即强一致性的请求,因为 OLAP 的分析计算,一般对于数据的一致性要求不高,因此可以在开启弱一致性查询之后,实现请求访问到 Follower 副本。 -### **弱一致性读设置** -弱一致性读设置有三种方式: - -- SQL级别设置 -- 会话级别设置 -- 修改OBProxy配置项实现 - -1、SQL级别设置,即在请求的SQL中,加上弱一致性读的Hint,方法如下: -```sql -obclient> select /*+READ_CONSISTENCY(WEAK)*/ * from t1; -``` - -2、会话级别设置,分Global级别和Session级别,修改变量 ob_read_consistency - -| **属性** | **描述** | -| --- | --- | -| 参数类型 | enum | -| 默认值 | STRONG | -| 取值范围 | 0:空字符串 1:FROZEN 2:WEAK 3:STRONG | -| 生效范围 | Global Session | - -执行如下命令进行设置 -```sql -obclient> set global ob_read_consistency=‘weak’ -``` - -3、修改OBProxy配置项实现:通过 Hint 的方式设置弱读需要修改 SQL,修改 SQL 会有一定的业务侵入性,为了不侵入业务,可以指定连接某个OBProxy的所有请求都为弱一致性读,修改方式如下: -通过要设置为弱一致性读的OBProxy连接到数据库,执行如下命令进行修改 -```sql -alter proxyconfig set obproxy_read_consistency = 1; -``` -> 该配置项取值为 0 和 1,默认为 0,表示强读(需要读到最新的数据)。1 表示弱读。 - - -以上三种方式各有优劣势,通过Hint的方式,需要修改SQL,如果SQL数量比较多,则修改起来工作量大,并且对业务代码有一定侵入性,但是比较方便灵活,对于分析请求场景需求不高的情况,可修改个别SQL快速实现弱一致性。会话级别的设置,如果是全局设置,则会导致所有的连接会话都开启弱一致性读,如果只开启Session级别,则每开启一个Session都需要先执行以下命令,也有一定业务的入侵。通过修改OBProxy配置,在分析请求比较多且复杂的场景,则是最方便的方式,所有连接这个OBProxy的SQL默认开启弱一致性读。 - -开启弱一致性读只是配置读写分离的第一步,在开启弱一致性读时,如果没有设置LDC策略,所有的请求还是会按照最开始讲到的 OBProxy 路由策略,优先发送到primary zone,如果未设置primary zone,即primary zone为random,那么请求会按照随机路由的策略,随机发送到任意副本,因此时有可能发送到 Leader 副本,并未实现完全的读写分离。 - -### **LDC设置** -前面介绍到 LDC 包含了 IDC 机房信息和 Region 城市信息。一个城市,包含一个或多个IDC,每个IDC中可部署一个或多个Zone。如下图,是一个典型的两地三中心部署方案,两个城市Region1 和 Region2,以及三个机房 idc1、idc2 和 idc3,其中 idc1 和 idc 3都包含了两个zone。另外还有3个OBProxy,分别部署在三个idc中。 -![image.png](/img/operation_maintenance/how_to_split_read_write/b.png) -LDC的设置,要分别设置OBProxy和OBServer,使其对应上之后,才会实现真正的就近访问。 -1、OBServer设置: -OceanBase 数据库的每个 Zone 都可以设置 Region 属性或 IDC 属性,Region 通常设置为城市名(大小写敏感),IDC 代表该 Zone 所处的机房信息,通常设置机房名(小写) -设置SQL如下: -```sql -alter system modify zone "zone1" set region = "BEIJING"; -alter system modify zone "zone1" set idc = "idc1"; -``` -修改完成后,通过执行 select * from DBA_OB_ZONES; 进行查询 -```sql -obclient [oceanbase]> select * from DBA_OB_ZONES; -+-------+----------------------------+----------------------------+--------+------+----------+-----------+ -| ZONE | CREATE_TIME | MODIFY_TIME | STATUS | IDC | REGION | TYPE | -+-------+----------------------------+----------------------------+--------+------+----------+-----------+ -| zone1 | 2023-06-16 18:32:40.102484 | 2023-08-17 17:36:57.810787 | ACTIVE | idc1 | BEIJING | ReadWrite | -| zone2 | 2023-06-16 18:32:40.102484 | 2023-08-17 17:37:06.622934 | ACTIVE | idc2 | BEIJING | ReadWrite | -| zone3 | 2023-06-16 18:32:40.102484 | 2023-08-17 17:37:11.816791 | ACTIVE | idc3 | SHANGHAI | ReadWrite | -+-------+----------------------------+----------------------------+--------+------+----------+-----------+ -3 rows in set (0.012 sec) -``` - -2、OBProxy设置: -方法一:ODP 进程启动时通过 -o 参数指定设置 LDC 信息 -```sql -cd /opt/taobao/install/obproxy -./bin/obproxy -o proxy_idc_name=idc1 -``` -方法二:通过执行 SQL 语句设置 LDC 信息 -```sql -obclient> alter proxyconfig set proxy_idc_name='idc1'; -``` -通过执行 show proxyinfo idc 命令,可检查是否设置成功 -```sql -obclient [oceanbase]> show proxyinfo idc; -+-----------------+--------------+----------------+----------------+--------------+--------------+--------------+ -| global_idc_name | cluster_name | match_type | regions_name | same_idc | same_region | other_region | -+-----------------+--------------+----------------+----------------+--------------+--------------+--------------+ -| idc1 | obcluster | MATCHED_BY_IDC | [[0]"BEIJING"] | [[0]"zone1"] | [[0]"zone2"] | [[0]"zone3"] | -+-----------------+--------------+----------------+----------------+--------------+--------------+--------------+ -1 row in set (0.001 sec) -``` - -在OBProxy设置完成之后,查看OBProxy的LDC信息,可以在regions_name 和 same_idc中看到,OBProxy和对应的region和zone已经实现了自动匹配,这个时候,业务访问idc1 的这个OBProxy,默认会将请求路由到zone1的OBServer上。 - -但是这样也无法保证OLAP类的读请求都访问到 Follower副本,因此还需要再进一步设置。 - -### **FOLLOWER_FIRST设置** -在设置了LDC路由策略之后,弱一致性的读请求只会就近访问,为了保证弱一致性的读请求能够优先路由到Follower 副本上,还需要对OBProxy设置 proxy_route_policy 参数,这个参数有两个值: - -- FOLLOWER_FIRST:优先发往备副本,如果无备副本可用则发往主副本。 -- FOLLOWER_ONLY:只能发往备副本,如果无备副本可用则报错。 - -设置方式如下,通过连接OBProxy,执行如下命令 -```sql -obclient [oceanbase]> alter proxyconfig set proxy_route_policy="follower_first"; -Query OK, 0 rows affected (0.005 sec) - -obclient [oceanbase]> show proxyconfig like "%proxy_route_policy%"; -+--------------------+----------------+--------------------+-------------+---------------+ -| name | value | info | need_reboot | visible_level | -+--------------------+----------------+--------------------+-------------+---------------+ -| proxy_route_policy | follower_first | proxy route policy | false | SYS | -+--------------------+----------------+--------------------+-------------+---------------+ -1 row in set (0.002 sec) -``` -通过以上的设置,那么就可以实现弱一致性的优先访问 follower 副本的策略。 - -通过以上集中方式的设置,我们就可以灵活配置读写分离的方案,以下举几个常见的读写分离案例,以供大家参考 -## **读写分离案例** -### **案例一:本地备优先读** - -**设置条件:** - -1. 会话级别设置弱一致性读 **或者** SQL 请求加 弱一致性读 Hint -2. 设置LDC策略,OBProxy 和 OBServer 绑定 -3. 配置了FOLLOWER_FIRST - -**路由策略:** - -1. 默认情况下,强一致性读以及增删改的SQL,依然访问 Leader 副本; -2. 开启弱一致性读的 SQL 请求,连接串中指定访问的 OBProxy,OBProxy 默认会将请求路由到本地 Follower 副本,如果本地该数据的副本为 Leader 副本,则自动路由到同 Region 中其他 IDC 的 Follower 副本,如下图中红色线条,跨 IDC 访问数据。 - -**优缺点:** - -1. 优点:配置简单,不需要单独配置一个弱一致性读的 OBProxy; -2. 缺点:如果本地副本是 Leader 副本,则会跨 IDC 访问,SQL 需要改造,对业务有一定影响。 - -![image.png](/img/operation_maintenance/how_to_split_read_write/c.png) - -### **案例二:只读zone** - -**设置条件:** - -1. 设置弱一致性读 OBProxy:obproxy_read_consistency = 1; -2. 设置Primary Zone为:zone1,zone2;zone3 -3. 设置LDC策略,OBProxy 和 OBServer 绑定 -4. 配置了FOLLOWER_FIRST - -**路由策略:** - -1. 需要弱一致性读的SQL,连接设置了弱读的 OBProxy,其余SQL连接正常OBProxy; -2. 通过连接弱读的 OBProxy 的所有 SQL,会基于 LDC 路由策略,以及 FOLLOWER_FIRST策略,会自动访问本地 Follower 副本。 -3. 因为设置了 Primary Zone(zone1,zone2;zone3),所有的 Leader 副本都被迁移到了zone1 和zone2中,因此zone3默认情况下都为Follower 副本,那么zone3的副本就可以只给弱一致性读的分析计算类SQL提供服务。 - -**优缺点:** - -1. 优点:zone级别隔离读写请求,隔离相比方案一更彻底; -2. 缺点:需要单独配置一个弱读的OBProxy,需要设置Primary Zone。 - -![image.png](/img/operation_maintenance/how_to_split_read_write/d.png) - -### **方案三:只读副本** -OceanBase 中除了默认的全功能性副本之外,还有一种只读型副本,只读型副本的名称为 READONLY,简称 R,区别于全功能副本,只读副本提供读的能力,不提供写的能力,只能作为日志流的 Follower 副本,不参与选举及日志的投票,不能当选为日志流的 Leader 副本。 -利用只读副本,我们就可以专门再配置一个zone,只放只读型副本,专门提供给OLAP分析计算类请求,并且只读副本出现故障,并不会影响主集群服务。 - -**设置条件:** - -1. 设置弱一致性读 OBProxy:obproxy_read_consistency = 1; -2. 配置只读型副本 -3. 设置LDC策略,OBProxy 和 OBServer 绑定 -4. 配置了FOLLOWER_FIRST - -**路由策略:** - -1. 主集群正常提供服务,AP类请求,走独立的OBProxy,访问只读型副本; - -**优缺点:** - -1. 优点:OLAP与OLTP完全隔离,互相不受任何影响; -2. 缺点:需要更多的资源来提供给只读型副本。 - -![image.png](/img/operation_maintenance/how_to_split_read_write/e.png) - -以上是几种比较典型的读写分离方案,用户还可以根据自己情况,灵活配置。 -## **大查询策略** -除此之外,在内核层面,OceanBase 还会对大查询进行资源限制,减少大查询对小查询的资源占用。 -配置大查询的参数为 large_query_threshold,超过这个参数设置的阈值,则认为是大查询 - -| **属性** | **描述** | -| --- | --- | -| 参数类型 | 时间类型 | -| 默认值 | 5s | -| 取值范围 | [1ms, +∞) | -| 是否重启 OBServer 节点生效 | 否 | - -如果系统中同时运行着大查询和小查询,OceanBase 数据库会将一部分 CPU 资源分配给大查询,并通过配置参数 large_query_worker_percentage(默认值为 30%)来限制执行大查询最多可以使用的租户活跃工作线程数。 - -| **属性** | **描述** | -| --- | --- | -| 参数类型 | 双精度浮点数 | -| 默认值 | 30 | -| 取值范围 | [0, 100] | -| 是否重启 OBServer 节点生效 | 否 | - -OceanBase 数据库通过限制大查询能使用的租户活跃工作线程数来约束大查询最多能够使用的 CPU 资源,以此来保证系统还会有足够的 CPU 资源来执行 OLTP(例如,交易型的小事务)负载。通过这样的方式来保证对响应时间比较敏感的 OLTP 负载能够得到足够多的 CPU 资源尽快地被执行。另外需要注意的是,虽然 OceanBase 数据库可以做到大查询和 OLTP 资源的分配,large_query_threshold 参数也应设置在一个在合理的范围内,不应该设置成为一个过大的值。否则大查询很容易抢占系统的 CPU 资源而挤进而引发 OLTP 响应过慢甚至队列堆积的问题。 - - diff --git a/docs/user_manual/user_best_practices/operation_maintenance/machine_maintenance.md b/docs/user_manual/user_best_practices/operation_maintenance/machine_maintenance.md deleted file mode 100644 index f622cb6da..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/machine_maintenance.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -title: 机器维护 -weight: 3 ---- -# **机器维护** - -本文介绍如何对机器进行临时停机维护。 - -## **OBServer 节点停机维护** - -### **操作步骤** - -重启节点的主要流程为:停止服务 -> 转储 -> 关闭进程 -> 启动进程 -> 启动服务。 - -1. 根据预估维护时间调整 server_permanent_offline_time - - ```sql - alter system server_permanent_offline_time='2h'; - ``` - - 在 OceanBase 数据库中,server_permanent_offline_time 配置项的名称为“永久下线时长”,是用来控制集群中某个节点的不可用时长,超过设置的时长后,OceanBase 数据库会将其踢出成员列表。 - -2. 使用 root 用户登录到集群的 sys 租户。 - - ```shell - obclient -h10.xx.xx.xx -P2883 -uroot@sys -p -A - ``` - -3. 执行以下命令,进行节点隔离。 - - > **说明** - > - > 如果可以接受节点停止服务,也可以跳过本步骤。 - - ```sql - obclient [(none)]> ALTER SYSTEM STOP SERVER 'svr_ip:svr_port'; - ``` - - 端口默认为 2882。 - - 执行成功后,可以查询 oceanbase.DBA_OB_SERVERS 视图中该 Server 的 STATUS 字段,可以看到字段值仍为 ACTIVE 不变,但 STOP_TIME 字段的值由 NULL 变为停止服务的时间点。 - - STOP SERVER 命令在多副本架构的基础上能够达到业务无损的重启效果,STOP SERVER 命令执行以下逻辑: - - - 将待重启节点上的 Leader 全部切走,并保证除了重启节点以外的其他节点上的副本满足多数派。 - - - 在 Root Service 上将待重启节点标记为 stopped(节点状态为 ACTIVE 状态且 stop_time 字段大于 0),客户端识别后,不会将业务请求路由到该节点。 - - 最终成功 Stop Server 后,重启节点不会引起无主选举及客户端报错等问题,对业务流量完全透明。 - -4. 执行以下命令,对待重启的节点进行转储操作,以便缩短重启后回放 Redo Log 的时间,加速重启。 - - ```sql - obclient [(none)]> ALTER SYSTEM MINOR FREEZE SERVER = ('svr_ip:svr_port'); - ``` - -5. 确认是否转储成功,需要等待转储结束方可执行下一步操作。 - - ```sql - SELECT * FROM oceanbase.GV$OB_TABLET_COMPACTION_PROGRESS WHERE TYPE='MINI_MERGE'\G - ``` - -6. 停止 observer 进程。 - - 1. 使用 admin 用户登录待停止进程的节点所在的机器。 - - 2. 通过命令行工具进入 /home/admin/oceanbase/bin 目录。 - - 3. 停止 observer 进程。 - - 4. 执行以下命令,确认进程是否已停止。 - - ```bash - kill `pidof observer` - ps -ef | grep observer | grep -v grep - ``` - -7. (可选)如果需要维修机器,在本步骤对机器进行短暂的维修。 - -8. 启动 observer 进程。 - - 1. 使用 admin 用户登录待启动进程的节点所在的机器。 - - 2. 启动 observer 进程 - - ```bash - [admin@xxx oceanbase]$ cd /home/admin/oceanbase && ./bin/observer - ``` - -9. 执行以下命令,启动节点服务。 - - ```shell - obclient [(none)]> ALTER SYSTEM START SERVER 'svr_ip:svr_port'; - ``` - - 执行成功后,可以查询 oceanbase.DBA_OB_SERVERS 视图中的 START_SERVICE_TIME 字段,该字段表示节点启动服务的时间。如果该值为 NULL,则表示该节点的服务还没有启动。 - -## **OBProxy 停机维护** - -OceanBase Database Proxy(简称 ODP)是 OceanBase 数据库专用的代理服务器,正常情况下单节点重启对业务没有影响。 - -### **停止 obproxy 进程** - -1. 使用 admin 用户登录到 obproxy 进程所在的机器。 - -2. 执行以下命令,查看 obproxy 进程的进程号。 - - ```shell - $ps -ef | grep obproxy - admin 37360 0 6 11:35 ? 00:00:09 bin/obproxy - admin 43055 36750 0 11:37 pts/10 00:00:00 grep --color=auto obproxy - root 85623 1 0 Jun02 ? 00:15:19 /home/admin/ocp_agent/obagent/obstat2 -o http://xx.xx.xx.xx:81 -c test323 __obproxy__ -f 20 - ``` - - 示例中查询到 ODP 的进程号为 37360。 - -3. 执行如下命令,根据查询到的进程号,停止 obproxy 进程。 - - ```shell - $kill -9 37360 - ``` - -4. 停止成功后,再次执行以下命令,确认 obproxy 进程已不存在。 - - ```shell - $ps -ef|grep obproxy - ``` - -### **启动 obproxy 进程** - -#### **背景信息** - -支持通过以下两种方式来启动 obproxy 进程: - -- 在启动命令中指定 -r 参数来指定 OceanBase 集群的 RootServer 信息。该启动方式不需要额外配置,一般用于开发调试阶段。 - -- 在启动命令中指定 obproxy_config_server_url 参数项来查询获取 OceanBase 集群的 RootServer 信息。该方式需要配置 obproxy_config_server_url,故会依赖 Config Server 的启动,建议使用该方式启动 ODP。 - -#### **操作步骤** - -1. 使用 admin 用户登录到待启动的 ODP 所在的机器。 - -2. 进入 ODP 的安装目录。 - -3. 执行以下命令,启动 ODP。 - - - 在启动命令中指定 -r 参数命令如下: - - ```shell - $./bin/obproxy -p6789 -r'ip:port' -e -n appname -o obproxy_config_server_url='' -c cluster_name - ``` - - 示例: - - ```bash - $./bin/obproxy -r'10.10.10.1:26506;10.10.10.2:26506' -n test -c mycluster - ``` - - - 在启动命令中指定 obproxy_config_server_url 参数命令如下: - - ```shell - $./bin/obproxy -p6789 -e -n appname -o obproxy_config_server_url='your_config_url' - ``` - - 示例: - - ```bash - $./bin/obproxy -n test -o obproxy_config_server_url='http://xx.xx.xx.xx:8877/obproxy_config' - ``` - -4. 启动后,执行以下命令,查看 obproxy 进程是否存在。 - - ```shell - $ps -ef|grep obproxy - ``` - -## **Prometheus** - -可以直接重启,但是这段时间的监控告警会没办法发出。或者重新部署一个 Prometheus,让其接管这个集群,并将当前 Prometheus 下线掉。但是使用此方法后历史的监控数据可能就没办法查看了。 - -## **OBAgent** - -OBAgent 是一个监控采集框架。OBAgent 支持推、拉两种数据采集模式,可以满足不同的应用场景。正常情况下停止机器的 OBAgent 会导致监控不到对应机器的信息,机器维护直接停就可以了,重启后恢复。 - -## **OCP** - -待补充,敬请期待。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/online_offline_ddl.md b/docs/user_manual/user_best_practices/operation_maintenance/online_offline_ddl.md deleted file mode 100644 index 2634704cf..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/online_offline_ddl.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Online DDL 和 Offline DDL -weight: 11 ---- -# **Online DDL 和 Offline DDL** - -本文介绍 OceanBase 数据库 4.0,4.1 和 4.2 版本中,Online DDL 与 Offline DDL 的列表。其中,Online 和 Offline 的区别是执行 DDL 时是否堵塞 DML。 - -## **Online DDL 列表** - -| 分类| 操作| 备注| -| --- | --- | --- | -| 索引 | 增加索引 |全局/局部索引;同一条语句内加多个索引;全局索引带分区;空间索引( V4.1 及之后版本)| -| | 删除索引| | -| | 重建索引| | -| | 重命名索引| | -| 列操作| 末尾加列 | 加 lob 列(text) | -| | 添加 virtual 列 | | -| | 删除 virtual 列 | | -| | 修改列为 not null | | -| | 修改列为 null | | -| | 设列默认值 | | -| | 删除列默认值 | | -| | 修改自增列值 | | -| | 重命名列 | | -| | 增加列类型长度或精度 | int 的增长、varchar 的变长、number 类转换规则 | -| 外键约束操作 | 增加外键、check/not null 约束 | | -| | 删除外键、check/not null 约束 | | -| 表操作 | 重命名表 | | -| | 修改行格式 | | -| | 修改块大小 | | -| | 修改压缩算法 | | -| | 优化表空间 | | -| 分区操作 | 添加分区 | | - -## **Offline DDL列表** - -| 分类 | 操作 | 备注 | -| --- | --- | --- | -| 列操作 | 中间加列(before/after/first) | | -| | 重排列(before/after/first) | | -| | 添加自增列 | | -| | 修改为自增列 | | -| | 修改列类型 | | -| | 加/删/改主键列 | | -| | 添加/删除 stored 生成列 | | -| | 删列 | | -| | 混合列操作在同一条 DDL 语句中 | | -| 表操作 | truncate 表 | | -| | 转换字符集 | | -| | 删表 | | -| 分区操作 | 修改分区规则 | | -| | 删除分区 | 锁分区 | -| | truncate 分区 | 锁分区 | diff --git a/docs/user_manual/user_best_practices/operation_maintenance/outline_binding_explain.md b/docs/user_manual/user_best_practices/operation_maintenance/outline_binding_explain.md deleted file mode 100644 index 53fa35a2b..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/outline_binding_explain.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: 使用 Outline 绑定执行计划 -weight: 9 ---- -# **使用 Outline 绑定执行计划** - -## **语法** - -```sql -CREATE [OR REPLACE] OUTLINE outline_name ON stmt [ TO target_stmt ]; - -# 或 - -CREATE [OR REPLACE] OUTLINE outline_name ON 'SQLID' using 'HINTS'; -``` - -通过语句文本(用户执行的带参数的原始语句)或者 SQLID 都可以创建 Outline。不过由于文本中空格和换行如果有差异就会导致匹配不上,所以不建议通过文本创建 Outline(除非文本非常简单)。这里通过 SQLID 来创建 Outline,只要 SQL 文本不变,SQLID 就不会变。 - -Outline 的用法也是通过 SQL Hint 固定 SQL 的执行计划,可以调整表连接算法、使用的索引等等。 - -## **示例** - -下面示例还是针对前面测试 SQL,尝试调整一下执行计划里表连接顺序。 - -- 执行原 SQL,通过视图 gv$ob_sql_audit 或 gv$ob_plan_cache_plan_stat 找到该 SQL 的 SQLID。 - -- 根据 SQLID 创建 Outline,指定 HINTS。 - - ```sql - create outline otl_test_1 on "9D276020142C5B8259B85C3E8966C579" using hint /*+ leading(i) */ ; - - MySQL [tpccdb]> select * from oceanbase.__all_outline \G - *************************** 1. row *************************** - gmt_create: 2023-06-15 14:04:13.366369 - gmt_modified: 2023-06-15 14:04:13.366369 - tenant_id: 0 - outline_id: 500008 - database_id: 500001 - schema_version: 1686809053365736 - name: otl_test_1 - signature: - outline_content: /*+leading(i) */ - sql_text: - owner: root - used: 0 - version: 101010022023051821-f7379b26f8cd11f026e06846043550f9e0d42ead - compatible: 1 - enabled: 1 - format: 0 - outline_params: ���� - outline_target: - sql_id: 84954A52770C87F243CE1FD58EE60B7F - owner_id: NULL - 1 row in set (0.007 sec) - ``` - -- 删除 Outline - - ```sql - drop outline otl_test_1 ; - ``` - - 删除 Outline 后,该 SQL ID 的执行计划重新生成,又回归到原始的执行计划了。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/replace_and_offline.md b/docs/user_manual/user_best_practices/operation_maintenance/replace_and_offline.md deleted file mode 100644 index f57575388..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/replace_and_offline.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -title: 节点替换或下线 -weight: 4 ---- -# **节点替换或下线** - -## **OBServer** - -OBServer 替换将提供三种场景: - - 场景一:OBServer 所在服务器故障,需要重新安装系统,但是没有空闲的机器补充,后续初始化完需要将这台机器加入到集群,补齐副本。 - - 场景二:OBServer 所在服务器故障,可以补充空闲的机器; - - 场景三:BServer 所在服务器存在已知问题(此时还未故障),需要用正常的机器替换掉。 - -> **注意** -> -> 新增节点操作可以参照 [扩缩容](./scale_in_out.md),节点数量按需添加。如果IP、配置不变,那么部署完成后直接启动就可以了。 -> -> 如果可以使用 OCP 来接管,推荐使用 OCP 直接进行添加/替换节点操作。 -> -> 如下操作都是在 sys 租户下进行。 - - -### **场景一:服务器故障并且后续需要复用&补齐副本** - -1. 缩短永久下线时间,让集群主动将节点永久下线(这时候如果有可用的 OBServer 会自动补齐副本,该场景默认不会有) - - ```sql - alter system set server_permanent_offline_time='20s'; - ``` -2. 确认节点已经永久下线 - - ```sql - select * from DBA_OB_ROOTSERVICE_EVENT_HISTORY where event like "%permanent_offline%" order by 1 desc limit 10; - ``` - -3. 观察日志流和副本是否已经没有该节点的信息 - - ```sql - select SVR_IP,ROLE,count(*) from CDB_OB_TABLE_LOCATIONS group by SVR_IP,ROLE; - - select SVR_IP,ROLE,count(*) from CDB_OB_LS_LOCATIONS group by SVR_IP,ROLE; - ``` - -4. 等待机器维护完成,并完成前置工作(admin用户、目录、授权等) - -5. 重新部署 OBserver 并加入到集群 - -6. 登录集群观察副本情况,正常情况下副本会自动补齐。 - - ```sql - select SVR_IP,ROLE,count(*) from CDB_OB_TABLE_LOCATIONS group by SVR_IP,ROLE; - - select SVR_IP,ROLE,count(*) from CDB_OB_LS_LOCATIONS group by SVR_IP,ROLE; - ``` - -7. 将 server_permanent_offline_time 恢复 - - ```sql - alter system set server_permanent_offline_time='3600s'; - ``` - -### **场景二:服务器故障并且补充新的节点** - -1. 缩短永久下线时间,让集群主动将节点永久下线(这时候如果有可用的observer会自动补齐副本,该场景默认不会有) - - ```sql - alter system set server_permanent_offline_time='20s'; - ``` - -2. 确认节点已经永久下线 - - ```sql - select * from DBA_OB_ROOTSERVICE_EVENT_HISTORY where event like "%permanent_offline%" order by 1 desc limit 10; - ``` - -3. 观察日志流和副本是否已经没有该节点的信息 - - ```sql - select SVR_IP,ROLE,count(*) from CDB_OB_TABLE_LOCATIONS group by SVR_IP,ROLE; - - select SVR_IP,ROLE,count(*) from CDB_OB_LS_LOCATIONS group by SVR_IP,ROLE; - ``` - -4. 新增节点并启动服务 - -5. 将节点添加到集群中 - - ```sql - alter system add server 'xx.xx.xx.xx:2882' zone 'zone1'; - ``` - -6. 登录集群观察副本情况,确认已经没有故障节点,并且新节点已经在补齐副本和日志流 - - ```sql - select SVR_IP,ROLE,count(*) from CDB_OB_TABLE_LOCATIONS group by SVR_IP,ROLE; - - select SVR_IP,ROLE,count(*) from CDB_OB_LS_LOCATIONS group by SVR_IP,ROLE; - ``` - -7. 删除旧的节点 - - ```sql - alter system delete server 'xx.xx.xx.xx:2882'; - ``` - -8. 将 server_permanent_offline_time 恢复 - - ```sql - alter system set server_permanent_offline_time='3600s'; - ``` - -### **场景三:服务器存在已知问题(此时还未故障),用正常的机器替换** - -1. 新增节点并启动服务 - -2. 将节点加到集群 - - ```sql - alter system add server 'xx.xx.xx.xx:2882' zone 'zone1'; - ``` - -3. 确认集群节点信息 - - ```sql - SELECT * FROM oceanbase.DBA_OB_SERVERS; - ``` - -4. 临时关闭 rebalance,否则租户会自动调度均衡 - - ```sql - alter system set enable_rebalance=False; - ``` - -5. 根据节点 IP 查询待替换的节点的 Unit 列表。语句如下: - - ```sql - SELECT unit_id FROM oceanbase.DBA_OB_UNITS WHERE SVR_IP = 'xx.xx.xx.xx'; - ``` - - 其中,svr_ip 需要根据实际情况填写待替换节点的 IP 地址。 - -6. 提交 Unit 迁移任务,将旧节点上的 Unit 迁移到新增的同 Zone 其他节点上。 - - ```sql - ALTER SYSTEM MIGRATE UNIT unit_id DESTINATION 'xx.xx.xx.xx:2882'; - ``` - - 其中,svr_ip 为新增节点的 IP 地址。 - - 每条命令仅支持迁移一个 Unit,多个 Unit 需要执行多次该命令。 - -7. 确认 Unit 的迁移进度 - - ```sql - # 查看迁移状态 - SELECT * FROM oceanbase.DBA_OB_UNIT_JOBS WHERE JOB_TYPE = 'MIGRATE_UNIT'; - - # 如果 MIGRATE_FROM_SVR_IP和MIGRATE_FROM_SVR_PORT为空,则迁移完成 - SELECT * FROM oceanbase.DBA_OB_UNITS WHERE SVR_IP = 'xx.xx.xx.xx'; - ``` - -8. 删除旧节点 - - ```sql - ALTER SYSTEM DELETE SERVER 'xx.xx.xx.xx:2882' ZONE = 'zone1'; - ``` - -9. 确认旧节点是否删除成功。 - - ```sql - SELECT * FROM oceanbase.DBA_OB_SERVERS; - ``` - - 如果列表中已经查询不到旧节点信息,则表示删除成功。如果列表中仍然有该节点,且该节点的状态为 DELETING,则表示该节点仍然在删除状态中。 - -10. 恢复 rebalance。 - - ```sql - alter system set enable_rebalance=True; - ``` - -## **Prometheus** - -重新部署一个 Prometheus,让其接管这个集群,并将当前 Prometheus下线掉。不过历史的监控数据可能将无法查看。 - -## **OBAgent** - -1. 修改 Prometheus 的配置文件,并将对应的 OBAgent 节点剔除 - - 类似于 /usr/local/prometheus-2.37.8.linux-amd64/prometheus_config/prometheus.yaml,根据实际情况来 - -2. 重启 Prometheus - - ```bash - systemctl restart Prometheus - ``` - -## **OBProxy** - -待补充,敬请期待。 - -## **OCP** - -待补充,敬请期待。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/scale_in_out.md b/docs/user_manual/user_best_practices/operation_maintenance/scale_in_out.md deleted file mode 100644 index 4d720c6f5..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/scale_in_out.md +++ /dev/null @@ -1,520 +0,0 @@ ---- -title: 扩缩容 -weight: 2 ---- -# **集群扩缩容** - -本节介绍的是通过 OBD 手动扩缩容的方式,如果集群通过 OCP 管理,那么也可以直接在 OCP 上操作。 - -集群扩缩容包括两种,一种是节点,即扩容每个 Zone 内的 OBServer 的数量;还有一种是 Zone,即增减 Zone 的数量。两种操作都需要在 sys 租户下进行。 - -## **扩容** - -### **扩容节点** - -#### **场景** - -本例从 1-1-1 的集群架构扩容到 2-2-2 的集群架构 - -#### **操作流程** - -1. 新建新增集群的配置文件 - - 新增集群配置参数建议跟原集群配置保持一致,防止资源量以及性能跟其他节点不同,注意:需要替换 server 名字以及 IP 地址。 - - 比如:原先为zone1(server1)、zone2(server2)、zone3(server3)。现在新增的配置里面 zone 不变,server 要变成 server4、server5、server6,并且将 IP 替换为新的 OBServer 地址。 - - 可以参照 [配置文件](../appendix/obtest_config2.md)。 - -2. 使用 OBD 部署新集群 - - obtest2 为新集群名字 - - ```bash - obd cluster deploy obtest2 -c obtest2.yaml - ``` - -3. 把新的配置文件复制到原本的配置文件中。 - - 查看原配置文件路径。 - ```sql - [admin@obtest conf]$ obd cluster list - +--------------------------------------------------------------+ - | Cluster List | - +---------+----------------------------------+-----------------+ - | Name | Configuration Path | Status (Cached) | - +---------+----------------------------------+-----------------+ - | obtest | /home/admin/.obd/cluster/obtest | running | - | obtest2 | /home/admin/.obd/cluster/obtest2 | destroyed | - +---------+----------------------------------+-----------------+ - ``` - - 根据原配置文件路径,找到原配置文件 config.yaml , 打开原配置文件,将新配置文件的节点内容复制到原配置文件中,复制后的配置文件可参考 [配置文件](../appendix/obtest_config3.md)。 - - ```bash - vim /home/admin/.obd/cluster/obtest/config.yaml - ``` - - > **注意** - > - > 新配置一定要放在对应原配置内容下面。 - - - -4. 启动集群 - - 这里的集群为原集群,原先的 OBServer 不会受影响,只会启动新加入的节点。 - - ```bash - obd cluster start obtest - ``` - -5. 将节点添加到集群 - - 使用 root 用户登录集群的 sys 租户,并执行如下命令。 - - ```sql - alter system add server 'xxx.xxx.x.xxx:2882' zone 'zone1'; - alter system add server 'xxx.xxx.x.xxx:2882' zone 'zone2'; - alter system add server 'xxx.xxx.x.xxx:2882' zone 'zone3'; - ``` - -6. 确认集群节点信息 - - ```bash - # 集群内通过如下 SQL 命令查询 - select * from __all_server; - - # OBD 查询 - obd cluster display obtest - ``` - -7. 添加成功后,根据业务实际情况,调整租户的资源配置,即调大 UNIT_NUM。 - - ```bash - ALTER RESOURCE TENANT tenant1 UNIT_NUM 2; - ``` - -### **扩容 Zone** - -#### **场景** - -本例从 zone1,zone2,zone3 扩容为 zone1,zone2,zone3,zone4,zone5 - -#### **操作流程** - -前面增加节点的操作跟扩容节点相同,需要注意的是zone需要变化 - -1. 添加 zone - - ```sql - alter system add zone 'zone4' region 'sys_region'; - alter system start zone 'zone4'; - alter system add zone 'zone5' region 'sys_region'; - alter system start zone 'zone5'; - ``` - -2. 添加 节点 到集群 - - ```sql - alter system add server 'xx.xx.xx.xx:2882' zone 'zone4'; - alter system add server 'xx.xx.xx.xx:2882' zone 'zone5'; - ``` - -3. 修改 resource pool - - ```sql - alter resource pool pool_1 zone_list=('zone1','zone2','zone3','zone4','zone5'); - ``` - -4. 通过修改租户的 Locality 来增加副本。每次只能增加一个 Zone 内的 Locality。 - - ```sql - ALTER TENANT tenant1 LOCALITY='F@zone1,F@zone2,F@zone3,F@zone4'; - - ALTER TENANT tenant1 LOCALITY='F@zone1,F@zone2,F@zone3,F@zone4,F@zone5'; - ``` - -5. 确认当前副本信息 - - ```sql - select SVR_IP,SVR_PORT,TENANT_ID,ROLE,count(*) from CDB_OB_TABLE_LOCATIONS group by SVR_IP,SVR_PORT,TENANT_ID,ROLE; - ``` - -操作结束后,本次扩容完成,等待副本迁移完成。 - -## **缩容** - -### **缩容 Zone** - -#### **场景** - -在进行集群的缩容操作前,需要确认集群中资源对当前负载有较多冗余,查看集群中资源的详细信息的相关操作请参见常用SQL。 -本案例中的缩容是将集群中租户的 5 副本降为 3 副本的场景。 - -#### **操作步骤** - -1. (可选)如果租户使用了 z4 或 z5 作为 Primary Zone,则需要修改该租户的 Primary Zone。 - - ```sql - ALTER TENANT tenant_name PRIMARY_ZONE='z1,z2,z3'; - ``` - -2. 通过修改租户 tenant1 的 Locality 来删除副本。根据 Locality 的变更规则,每次只能删除一个 Zone 内的 Locality,Locality 的变更规则相关信息请参见官网 OceanBase 数据库文档 [Locality 概述](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699416)。 - - ```sql - ALTER TENANT tenant1 LOCALITY='F@z1,F@z2,F@z3,F@z4'; - - ALTER TENANT tenant1 LOCALITY='F@z1,F@z2,F@z3'; - ``` - -3. 执行以下命令,停止 z4、z5。 - - ```sql - ALTER SYSTEM STOP ZONE z4; - - ALTER SYSTEM STOP ZONE z5; - ``` - -4. 缩小资源池 pool1 的 ZONE_LIST 范围,从而将 z4、z5 从资源池中移出。 - - ```sql - ALTER RESOURCE POOL pool1 ZONE_LIST=('z1','z2','z3'); - ``` - -5. 执行以下语句,从集群中删除 Zone 中的 OBServer 节点。 - - > **注意** - > - > 本示例中,仅租户 tenant1 使用了 z4、z5 上的资源,在实际场景中,如果有其他租户也使用了 z4 或 z5,则还需要对这些租户也执 行一遍前面的步骤(步骤 2 ~ 步骤 5)。 - - ```sql - ALTER SYSTEM DELETE SERVER 'xxx.xxx.x.xx4:2882'; - - ALTER SYSTEM DELETE SERVER 'xxx.xxx.x.xx5:2882'; - ``` - - 删除后,可以执行以下语句,确认列表中已查询不到这些 OBServer 节点,则表示删除成功。 - - ```sql - SELECT * FROM oceanbase.DBA_OB_SERVERS; - ``` - -6. 确认 OBServer 节点删除成功后,执行以下语句,从集群中删除 Zone。 - - ```sql - ALTER SYSTEM DELETE ZONE z4; - - ALTER SYSTEM DELETE ZONE z5; - ``` - - 删除后,可以执行如下语句,确认列表中已查询不到这些 Zone,则表示删除成功。结束后,本次缩容完成。 - -### **缩容节点** - -#### **场景** - -在进行集群的缩容操作前,需要确认集群中资源对当前负载有较多冗余,查看集群中资源的详细信息的相关操作请参见常用SQL。 -该案例场景为当前集群中共包含 3 个可用区 z1、z2、z3,每个 Zone 内包含 2 个 OBServer 节点,将每个 Zone 缩容到 1 个 OBServer 节点。 - -#### **操作步骤** - -1. 使用 root 用户登录集群的 sys 租户。 - - > **注意** - > - > 如果要删除的节点没有任何 UNIT,那么可以跳过 2-6,直接删除。 - > - > 由于当前版本暂不支持调小租户的 UNIT_NUM,该缩容方式仅适用于当前集群中 Unit 数量小于或等于计划删除 OBserver 节点后的单个 Zone 中的可用 OBserver 节点数量。例如,本示例中,如果租户 tenant1 的 UNIT_NUM 为 2,则删除各 Zone 中的 OBServer 时会失败。 - -2. 临时关闭 rebalance,否则租户会自动调度均衡 - - ```sql - alter system set enable_rebalance=False; - ``` - -3. 查看当前 Unit 分布,获取待迁移的 UNIT_ID。 - - ```sql - SELECT * FROM OCEANBASE.__all_unit; - ``` - -4. 手动迁移Unit - - ```sql - ALTER SYSTEM MIGRATE UNIT = unit_id DESTINATION = 'xx.xx.xx.xx:2882'; - ``` - -5. 确认 Unit 的迁移进度 - - ```sql - # 查看迁移状态 - SELECT * FROM oceanbase.DBA_OB_UNIT_JOBS WHERE JOB_TYPE = 'MIGRATE_UNIT'; - - # 如果 MIGRATE_FROM_SVR_IP和MIGRATE_FROM_SVR_PORT为空,则迁移完成 - SELECT * FROM oceanbase.DBA_OB_UNITS WHERE SVR_IP = 'xx.xx.xx.xx'; - ``` - -6. 多台节点迁移重复 3-5 - -7. 删除各 Zone 中的 OBServer 节点。 - - ```sql - ALTER SYSTEM DELETE SERVER 'xxx.xxx.x.xx1:2882' ZONE='z1'; - ALTER SYSTEM DELETE SERVER 'xxx.xxx.x.xx2:2882' ZONE='z2'; - ALTER SYSTEM DELETE SERVER 'xxx.xxx.x.xx3:2882' ZONE='z3'; - ``` - - 如果列表中已经查询不到旧节点信息,则表示删除成功。如果列表中仍然有该节点,且该节点的状态为 DELETING,则表示该节点仍然在删除状态中。 - - ```sql - SELECT * FROM OCEANBASE.DBA_OB_SERVERS; - ``` - -8. 恢复 rebalance。 - - ```sql - alter system set enable_rebalance=True; - ``` - -## **租户资源的扩缩容** - -租户资源的扩缩容也是包括两种,一种是增加租户的资源单元(unit)配置大小;另一种是增加租户的资源单元(unit)数量。两种操作均需在 sys 租户下进行操作。 - -### **前提条件** - -在进行租户的扩容和缩容操作前,需要进行以下操作: - -- 由于空闲的资源池会被计算为占用的资源,故在扩容前,如果有租户被删除,建议与租户对应的资源池也一并删除,以便释放资源。删除资源池的相关操作请参见官网 OceanBase 数据库文档 [删除资源池](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001701230)。 - -- 进行租户缩容前,建议进行一轮转储以便释放租户正在使用的内存。手动触发转储的相关操作请参见官网 OceanBase 数据库文档 [手动触发转储](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001701236)。 - -- 查看集群中资源的分配情况,了解集群中资源的使用情况。查看集群节点的资源总量和分配状态的相关操作请参见官网 OceanBase 数据库文档 [查看集群的资源信息](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001701028)。 - -### **通过 SQL 语句修改租户的资源单元配置** - -在通过调大和调小租户资源单元的配置(unit_config)进行扩容和缩容时,有以下两种场景: - -- 当前租户配置了独立的资源单元配置,可以直接修改租户的资源单元配置。 - -- 多个租户使用了相同的资源单元配置,需要切换租户的资源单元配置。 - -确认租户是否使用了独立的资源单元配置的操作如下: - -1. 使用 root 用户登录集群的 sys 租户。 - -2. 执行以下语句,获取待操作的租户所属的资源配置 ID。 - - ```sql - SELECT a.TENANT_NAME, b.UNIT_CONFIG_ID FROM oceanbase.DBA_OB_TENANTS a,oceanbase. DBA_OB_RESOURCE_POOLS b WHERE b.TENANT_ID=a.TENANT_ID; - +-------------+----------------+ - | TENANT_NAME | UNIT_CONFIG_ID | - +-------------+----------------+ - | sys | 1 | - | MySQL | 1002 | - | Oracle | 1004 | - +-------------+----------------+ - 3 rows in set - ``` - -根据查询结果,如果当前租户对应的 UNIT_CONFIG_ID 与其他租户相同,则表示有多个租户使用了相同的资源单元配置;如果当前租户中对应的 UNIT_CONFIG_ID 与其他租户均不相同,则表示该租户使用了独立的资源单元配置。 - -以下将通过这两种场景提供租户扩容和缩容的操作指导。 - -#### **注意事项** - -在调大资源规格时,无论是通过修改资源配置还是切换资源配置,调整后的资源总量都必须满足以下要求: - -```shell -Sum(min_cpu) <= CPU_CAPACITY; // CPU 总的容量 -Sum(max_cpu) <= CPU_CAPACITY * resource_hard_limit; // CPU 总的容量 * 配置项的值(默认100) -Sum(memory_size) <= MEM_CAPACITY; // 内存总的容量 -Sum(log_disk_size) <= LOG_DISK_CAPACITY; // 日志盘总的容量 -``` - -否则,系统会报错,提示扩容失败。 - -#### **场景** - -假设当前集群中共包含 3 个可用区 z1、z2、z3,每个 Zone 内包含 3 台 OBServer。集群中有一个普通租户 tenant1,其资源分配情况如下: - -```sql -CREATE RESOURCE UNIT unit1 MAX_CPU 6, MIN_CPU 6, MEMORY_SIZE '36G', MAX_IOPS 1024, MIN_IOPS 1024, IOPS_WEIGHT=0, LOG_DISK_SIZE = '4G'; - -CREATE RESOURCE POOL pool1 UNIT 'unit1', UNIT_NUM 2, ZONE_LIST ('z1','z2','z3'); - -CREATE TENANT tenant1 resource_pool_list=('pool1'); -``` - -#### **租户配置了独立的资源单元配置的场景** - -如果待操作的租户配置了独立的资源单元配置,您可以直接通过修改租户的 unit_config 来完成租户的扩容和缩容。 -方法如下: - -1. 进入 oceanbase 数据库。 - - ```sql - USE oceanbase; - ``` - -2. 执行以下语句,获取待操作的租户所使用的资源配置 ID。 - - ```sql - SELECT a.TENANT_NAME, b.UNIT_CONFIG_ID FROM oceanbase.DBA_OB_TENANTS a,oceanbase. DBA_OB_RESOURCE_POOLS b WHERE b.TENANT_ID=a.TENANT_ID; - +-------------+----------------+ - | TENANT_NAME | UNIT_CONFIG_ID | - +-------------+----------------+ - | sys | 1 | - | MySQL | 1001 | - | Oracle | 1002 | - +-------------+----------------+ - 3 rows in set - ``` - -3. 执行以下语句,获取待操作租户的资源配置详细信息。 - - ```sql - SELECT * FROM oceanbase.DBA_OB_UNIT_CONFIGS WHERE UNIT_CONFIG_ID='1001'; - +----------------+-------+---------+---------+-------------+---------------+----------+----------+-------------+ - | UNIT_CONFIG_ID | NAME | MAX_CPU | MIN_CPU | MEMORY_SIZE | LOG_DISK_SIZE | MAX_IOPS | MIN_IOPS | IOPS_WEIGHT | - +----------------+-------+---------+---------+-------------+---------------+----------+----------+-------------+ - | 1001 | unit1 | 6 | 6 | 38654705664 | 4294967296 | 1024 | 1024 | 0 | - +----------------+-------+---------+---------+-------------+---------------+----------+----------+-------------+ - 1 row in set - ``` - -4. 根据获取的资源单元配置信息,修改 unit1 的配置。 - - - 调大 unit1 的配置 - - ```sql - ALTER RESOURCE UNIT unit1 MAX_CPU 8, MIN_CPU 8, MEMORY_SIZE '40G', MAX_IOPS 1024, MIN_IOPS 1024, IOPS_WEIGHT 0, LOG_DISK_SIZE '6G'; - ``` - - - 调小 unit1 的配置 - - ```sql - ALTER RESOURCE UNIT unit1 MAX_CPU 5, MIN_CPU 5, MEMORY_SIZE '5G', MAX_IOPS 1024, MIN_IOPS 1024, IOPS_WEIGHT 0, LOG_DISK_SIZE '2G'; - ``` - -#### **多个租户使用了相同的资源单元配置的场景** - -如果多个租户共用了同一个资源单元配置模版,则不能通过简单的调大资源单元配置来实现租户的扩容和缩容。因为一旦修改,将导致使用相同资源单元配置模版的所有租户同时进行了扩容或缩容。此场景下,需要先创建独立的资源单元配置后,再为租户切换资源单元配置。 - -例如,待扩容或缩容的租户为 tenant1,但由于 tenant1、tenant2 均使用了 unit1 作为资源单元配置,因此需要创建一个新的资源单元。 - -1. 使用 root 用户登录集群的 sys 租户。 - -2. 进入 oceanbase 数据库。 - -3. 创建一个独立的资源单元配置。其中,unit2为新建的资源单元的名称,名称需要保证全局唯一。 - - - 创建比当前资源单元配置高的 unit2 - - ```sql - CREATE RESOURCE UNIT unit2 MAX_CPU 8, MIN_CPU 8, MEMORY_SIZE '20G', MAX_IOPS 1024, MIN_IOPS 1024, IOPS_WEIGHT 0, LOG_DISK_SIZE '6G'; - ``` - - - 创建比当前资源单元配置低的 unit3 - - ```sql - CREATE RESOURCE UNIT unit3 MAX_CPU 5, MIN_CPU 5, MEMORY_SIZE '5G', MAX_IOPS 1024, MIN_IOPS 1024, IOPS_WEIGHT 0, LOG_DISK_SIZE '2G'; - ``` - -4. 修改租户的资源池,将资源池的资源单元配置替换为刚刚新创建的 Unit。其中,unit2 和 unit3 为刚刚新创建的 Unit。 - - ```sql - ALTER RESOURCE POOL pool1 unit='unit2'; - - ALTER RESOURCE POOL pool1 unit='unit3'; - ``` - -#### **后续处理** - -操作结束后,您可以通过 oceanbase.DBA_OB_UNIT_CONFIGS 视图,确认当前租户的 unit_config 是否修改成功。 - -```sql -SELECT * FROM oceanbase.DBA_OB_UNIT_CONFIGS; -``` - -更多 DBA_OB_UNIT_CONFIGS 视图的字段及说明信息请参见官网 OceanBase 数据库文档 [DBA_OB_UNIT_CONFIGS](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699272)。 - -### **通过修改 UNIT_NUM** - -对租户内资源的扩容还可以通过调大资源池中的 UNIT_NUM 来实现。当前暂不支持通过调小资源池中的 UNIT_NUM 来进行缩容。 - -#### **前提条件** - -在进行租户的扩容操作前,需要进行以下操作: - -- 由于空闲的资源池会被计算为占用的资源,故在扩容前,如果有租户被删除,建议与租户对应的资源池也一并删除,以便释放资源。删除资源池的相关操作请参见 [删除资源池](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001701230)。 - -- 查看集群中资源的分配情况,了解集群中资源的使用情况。查看集群节点的资源总量和分配状态的相关操作请参见 [查看集群的资源信息](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001701028)。调大资源池的 UNIT_NUM 时,修改后的 UNIT_NUM 数量要小于或等于 Zone 内可用的 OBServer 节点数量。 - -> **注意** -> -> 调大资源池的 UNIT_NUM 时,修改后的 UNIT_NUM 数量要小于或等于 Zone 内可用的 OBServer 节点数量。 - -#### **通过 SQL 语句修改租户的 UNIT_NUM** - -您可以通过调大租户的 UNIT_NUM 来进行租户的扩容。 - -#### **场景** - -假设当前集群中共包含 3 个可用区 z1、z2、z3,每个 Zone 内包含 3 个 OBServer 节点。在集群中创建一个普通租户 MySQL,其资源分配情况如下: - -```sql -CREATE RESOURCE UNIT unit1 MAX_CPU 6, MIN_CPU 6, MEMORY_SIZE '36G', MAX_IOPS 1024, MIN_IOPS 1024, IOPS_WEIGHT=0, LOG_DISK_SIZE '2G'; - -CREATE RESOURCE POOL pool1 UNIT 'unit1', UNIT_NUM 1, ZONE_LIST ('z1','z2','z3'); - -CREATE TENANT MySQL resource_pool_list=('pool1'); -``` - -#### **调大 UNIT_NUM** - -随着业务量的不断变大,每个 Zone 上 1 个 Unit 已经无法承载当前的业务量,因此需要考虑调大 UNIT_NUM 来提高租户的服务能力,以满足新的业务需求。 - -1. 使用 root 用户登录集群的 sys 租户。 - -2. 进入 oceanbase 数据库。 - - ```sql - USE oceanbase; - ``` - -3. 执行以下语句,获取当前租户的资源配置信息。 - - ```sql - SELECT a.TENANT_NAME, b.RESOURCE_POOL_ID,b.NAME resource_pool_name,b.UNIT_CONFIG_ID,b. UNIT_COUNT FROM oceanbase.DBA_OB_TENANTS a,oceanbase.DBA_OB_RESOURCE_POOLS b WHERE b.TENANT_ID=a.TENANT_ID; - +-------------+------------------+--------------------+----------------+------------+ - | TENANT_NAME | RESOURCE_POOL_ID | resource_pool_name | UNIT_CONFIG_ID | UNIT_COUNT | - +-------------+------------------+--------------------+----------------+------------+ - | sys | 1 | sys_pool | 1 | 1 | - | tenant1 | 1001 | pool1 | 1001 | 2 | - | Oracle | 1002 | pool002 | 1002 | 1 | - +-------------+------------------+--------------------+----------------+------------+ - 3 rows in set - - SELECT * FROM oceanbase.DBA_OB_UNIT_CONFIGS WHERE UNIT_CONFIG_ID='1001'; - +----------------+-------+---------+---------+-------------+---------------+----------+----------+-------------+ - | UNIT_CONFIG_ID | NAME | MAX_CPU | MIN_CPU | MEMORY_SIZE | LOG_DISK_SIZE | MAX_IOPS | MIN_IOPS | IOPS_WEIGHT | - +----------------+-------+---------+---------+-------------+---------------+----------+----------+-------------+ - | 1001 | unit1 | 6 | 6 | 38654705664 | 2147483648 | 1024 | 1024 | 0 | - +----------------+-------+---------+---------+-------------+---------------+----------+----------+-------------+ - 1 row in set - ``` - - 其中,UNIT_COUNT 表示当前租户所分配的 UNIT_NUM 。 - -4. 执行以下语句,调大 UNIT_NUM 的数量。 - - > **说明** - > - > 不支持通过指定 unit_id 的方式调大 UNIT_NUM 的数量。 - - ```sql - ALTER RESOURCE TENANT MySQL UNIT_NUM = 2; - ``` - - 语句执行后,系统会直接在每个 Zone 内添加一个 Unit。 diff --git a/docs/user_manual/user_best_practices/operation_maintenance/statistical_info_update.md b/docs/user_manual/user_best_practices/operation_maintenance/statistical_info_update.md deleted file mode 100644 index 5fc628e28..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/statistical_info_update.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: 统计信息更新 -weight: 7 ---- -# **统计信息更新** - -OceanBase 数据库中表的统计信息过期判断标准为:如果当前表增量的 DML 次数(上一次收集统计信息时 DML 次数到本次收集统计信息期间发生的增/删/改总次数)超过设置的阈值时就会过期。阈值的默认值是 10%。 - -## **手动收集统计信息** - -语法如下: - -```sql -analyze_stmt: -ANALYZE TABLE table_name UPDATE HISTOGRAM ON column_name_list WITH INTNUM BUCKETS -``` - -示例:收集表 tbl1 的统计信息,列的桶个数为 30 个。 - -```sql -ANALYZE TABLE tbl1 UPDATE HISTOGRAM ON a, b, c, d WITH 30 BUCKETS; -``` - -OceanBase 数据库 MySQL 模式下用于查询相关统计信息的视图如下表所示: - -| 视图名称 | 描述 | -| --- | --- | -| OCEANBASE.DBA_TAB_STATISTICS | 用于查询表级的统计信息。 | -| OCEANBASE.DBA_TAB_COL_STATISTICS | 用于查询 GLOBAL 级别的列级统计信息。 | -| OCEANBASE.DBA_PART_COL_STATISTICS | 用于查询 PARTITON 级别的列级统计信息。 | -| OCEANBASE.DBA_SUBPART_COL_STATISTICS | 用于查询 SUBPARTITON 级别的列级统计信息。 | -| OCEANBASE.DBA_TAB_HISTOGRAMS | 用于查询 GLOBAL 级别的列级直方图统计信息。 | -| OCEANBASE.DBA_PART_HISTOGRAMS | 用于查询 PARTITON 级别的列级直方图统计信息。 | -| OCEANBASE.DBA_SUBPART_HISTOGRAMS | 用于查询 SUBPARTITON 级别的列级直方图统计信息。 | -| OCEANBASE.DBA_IND_STATISTICS | 用于查询索引统计信息。 | diff --git a/docs/user_manual/user_best_practices/operation_maintenance/view_modify_parameters.md b/docs/user_manual/user_best_practices/operation_maintenance/view_modify_parameters.md deleted file mode 100644 index f22af9c1f..000000000 --- a/docs/user_manual/user_best_practices/operation_maintenance/view_modify_parameters.md +++ /dev/null @@ -1,453 +0,0 @@ ---- -title: 查看及修改参数 -weight: 5 ---- -# **查看及修改参数** - -本文介绍如何查看和修改集群参数、租户参数。 - -## **集群参数** - -OceanBase 数据库以集群形态运行,提供多租户(也叫多实例)能力。集群初始化成功后,默认会有一个 sys 租户,用以保存集群的所有元数据、参数等。您也可通过登录 sys 租户管理 OceanBase 集群。 - -### **查看和修改 OceanBase 集群参数** - -您可通过命令 `show parameters [ like '%参数名特征%' ] ;` 或 `show parameters where name in ( '参数名1' , '参数名2' ) ;` 查看 OceanBase 集群参数。 - -在命令 `show parameters [ like '%参数名特征%' ] ;` 中不带 like 子句表示查看所有参数。 - -现在以查看参数 memory_limit 和 memory_limit_percentage 为例进行讲解。 - -首先这两个参数是指定进程 observer 启动后能获取的最大内存,如果分配不出来进程可能会启动失败或运行异常。这个内存可以指定大小,也可以指定总可用内存的比例。无论使用哪种方法,都需要确保实际可以使用的内存不少于 8G。 - -这两个参数实际只有一个生效,取两个参数中的最低值。memory_limit 设置为 0 时就表示不限制。使用哪个参数控制进程 observer 内存大小由运维人员决定。生产环境中,机器内存很大的时候,通常是通过 memory_limit_percentage 控制,默认值是 80(表示总可用内存的 80%)。 - -```sql -MySQL [oceanbase]> show parameters like 'memory_limit%'; -+-------+----------+---------------+----------+-------------------------+-----------+-------+--------------------------------------------------------------------------------------------------------------------------------+----------+---------+---------+-------------------+ -| zone | svr_type | svr_ip | svr_port | name | data_type | value | info | section | scope | source | edit_level | -+-------+----------+---------------+----------+-------------------------+-----------+-------+--------------------------------------------------------------------------------------------------------------------------------+----------+---------+---------+-------------------+ -| zone1 | observer | x.x.x.x | 2882 | memory_limit_percentage | NULL | 80 | the size of the memory reserved for internal use(for testing purpose). Range: [10, 90] | OBSERVER | CLUSTER | DEFAULT | DYNAMIC_EFFECTIVE | -| zone1 | observer | x.x.x.x | 2882 | memory_limit | NULL | 8G | the size of the memory reserved for internal use(for testing purpose), 0 means follow memory_limit_percentage. Range: 0, [8G,) | OBSERVER | CLUSTER | DEFAULT | DYNAMIC_EFFECTIVE | -+-------+----------+---------------+----------+-------------------------+-----------+-------+--------------------------------------------------------------------------------------------------------------------------------+----------+---------+---------+-------------------+ -2 rows in set (0.002 sec) - -MySQL [oceanbase]> show parameters where name in ('memory_limit','memory_limit_percentage')\G -*************************** 1. row *************************** - zone: zone1 - svr_type: observer - svr_ip: x.x.x.x - svr_port: 2882 - name: memory_limit_percentage - data_type: NULL - value: 80 - info: the size of the memory reserved for internal use(for testing purpose). Range: [10, 90] - section: OBSERVER - scope: CLUSTER - source: DEFAULT -edit_level: DYNAMIC_EFFECTIVE -*************************** 2. row *************************** - zone: zone1 - svr_type: observer - svr_ip: x.x.x.x - svr_port: 2882 - name: memory_limit - data_type: NULL - value: 8G - info: the size of the memory reserved for internal use(for testing purpose), 0 means follow memory_limit_percentage. Range: 0, [8G,) - section: OBSERVER - scope: CLUSTER - source: DEFAULT -edit_level: DYNAMIC_EFFECTIVE -2 rows in set (0.002 sec)0 -``` - -上述参数输出结果说明如下: - -| 列名 | 列值 | 备注 | -| --- | --- | --- | -| zone | zone1 | 节点的 Zone 名称 | -| svr_type | observer | 节点类型 | -| svr_ip | x.x.x.x | 节点 IP | -| svr_port | 2882 | 节点 RPC 端口 | -| name | memory_limit_percentage | 参数名 | -| data_type | NULL | 参数类型 | -| value | 80 | 参数值 | -| info | the size of the memory reserved for internal use(for testing purpose). Range [10, 90] | 参数的描述。 该参数的这个描述不是很准确,这是限制进程 observer 能分配的最大内存 | -| section | OBSERVER | 参数归类 | -| scope | CLUSTER | 参数生效范围 | -| edit_level | DYNAMIC_EFFECTIVE | 参数生效时机:动态生效 / 需要重启 | - -OceanBase 集群参数可通过命令 alter system set 参数名='参数值' [ server = '节点IP:节点RPC端口' ] ; 进行修改。不指定 server 子句表示参数修改应用于所有 OceanBase 集群节点。 - -示例:调整参数 syslog_level 值为 USER_ERROR。 - -```sql -MySQL [oceanbase]> alter system set syslog_level = 'USER_ERR' server='x.x.x.x:2882' ; -Query OK, 0 rows affected (0.021 sec) - -MySQL [oceanbase]> show parameters like 'syslog_level'\G -*************************** 1. row *************************** - zone: zone1 - svr_type: observer - svr_ip: x.x.x.x - svr_port: 2882 - name: syslog_level - data_type: NULL - value: USER_ERR - info: specifies the current level of logging. There are DEBUG, TRACE, INFO, WARN, USER_ERR, ERROR, six different log levels. - section: OBSERVER - scope: CLUSTER - source: DEFAULT -edit_level: DYNAMIC_EFFECTIVE -1 row in set (0.002 sec) -``` - -### **OceanBase 集群参数文件** - -上述参数的修改都是立即生效,并且参数修改会持久化到 OceanBase 集群节点自己的参数文件。 - -> **注意** -> -> 此处的 OceanBase 集群节点自己的参数文件,不是指前面提到的 OBD 集群部署参数文件。 - -通常 OceanBase 集群每个节点的启动目录下都会有一个目录 etc,保存了该节点进程的参数文件 observer.config.bin。observer.config.bin 是一个 binary 类型的文件,不能直接用 cat 命令读取,您可使用 strings 命令读取。该文件也不建议直接修改,您可通过上面提到的命令进行修改。 - -```bash -[admin@obce00 oceanbase-ce]$ pwd -/home/admin/oceanbase-ce -[admin@obce00 oceanbase-ce]$ tree -L 2 -. - -├── bin -│ └── observer -> /home/admin/.obd/repository/oceanbase-ce/3.1.0/84bd2fe27f8b8243cc57d8a3f68b4c50f94aab80/bin/observer -├── etc -│ ├── observer.config.bin -│ └── observer.config.bin.history -├── etc2 -│ ├── observer.conf.bin -│ └── observer.conf.bin.history -├── etc3 -│ ├── observer.conf.bin -│ └── observer.conf.bin.history - -<省略掉无关内容> - -9 directories, 20 files -``` - -从上述目录结构可得,启动目录下有三个文件夹:etc、etc2、etc3,每个文件夹下都有其参数文件及其历史文件备份。 - -observer 进程默认会读取文件夹 etc 中的参数文件,其他两个目录是参数文件的备份,这个备份路径也是通过参数 config_additional_dir 指定的,默认值是同一个启动目录的 etc2 和 etc3。 - -生产环境一般会将 etc 设置到其他磁盘,这样会更加安全。当前 OBD 版本还是把它放到同一块盘,但参数文件需要放到哪里并没有具体规定,您可自行修改。 - -> **说明** -> -> etc2 和 etc3 下的参数文件名跟 etc 下参数文件名并不完全一致。 - -```sql -MySQL [oceanbase]> show parameters like 'config_additional_dir'\G -*************************** 1. row *************************** - zone: zone1 - svr_type: observer - svr_ip: x.x.x.x - svr_port: 2882 - name: config_additional_dir - data_type: NULL - value: etc2;etc3 - info: additional directories of configure file - section: OBSERVER - scope: CLUSTER - source: DEFAULT -edit_level: DYNAMIC_EFFECTIVE -1 row in set (0.002 sec) - -[admin@obce00 oceanbase-ce]$ strings etc/observer.config.bin | grep -n memory_limit -25:memory_limit=8G -[admin@obce00 oceanbase-ce]$ strings etc2/observer.conf.bin | grep -n memory_limit -25:memory_limit=8G -[admin@obce00 oceanbase-ce]$ strings etc3/observer.conf.bin | grep -n memory_limit -25:memory_limit=8G -``` - -查看实际参数文件内容,可以看出不是所有参数都在这个参数文件中。只有那些被 alter system set 命令修改过的参数,以及在进程 observer 启动时通过 -o 指定的参数才会记录在参数文件里。其他参数都是取默认值(写在进程 observer 的代码里)。 - -### **使用 OBD 修改 OceanBase 集群参数** - -上文中直接在 OceanBase 集群里修改参数后,会立即同步到集群节点自身的参数文件中,但是不会同步到 OBD 的集群部署配置文件中(后期 OBD 可能会改进这个功能)。 - -所以,在您使用 OBD 工具重启 OceanBase 集群时,默认又会带参数启动进程 observer。如果前面在 OceanBase 集群里修改的参数在 OBD 集群部署配置文件中也有,并且 OBD 集群部署配置文件的值还是未经修改的,那就意味着修改过的参数又被调整回原来的设置值了(运维需要理解这里变化的原理)。 - -针对这个问题,OBD 提供两个解决思路: - -- 手动同步修改 OBD 集群部署配置文件中的参数值(以后工具可能会自动同步)。 - -- OBD 重启集群的时候不带参数启动节点进程。 - -您可使用命令 obd cluster edit-config 编辑集群部署配置文件,退出时会保存到上文的工作目录中。 - -```bash -obd cluster edit-config obce-single - -保存时输出: -oceanbase-ce-3.1.0 already installed. -Search param plugin and load ok -Parameter check ok -Save deploy "obce-single" configuration -deploy "need reload" -``` - -使用命令 edit-config 退出后会提示 reload 集群配置。 - -```bash -[admin@obce00 ~]$ obd cluster reload obce-single -Get local repositories and plugins ok -Open ssh connection ok -Cluster status check ok -Connect to observer ok -obce-single reload -``` - -> **说明** -> -> 如果 OBD 命令运行出错,可以运行命令 `tail -n 50 ~/.obd/log/obd` 查看日志。 - -### **进程启动时指定参数** - -前面说到 OBD 在启动集群节点进程 observer 时,会在命令行下通过 -o 指定参数。对于运维来说,如果某个节点的进程 observer 因为某种原因退出,启动进程是当务之急。可能需要调整某个参数再启动一次,通过 OBD 工具会导致效率低下。所以,掌握 OceanBase 集群节点进程 observer 的启动方法是很有必要的。 - -首先进入到工作目录。必须在上一次启动 observer 进程的工作目录(假设它是正确的)下再次尝试。前面分析过,工作目录在 OBD 集群部署配置文件中指定 home_path。本教程里工作目录都默认是 /home/admin/oceanbase-ce。进程 observer 启动后会在这个目录找目录 etc,找默认的参数文件 observer.config.bin。启动后的日志会默认写到 `log/{observer.log, rootservice.log, election.log}`。所以,工作目录不能错,目录的权限也不能错。 - -示例:不带参数启动进程 observer 。为了模拟故障,先强行杀掉进程 observer。 - -```bash -[admin@obce00 ~]$ cd -[admin@obce00 ~]$ cd oceanbase-ce/ -[admin@obce00 oceanbase-ce]$ kill -9 `pidof observer` -[admin@obce00 oceanbase-ce]$ sleep 3 -[admin@obce00 oceanbase-ce]$ ps -ef|grep observer -admin 35278 28904 0 11:26 pts/2 00:00:00 grep --color=auto observer -[admin@obce00 oceanbase-ce]$ pwd -/home/admin/oceanbase-ce -[admin@obce00 oceanbase-ce]$ bin/observer -bin/observer -[admin@obce00 oceanbase-ce]$ ps -ef|grep observer -admin 35280 1 99 11:26 ? 00:00:06 bin/observer -admin 35848 28904 0 11:26 pts/2 00:00:00 grep --color=auto observer -[admin@obce00 oceanbase-ce]$ netstat -ntlp -(Not all processes could be identified, non-owned process info - will not be shown, you would have to be root to see it all.) -Active Internet connections (only servers) -Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name -tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - -tcp 0 0 0.0.0.0:2881 0.0.0.0:* LISTEN 35280/bin/observer -tcp 0 0 0.0.0.0:2882 0.0.0.0:* LISTEN 35280/bin/observer -``` - -示例:带参数启动进程 observer。为了模拟故障,先强行杀掉进程 observer。 - -```bash -[admin@obce00 oceanbase-ce]$ kill -9 `pidof observer` -[admin@obce00 oceanbase-ce]$ sleep 3 -[admin@obce00 oceanbase-ce]$ bin/observer -o "max_syslog_file_count=15,datafile_size=60G" -bin/observer -o max_syslog_file_count=15,datafile_size=60G -optstr: max_syslog_file_count=15,datafile_size=60G -[admin@obce00 oceanbase-ce]$ ps -ef|grep observer -admin 35867 1 99 11:34 ? 00:00:09 bin/observer -o max_syslog_file_count=15,datafile_size=60G -admin 36435 28904 0 11:34 pts/2 00:00:00 grep --color=auto observer -[admin@obce00 oceanbase-ce]$ netstat -ntlp -(Not all processes could be identified, non-owned process info - will not be shown, you would have to be root to see it all.) -Active Internet connections (only servers) -Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name -tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - -tcp 0 0 0.0.0.0:2881 0.0.0.0:* LISTEN 35867/bin/observer -tcp 0 0 0.0.0.0:2882 0.0.0.0:* LISTEN 35867/bin/observer -``` - -## **租户参数** - -### **通过 SYS 租户修改业务租户参数** - -上一节介绍了 OceanBase 集群参数设置,其中有部分参数生效范围是租户(TENANT)。在 OceanBase 内部租户(sys)里,可以修改业务实例的部分参数。比如参数 writing_throttling_trigger_percentage,用于对指定租户进行内存限流(增量内存使用率达到这个阈值就对写入降速)。 - -```sql -MySQL [oceanbase]> show parameters like 'writing_throttling_trigger_percentage%'\G -*************************** 1. row *************************** - zone: zone1 - svr_type: observer - svr_ip: x.x.x.x - svr_port: 2882 - name: writing_throttling_trigger_percentage - data_type: NULL - value: 100 - info: the threshold of the size of the mem store when writing_limit will be triggered. Rang:(0, 100]. setting 100 means turn off writing limit - section: TRANS - scope: TENANT - source: DEFAULT -edit_level: DYNAMIC_EFFECTIVE -1 row in set (0.002 sec) - -MySQL [oceanbase]> alter system set writing_throttling_trigger_percentage = 90 tenant='obmysql'; -Query OK, 0 rows affected (0.011 sec) -``` - -修改后的参数值只能在对应租户里查看。 - -```sql -$ mysql -h x.x.x.x -u root@obmysql -P 2881 -p -c -A oceanbase -Enter password: -Welcome to the MariaDB monitor. Commands end with ; or \g. -Your MySQL connection id is 3221538749 -Server version: 5.7.25 OceanBase 3.1.0 (r3-b20901e8c84d3ea774beeaca963c67d7802e4b4e) (Built Aug 10 2021 08:10:38) - -Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. - -Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. - -MySQL [oceanbase]> show parameters like 'writing_throttling_trigger_percentage%'\G -*************************** 1. row *************************** - zone: zone1 - svr_type: observer - svr_ip: x.x.x.x - svr_port: 2882 - name: writing_throttling_trigger_percentage - data_type: NULL - value: 90 - info: the threshold of the size of the mem store when writing_limit will be triggered. Rang:(0, 100]. setting 100 means turn off writing limit - section: TRANS - scope: TENANT - source: DEFAULT -edit_level: DYNAMIC_EFFECTIVE -1 row in set (0.004 sec) -``` - -### **修改业务租户参数** - -在业务租户里,可以自己设置参数。比如,参数 writing_throttling_maximum_duration,用于控制增量内存的剩余内存根据当前写入速度的最长写入时间。触发写入限速后,剩余 memstore 的内存量预期在 writing_throttling_maximum_duration 时间内分配完。 - -> **说明** -> -> 该参数仅供参考,准确性不及参数 `writing_throttling_trigger_percentage`。 - -```sql -## 在业务租户里修改参数,后面就不需要指定租户名。 - -MySQL [oceanbase]> alter system set writing_throttling_maximum_duration = '2h'; -Query OK, 0 rows affected (0.006 sec) - -MySQL [oceanbase]> show parameters like 'writing_throttling_maximum_duration'\G -*************************** 1. row *************************** - zone: zone1 - svr_type: observer - svr_ip: x.x.x.x - svr_port: 2882 - name: writing_throttling_maximum_duration - data_type: NULL - value: 2h - info: maximum duration of writing throttling(in minutes), max value is 3 days - section: TRANS - scope: TENANT - source: DEFAULT -edit_level: DYNAMIC_EFFECTIVE -1 row in set (0.004 sec) -``` - -### **修改业务租户变量** - -OceanBase 租户还有一个名为变量(VARIABLE)的设计,这个和 MySQL 实例很像。变量其实就是租户的参数。可以在租户全局层面修改,也可以在会话层面修改,很多变量和对应的 SQL HINT 还可在语句级别修改。 - -全局层面的修改影响的是后续的会话,会话层面的修改仅影响当前会话,语句级别的修改只影响当前语句。 - -初次使用 OceanBase 租户时,建议调整租户的几个超时参数。 - -- ob_query_timeout:语句执行超时时间,单位 us,默认值是 10000000 (即 10s)。建议根据业务 SQL 的平均执行时间水平调整。OLTP 场景调整小一些,OLAP 场景调整大一些。初学者建议调大 10 倍。 - -- ob_trx_idle_timeout:事务空闲超时时间,单位 us,默认值是 120000000(即 120s)。建议根据业务事务平均空闲时间水平调整。空闲事务会占用连接,并可能持有锁不释放,导致高并发时阻塞和死锁概率增加,不建议调大。 - -- ob_trx_timeout:事务未提交超时时间,单位 us,默认值是 100000000 (即 100s)。建议根据业务事务平均持续时间水平调整。事务长期不提交,会占用连接、可能持有锁不释放,导致高并发时阻塞和死锁概率增加,不建 -议调大。如果是后台跑批业务,建议在会话级别调大。 - -- ob_trx_lock_timeout:事务申请加锁等待超时时间,单位 us,默认值是 -1,即不控制。超时依然会受 ob_query_timeout 限制。当调大语句超时时间变量(ob_query_timeout)后,可以将这个锁等待超时改为 10000000 (即 10s),以减少阻塞和死锁的概率。 - -您可运行以下命令查看和修改变量: - -```sql -show global | session variables like '%变量名部分字段%' ; - -set global | session 变量名 = '变量值' ; -``` - -示例: - -```sql -MySQL [oceanbase]> show global variables like '%timeout%'; -+---------------------+------------------+ -| Variable_name | Value | -+---------------------+------------------+ -| connect_timeout | 10 | -| interactive_timeout | 28800 | -| lock_wait_timeout | 31536000 | -| net_read_timeout | 30 | -| net_write_timeout | 60 | -| ob_pl_block_timeout | 3216672000000000 | -| ob_query_timeout | 10000000 | -| ob_trx_idle_timeout | 120000000 | -| ob_trx_lock_timeout | -1 | -| ob_trx_timeout | 100000000 | -| wait_timeout | 28800 | -+---------------------+------------------+ -11 rows in set (0.002 sec) - -MySQL [oceanbase]> set global ob_query_timeout = 100000000; -Query OK, 0 rows affected (0.015 sec) - -MySQL [oceanbase]> set global ob_trx_timeout = 1000000000; -Query OK, 0 rows affected (0.014 sec) - -MySQL [oceanbase]> set global ob_trx_idle_timeout = 1200000000; -Query OK, 0 rows affected (0.010 sec) - -MySQL [oceanbase]> SET GLOBAL ob_trx_lock_timeout=10000000; -Query OK, 0 rows affected (0.011 sec) -``` - -对于复杂的 SQL 场景或者 OLAP 场景,租户还需要调整 ob_sql_work_area_percentage 变量。该变量影响 SQL 里排序统计能利用的内存大小,可以根据情况进行调整。 - -```sql -set global ob_sql_work_area_percentage=50; -``` - -### **通过 SYS 租户修改业务租户变量** - -> **注意** -> -> 部分变量属于租户初始化变量,不能在业务租户里直接修改,需要在 sys 租户里修改。 - -示例: - -```sql -$ mysql -h x.x.x.x -uroot@obmysql#obdemo -P2883 -p****** -c -A oceanbase -Ns -MySQL [oceanbase]> set global lower_case_table_names=0; -ERROR 1238 (HY000): Variable 'lower_case_table_names' is a read only variable - -$mysql -h x.x.x.x -uroot@sys#obdemo -P2883 -p****** -c -A oceanbase -Ns -MySQL [oceanbase]> alter tenant obmysql set variables lower_case_table_names=0; - -$ mysql -h x.x.x.x -uroot@obmysql#obdemo -P2883 -p****** -c -A oceanbase -Ns -MySQL [oceanbase]> show global variables like 'lower_case_table_names'; -lower_case_table_names 0 -``` - -有些变量比较特殊,比如: - -- 变量 ob_tcp_invited_nodes,表示租户访问 IP 白名单。初始化租户的时候在 sys 租户中设置,后期可以在业务租户里修改。 - - ```sql - set global ob_tcp_invited_nodes='x.x.x.x/16,127.0.0.1'; - ``` - - 如果业务租户设置错误导致无法登录,可以通过 sys 租户再改回正确值。 - -- 变量 ob_compatibility_mode 表示租户兼容性。这个在租户创建时指定,后期不能修改。 diff --git a/docs/user_manual/user_best_practices/performance_tuning/_index.md b/docs/user_manual/user_best_practices/performance_tuning/_index.md deleted file mode 100644 index 9595e4896..000000000 --- a/docs/user_manual/user_best_practices/performance_tuning/_index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 性能调优 -bookCollapseSection: false -weight: 6 ---- diff --git a/docs/user_manual/user_best_practices/performance_tuning/comprehensive_sql_optimize.md b/docs/user_manual/user_best_practices/performance_tuning/comprehensive_sql_optimize.md deleted file mode 100644 index 42e943b3c..000000000 --- a/docs/user_manual/user_best_practices/performance_tuning/comprehensive_sql_optimize.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: SQL 性能调优 —— 调优综合实践 -weight: 5 ---- -  这篇内容更像是对前两篇 SQL 性能调优中内容的综合应用,总结了性能优化的方法,并通过例子展示了分析性能瓶颈的具体步骤。 - -# SQL 调优综合实践 -先给出性能优化方法和分析性能瓶颈步骤的文字描述: - -## 性能优化的方法 - -1. 开启并行执行等机制(简单),可以参考:《[OceanBase 并行执行技术](https://open.oceanbase.com/blog/5558373888)》。这篇博客内容过多(实际应该像性能调优系列拆成多篇发的),从 4.2 版本开始,OB 已经支持了 auto dop,如果用户不熟悉并行度的设置规则,可以设置 parallel_degree_policy 为 AUTO,让优化器帮忙自动选择合适的并行度,推荐使用。auto dop 的相关内容可以直接在上面这篇博客中搜索 parallel_degree_policy 关键字,或者参考[官网文档](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000220672)。 - -2. 创建合适的索引(简单),可以参考:《[性能调优学习笔记 1 —— 索引调优](https://open.oceanbase.com/blog/6735590448)》。 - -3. 调整连接方式(比较简单),可以参考:《[性能调优学习笔记 2 —— 连接调优](https://open.oceanbase.com/blog/6752926064)》 - -4. 调整连接顺序(难度较大),这里指的是:例如有 t1,t2,t3 三个表做连接, 假设 OB 的优化器认为 t1,t2 两个表先做连接,再与 t3 做连接,是一个比较好的计划。但是实际可能是 t1 和 t3 先做连接,再和 t2 做连接是更优的计划。此时可以通过 hint 告诉优化器正确的连接顺序来优化 SQL 的性能(参考[官网文档](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000222740)),在一些复杂场景下,需要丰富的经验支持才能通过调整连接顺序来优化 SQL 执行效率,有兴趣的同学可以自行研究和尝试。 - -5. 检查 OB 是否做了错误的查询改写 / 缺少合适的查询改写机制(难度较大)。 - -## 分析性能瓶颈的步骤 - -1. 利用 SQL 执行计划去分析具体哪些步骤(哪几个算子)的执行时间慢。个人经验是可以通过把大 SQL 拆分成小 SQL 去分析这一步。 -2. 充分利用已有的脚本和工具来简化分析过程。这里我理解主要是通过一些字典视图,例如 oceanbase.GV$SQL_PLAN_MONITOR。 - -## 分析 SQL 性能瓶颈的例子 -  举一个真实的 SQL 性能分析和优化的例子:下面这条 SQL 执行了 2.43s,接下来开始分析性能瓶颈并进行优化。
-![image.png](/img/performance_tuning/comprehensive_sql_optimize/1.png) - -  看到优化器生成计划是让 bbtr 表先与 cte 表做 merge join,再与 btr 表做 nest loop join。
-![image.png](/img/performance_tuning/comprehensive_sql_optimize/2.png) -
-  在上面的计划中,从 4 号算子到 8 号算子是三张表的 join 是这个计划最核心的部分,大概率也是性能的瓶颈点,我们先从这里开始分析。 -``` -============================= -|ID|OPERATOR |NAME| ------------------------------ -|4 |NESTED-LOOP JOIN | | -|5 |├─MERGE JOIN | | -|6 |│ └─TABLE SCAN |BBTR| -|7 |│ └─TABLE SCAN |CTE | -|8 |└─TABLE GET |BTR | -============================= -``` -  要分析出计划里哪里是瓶颈,首先得查一下每个表的数据量,先看最内层进行 merge join 的两张表 cte 和 bbtr,merge join 的代价是扫描出 cte 数据的代价 + 扫描出 bbtr 的代价 + 归并的代价:
cte 表在 7 号算子中的过滤条件是 cte.bpo_send_flag = '0',过滤之后返回数据量是 1638 行,扫描耗时 2.13 秒(这个时间明显不太对)。
-![image.png](/img/performance_tuning/comprehensive_sql_optimize/3.png) -
-  类似地,bbtr 表的数据在 6 号算子返回的数据量是 40 多万行,没有过滤条件,扫描耗费 0.19 秒。
-![image.png](/img/performance_tuning/comprehensive_sql_optimize/4.png) -
-  然后上层的 NLJ 要拿 merge join 的结果当做驱动表(左表),对右表 btr 进行 table get。
  这条 SQL 一共执行了 2.43 秒,但仅仅是 merge join 中 cte 表的扫描代价就已经高达 2.13 秒了,所以这条 SQL 的瓶颈点就是 cte 表的扫描。
  可以看到 cte 表上有一个过滤条件 bpo_send_flag = '0',所以我们可以通过在 cte 表的列 bpo_send_flag 上建一个索引来优化它的查询性能。如果考虑到计划中的 7 号算子还需要拿 cte 表中的 bpo_send_time 列和 claim_tpa_id 列的数据向上吐行,还可以考虑在(bpo_send_flag, bpo_send_time, claim_tpa_id)上创建联合索引来消除索引回表的性能消耗。
  创建索引之后的计划预期大概会长这样: -``` -================================= -|ID|OPERATOR |NAME | ---------------------------------- -|4 |NESTED-LOOP JOIN | | -|5 |├─MERGE JOIN | | -|6 |│ └─TABLE SCAN |BBTR | -|7 |│ └─TABLE SCAN |CTE(idx)| -|8 |└─TABLE GET |BTR | -================================= -``` -  假如上面排查下来发现 cte 表的扫描并不是瓶颈,那么应该做进一步的分析。例如尝试去单独去执行 bbtr 和 cte 两个表的连接,查看它的执行结果的行数和 btr 表的行数关系。4 号 NLJ 算子的左支返回的行数(merge join 结果的行)和右支返回的行数(btr 通过 8 号算子中 filter 过滤之后的行)如果没有明显的大小表关系(左支行数 / 右支行数 < 20),则意味着 4 号算子选择 Hash Join 的方式会比选择 NLJ 的方式更优。那么就可以通过使用 [hint](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000222744) /*+ leading(bbtr cte btr) use_hash(btr) */ 来修改 4 号算子的连接方式,将 Nested_Loop Join 改成 Hash Join。 - -# hint 和 outline - -## 使用 hint 生成指定计划 -  官网上写的很详细,这里不再赘述,详见 [OB 官网中的 hint 部分](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000222337):
-![image.png](/img/performance_tuning/comprehensive_sql_optimize/5.png) - -## 使用 outline 进行计划绑定 -  官网上写的很详细,这里不再赘述,详见 [OB 官网中的计划绑定部分](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000220954):
-![image.png](/img/performance_tuning/comprehensive_sql_optimize/6.png) \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/performance_tuning/explain.md b/docs/user_manual/user_best_practices/performance_tuning/explain.md deleted file mode 100644 index d654db82b..000000000 --- a/docs/user_manual/user_best_practices/performance_tuning/explain.md +++ /dev/null @@ -1,254 +0,0 @@ ---- -title: 执行计划 -weight: 1 ---- -# **执行计划** - -执行计划是对一条 SQL 查询语句在数据库中执行过程的描述,通常用于分析某条 SQL 的性能问题,读懂执行计划是 SQL 优化的先决条件。本文介绍如何查看 SQL 的逻辑执行计划和实际执行计划,并介绍一些常用的执行计划算子。 - -## **逻辑执行计划** - -使用 EXPLAIN 命令可以查看优化器针对指定 SQL 生成的逻辑执行计划。EXPLAIN 命令完整的语法如下: - -```sql -{EXPLAIN | DESCRIBE | DESC} [explain_type] dml_statement; - -explain_type: - BASIC - | OUTLINE - | EXTENDED - | EXTENDED_NOADDR - | PARTITIONS - | FORMAT = {TRADITIONAL| JSON} - -dml_statement: - SELECT statement - | DELETE statement - | INSERT statement - | REPLACE statement - -``` - -其中,FORMAT 有 TRADITIONAL 和 JSON 两种格式,默认是 TRADITIONAL 格式,可读性更好,JSON 格式对程序解析比较友好。 - -先看一个简单的 SQL 执行计划格式: - -```sql -obclient> EXPLAIN - -> SELECT count(*) FROM BMSQL_ITEM \G -*************************** 1. row *************************** -Query Plan: =============================================== -|ID|OPERATOR |NAME |EST. ROWS|COST | ------------------------------------------------ -|0 |SCALAR GROUP BY| |1 |78754| -|1 | TABLE SCAN |BMSQL_ITEM|99995 |59653| -=============================================== - -Outputs & filters: -------------------------------------- - 0 - output([T_FUN_COUNT(*)]), filter(nil), - group(nil), agg_func([T_FUN_COUNT(*)]) - 1 - output([1]), filter(nil), - access([BMSQL_ITEM.I_ID]), partitions(p0) - -1 row in set (0.01 sec) -``` - -执行计划的输出展示分为两部分: - -- 第一部分是用表格形式展示执行计划这棵树。每行是一个独立的操作,操作符是 OPERATOR,操作有 ID。操作展示可能会缩进。缩进表示是内部操作,可以嵌套。执行顺序遵循由内到外,由上到下。操作符支持的内容也是 SQL 引擎成熟度的一个体现。 - - - OPERATOR:表示操作算子的名称,TABLE SCAN 是常用操作算子,表示扫描。 - - - NAME:表示算子操作的对象。可以是表名、索引名、内部临时视图名。需要注意的是,如果扫描主键,依然展示表名。因为 OceanBase 数据库里的表和索引的本质都是索引组织表,表数据跟主键索引是一个概念。 - - - EST. ROWS:执行当前算子输出的行数,跟统计信息有关。OceanBase 数据库里表的统计信息目前只有在集群合并的时候才更新。 - - - COST:执行当前算子预估的成本。COST 计算比较复杂,暂时先不深入。 - -- 第二部分的内容跟第一部分有关,主要是描述第一部分算子的具体信息。 - - - output:表示当前算子输出的表达式(包含列)。 - - - filter:表示当前算子的过滤表达式,nil 表示无。如果当前算子是访问存储层,这个过滤表达式可以下推(push)。 - -在 OceanBase 数据库内部,这个结果是以 JSON 格式存储。示例如下: - -```json -{ - "ID": 1, - "OPERATOR": "SCALAR GROUP BY", - "NAME": "SCALAR GROUP BY", - "EST.ROWS": 1, - "COST": 78754, - "output": [ - "T_FUN_COUNT(*)" - ], - "CHILD_1": { - "ID": 0, - "OPERATOR": "TABLE SCAN", - "NAME": "TABLE SCAN", - "EST.ROWS": 99995, - "COST": 59653, - "output": [ - "1" - ] - } -} -``` - -该 JSON 内容描述的是一个树,对普通用户可读性不好。 - -## **实际执行计划** - -我们可以通过如下命令查看 SQL 实际执行计划,查看 SQL 的实际执行计划要求 SQL 被执行过。 - -运行下面两个 SQL,查看 SQL 审计视图,获取执行节点和 PLAN_ID 信息。 - -```sql -select o.o_w_id , o.o_d_id ,o.o_id , i.i_name ,i.i_price ,o.o_c_id from bmsql_oorder o , bmsql_item i where o.o_id = i.i_id and o.o_w_id = 3 limit 10 ; - -select o.o_w_id , o.o_d_id ,o.o_id , i.i_name ,i.i_price ,o.o_c_id from bmsql_item i , bmsql_oorder o where o.o_id = i.i_id and o.o_w_id = 3 limit 10 ; - -SELECT /*+ read_consistency(weak) ob_querytimeout(100000000) */ substr(usec_to_time(request_time),1,19) request_time_, s.svr_ip, s.client_Ip, s.sid,s.tenant_id, s.tenant_name, s.user_name, s.db_name, s.query_sql, s.plan_id, s.plan_type, s.affected_rows, s.return_rows, s.ret_code, s.event, s.elapsed_time, s.queue_time, s.execute_time -FROM oceanbase.gv$ob_sql_audit s -WHERE 1=1 and s.tenant_id = 1002 - and user_name='u_tpcc' and query_sql like 'select o.o_w_id%' - and request_time >= time_to_usec(date_sub(CURRENT_TIMESTAMP, interval 5 minute )) -ORDER BY request_time DESC -LIMIT 10 \G - -# 输出: - -*************************** 1. row *************************** -request_time_: 2021-10-05 11:24:50 - svr_ip: x.x.x.x - client_Ip: x.x.x.x - sid: 3221668666 - tenant_id: 1002 - tenant_name: obmysql - user_name: u_tpcc - db_name: tpccdb - query_sql: select o.o_w_id , o.o_d_id ,o.o_id , i.i_name ,i.i_price ,o.o_c_id from bmsql_item i , bmsql_oorder o where o.o_id = i.i_id and o.o_w_id = 3 limit 10 - plan_id: 3305 - plan_type: 3 -affected_rows: 0 - return_rows: 10 - ret_code: 0 - event: default condition wait - elapsed_time: 20058 - queue_time: 73 - execute_time: 19726 -*************************** 2. row *************************** -request_time_: 2021-10-05 11:24:46 - svr_ip: x.x.x.x - client_Ip: x.x.x.x - sid: 3222238517 - tenant_id: 1002 - tenant_name: obmysql - user_name: u_tpcc - db_name: tpccdb - query_sql: select o.o_w_id , o.o_d_id ,o.o_id , i.i_name ,i.i_price ,o.o_c_id from bmsql_oorder o , bmsql_item i where o.o_id = i.i_id and o.o_w_id = 3 limit 10 - plan_id: 273 - plan_type: 3 -affected_rows: 0 - return_rows: 10 - ret_code: 0 - event: system internal wait - elapsed_time: 141562 - queue_time: 48 - execute_time: 139714 -2 rows in set (0.119 sec) -``` - -其中 tenant_id、svr_ip、svr_port 和 plan_id 列信息很重要。查看视图 gv$ob_plan_cache_plan_explain 需要这些字段信息。 - -如果是在网页上,且以上输出结果格式化正确,对比 2 个 SQL 的实际执行计划可以看出分别是对那个表进行远程访问。 - -除了通过 SQL 审计视图定位具体的 SQL 及其执行计划外,还可以通过查看缓存的执行计划汇总视图 gv$ob_plan_cache_plan_stat。 - -```sql -SELECT s.tenant_id, svr_ip,plan_Id,sql_id,TYPE, query_sql, first_load_time, avg_exe_usec, slow_count,executions, slowest_exe_usec,s.outline_id -FROM oceanbase.`gv$ob_plan_cache_plan_stat` s -WHERE s.tenant_id = 1002 -- 改成具体的 tenant_id -ORDER BY avg_exe_usec desc limit 10 -; -``` - -从这个视图里可以看到全局的 SQL 执行汇总。适合找 TOP N 慢 SQL。根据里面的节点信息、SQLID 和 PLANID 信息,既可以到 SQL 审计视图里定位具体的 SQL 信息,也可以查看实际运行的执行计划信息。 - -执行计划可以清空,命令如下: - -```sql -ALTER SYSTEM flush plan cache GLOBAL; -``` - -仅用于测试环境研究,生产环境的 SQL 执行计划缓存通常不可随便清空。清空执行计划会导致所有 SQL 要重新进行一次硬解析。 - -## **常见执行计划算子** - -**TABLE GET** - -表示主键直接等值访问,后面接表名。OceanBase 数据库里主键就是表数据。 - -**TABLE SCAN** - -表示全表扫描、主键扫描或索引扫描。具体需根据该执行计划算子后面的操作对象名是表还是索引判断。 - -> **注意** -> -> 主键扫描时执行计划算子后面跟的操作对象也是表名。 - -**TOP-N SORT** - -常用的场景排序后可能只返回最大或最小的前 N 条记录。 - -**NESTED-LOOP JOIN** - -这个算法的整体性能取决于外部表返回的记录数(循环的次数)和内部表的查询性能。 - -个人经验是小表作为外部表,大表作为内部表。不过实际并不是按照表的大小区分,而是由过滤条件应用后的结果集大小来定。可以对比下面 SQL 的执行计划。 - -**MERGE JOIN** - -MERGE JOIN 主要用于两个不是很小或很大的结果集的连接,它们没有有效的过滤条件或者这个条件上没有合适的索引。 - -MERGE JOIN 算法基本分两大阶段: - -- 排序,将两个结果集分别按连接字段排序。 - -- 合并,分别从两个结果集里读取记录,进行比较、遍历等。 - -如果结果集本来就是有序的,那么第一阶段可以优化。MERGE JOIN 可以用于等值运算,也可以用于不等值运算(小于、大于、小于等于、大于等于)。 - -MERGE JOIN 主要利用数据主键或者索引的有序,此时它的性能有可能会更好。数据量非常大的时候,MERGE JOIN 性能并不是很好,要设法规避。 - -**HASH JOIN** - -HASH JOIN 用于两个比较大的结果集之间的连接,通常没有比较好的过滤条件或者过滤条件上没有索引。 - -> **说明** -> -> - HASH JOIN 也分外部表和内部表,内部表是 probe table,外部表是 hash table。通常数据库会挑选结果集相对小的表作为外部表,并在连接条件上用哈希函数构建 hash table,然后循环遍历 probe table,对连接条件列用哈希函数,探测是否在 hash table 中存在,如果存在,则返回匹配的记录。该算子和 NESTED-LOOP JOIN 很类似,不同之处是 HASH JOIN 会在连接条件列上用哈希函数,并在内存中构建 hash table。 -> -> - OceanBase 优化器一次能构建的最大 hash table 受内部参数(_hash_table_size)限制。如果外部表的结果集比这个大,就需要分多次构建 hash table,这个也叫 multiple pass,会涉及到一些内存和文件数据交换,以及多次哈希探测,性能相对会下降一些。 -> -> - HASH JOIN 的细节比较复杂,此处不详细讨论。目前只要能识别出 HASH JOIN,以及掌握产生后如何规避 HASH JOIN 算法。 - -**SUBPLAN SCAN 和 COUNT** - -算子 SUBPLAN SCAN 跟 TABLE SCAN 类似,不同的是: - -- SUBPLAN SCAN 是从视图(包括内部临时生成的)里读取数据。 - -- TABLE SCAN 是从基表(或者索引)里扫描数据。 - -**EXCHANGE IN|OUT REMOTE** - -首先看要访问表的主副本节点,然后直连另外一个节点。人为构造一个远程执行计划。 - -> **说明** -> -> Exchange 算子是分布式场景下,用于线程间进行数据交互的算子。它一般成对出现,数据源端有一个 out 算子,目的端会有一个 in 算子。 - -实际上业务都是通过 OBProxy 连接,能正确路由到 OBServer 节点上,很大程度规避了远程执行计划,不过并不能从根本上避免。后面还会举例说明。 diff --git a/docs/user_manual/user_best_practices/performance_tuning/index_optimize.md b/docs/user_manual/user_best_practices/performance_tuning/index_optimize.md deleted file mode 100644 index 8f49b26a1..000000000 --- a/docs/user_manual/user_best_practices/performance_tuning/index_optimize.md +++ /dev/null @@ -1,370 +0,0 @@ ---- -title: SQL 性能调优 —— 索引调优 -weight: 3 ---- -  调优相关的内容主要分为三个部分:索引调优、连接(join)方式调优、综合调优实践。这篇内容是最简单、最基础,同时也最常用的“索引调优”。 - -  这篇文章不涉及 OceanBase 改写、优化和执行的内核代码实现,也不涉及一条 SQL 从 parser、resolver 到计算出最终结果的相关原理。**只有数据库使用者最为关心的调优实践内容。** - -  阅读前建议先去了解一下在 OB 中如何通过 explain 阅读执行计划,例如可以先阅读一下庆涛写的这一系列博客:[https://open.oceanbase.com/blog/1100214](https://open.oceanbase.com/blog/1100214)。因为本篇博客涉及到的计划都超级简单,靠猜也能猜个八九不离十,所以如果实在懒得去了解 explain,也是 OK 的。 - -# 索引调优 -  当我们发现某一条 SQL 存在性能问题时,我们可以通过很多方式对这条 SQL 进行优化,其中最常见的是索引调优。索引调优通过为数据表创建合适的索引来达到减少数据扫描量,消除排序等目的。索引调优是一种比较简单的调优方式,也是 SQL 出现性能问题时通常在第一时间考虑的优化方式。在单表扫描场景下创建一个合适的索引往往可以极大地提高 SQL 的执行性能。
  所以在建索引前,我们需要考虑是否有必要建索引、应该在哪些列上建索引、索引列的顺序应该怎样安排。接下来就会记录下 OceanBase 中索引的一些基础知识,以及创建合适的索引的方法。 - -# OceanBase 索引的基础知识 -  OceanBase 的索引中除了有索引键,还会包含主表的主键。因为在使用索引的时候,需要通过主表的主键去关联索引中的某一行与主表中的某一行。也就意味着索引表中需要包含主表的主键才能去反向查找定位主表中的具体某一行(OceanBase 中常把这个操作叫做索引回表),因此需要把主表的主键加到索引表里面。
  我们可以简单做一个实验看下,创建一个索引叫 idx_b。 -``` -create table test(a int primary key, b int, c int, key idx_b(b)); -``` -  再去 oceanbase.__all_column 中查询一下这个索引有哪些列,可以看到虽然这个索引创建在 b 列上,但是这个索引中还包含了主表的主键列 a 列。 -``` -select - column_id, - column_name, - rowkey_position, - index_position -from - oceanbase.__all_column -where - table_id = ( - select - table_id - from - oceanbase.__all_table - where - data_table_id = ( - select - table_id - from - oceanbase.__all_table - where - table_name = 'test' - ) - ); -+-----------+-------------+-----------------+----------------+ -| column_id | column_name | rowkey_position | index_position | -+-----------+-------------+-----------------+----------------+ -| 16 | a | 2 | 0 | -| 17 | b | 1 | 1 | -+-----------+-------------+-----------------+----------------+ -2 rows in set (0.045 sec) -``` - -# 索引的几个作用 -查询时走索引相对于走主表有三个优势: - -1. 可以根据索引列的条件去快速定位数据,来减少数据的扫描量。 -2. 索引列是有序的,可以利用此特性消掉一些排序操作。 -3. 索引一般比主表小,如果过滤条件的过滤性好或者查询的列数较少,可以少扫描一些数据。 - -## 快速定位数据 -  索引的第一个优势是快速定位数据。可以将索引列上的过滤条件转化成索引扫描的开始位置和结束位置。在实际扫描的时候,只需要从开始位置一直扫描到结束位置,两个位置之间的数据就是满足索引列上的过滤条件的数据。扫描的开始位置到结束位置称为 query range。**这里需要记住的一个重要规则就是:索引可以从开头匹配多个等值谓词,直到匹配到第一个范围谓词为止。**
  举个例子,在 test 表上有个索引 b、c, 按照前文提到的内容,其实它是 b、c、a 的索引,因为 a 是主键。 -``` -create table test(a int primary key, b int, c int, d int, key idx_b_c(b, c)); -``` -  接下来执行几条 SQL,看看这些 SQL 能否充分利用这个索引。
  下面这条 SQL 有一个 b=1 的过滤条件,对应的 query range 是 (1,MIN,MIN ; 1,MAX,MAX)。即要从 b=1,c=min,a=min 向量点开始,一直扫到 b=1, c=max,a=max 向量点。这两个向量点之间所有数据都满足 b=1条件,不需要再使用 b=1 过滤条件去过滤。 -``` -explain select /*+index(test idx_b_c)*/ * from test where b = 1; -+-------------------------------------------------------------------------------+ -| Query Plan | -+-------------------------------------------------------------------------------+ -| ========================================================= | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| --------------------------------------------------------- | -| |0 |TABLE RANGE SCAN|test(idx_b_c)|1 |5 | | -| ========================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b], [test.c], [test.d]), filter(nil), rowset=256 | -| access([test.a], [test.b], [test.c], [test.d]), partitions(p0) | -| is_index_back=true, is_global_index=false, | -| range_key([test.b], [test.c], [test.a]), range(1,MIN,MIN ; 1,MAX,MAX), | -| range_cond([test.b = 1]) | -+-------------------------------------------------------------------------------+ -``` -  下面这条 SQL 有一个 b > 1 的过滤条件,和上面那条 SQL 类似,不赘述。 -``` -explain select /*+index(test idx_b_c)*/ * from test where b > 1; -+---------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------+ -| ========================================================= | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| --------------------------------------------------------- | -| |0 |TABLE RANGE SCAN|test(idx_b_c)|1 |5 | | -| ========================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b], [test.c], [test.d]), filter(nil), rowset=256 | -| access([test.a], [test.b], [test.c], [test.d]), partitions(p0) | -| is_index_back=true, is_global_index=false, | -| range_key([test.b], [test.c], [test.a]), range(1,MAX,MAX ; MAX,MAX,MAX), | -| range_cond([test.b > 1]) | -+---------------------------------------------------------------------------------+ -``` -  下面这条 SQL 的过滤条件是 b=1,c>1,对应的 query range 是 (1,1,MAX ; 1,MAX,MAX),range_cond 是 ([test.b = 1], [test.c > 1]),因为第一个谓词是等值条件 b = 1,所以索引还会继续向后匹配,直到出现第一个范围条件 c > 1。 -``` -explain select/*+index(test idx_b_c)*/ * from test where b = 1 and c > 1; -+-------------------------------------------------------------------------------+ -| Query Plan | -+-------------------------------------------------------------------------------+ -| ========================================================= | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| --------------------------------------------------------- | -| |0 |TABLE RANGE SCAN|test(idx_b_c)|1 |5 | | -| ========================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b], [test.c], [test.d]), filter(nil), rowset=256 | -| access([test.a], [test.b], [test.c], [test.d]), partitions(p0) | -| is_index_back=true, is_global_index=false, | -| range_key([test.b], [test.c], [test.a]), range(1,1,MAX ; 1,MAX,MAX), | -| range_cond([test.b = 1], [test.c > 1]) | -+-------------------------------------------------------------------------------+ -12 rows in set (0.041 sec) -``` -  下面这条 SQL 的过滤条件是 b>1,c>1。query range 在索引上抽 range 的时候,只能抽到第一个范围谓词为止。比如说这里 b>1,c>1,发现索引的第一列就被用来当范围谓词了,那么往后再出现任何的等值条件或范围条件,都不能再抽取 range。因此,此 SQL 对应的 query range 是 (1,MAX,MAX ; MAX,MAX,MAX),因为这里是用两个向量点去描述起始和结束位置,然而两个向量点是无法精确地描述出多个范围条件的。看下面计划中 range_cond 是 ([test.b > 1]),表明这条 SQL 在索引上也只完成了 b > 1 这个条件的过滤,索引回表(is_index_back=true)之后,还需要再对 c > 1 进行一次过滤(filter([test.c > 1]))。 -``` -explain select /*+index(test idx_b_c)*/ * from test where b > 1 and c > 1; -+----------------------------------------------------------------------------------------+ -| Query Plan | -+----------------------------------------------------------------------------------------+ -| ========================================================= | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| --------------------------------------------------------- | -| |0 |TABLE RANGE SCAN|test(idx_b_c)|1 |3 | | -| ========================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b], [test.c], [test.d]), filter([test.c > 1]), rowset=256 | -| access([test.a], [test.b], [test.c], [test.d]), partitions(p0) | -| is_index_back=true, is_global_index=false, filter_before_indexback[true], | -| range_key([test.b], [test.c], [test.a]), range(1,MAX,MAX ; MAX,MAX,MAX), | -| range_cond([test.b > 1]) | -+----------------------------------------------------------------------------------------+ -``` - -## 消除排序的开销 -**索引本身是有序的,可以利用此特性来消除排序的开销。**
  下面举几个简单的例子:
  还是先创建一张表。 -``` -create table test(a int primary key, b int, c int, d int, key idx_b_c_d(b, c, d)); -``` -  下面这条 SQL 是 b=1 order by c。 这条 SQL 用到了索引 idx_b_c_d,在计划中可以看到只有 table scan 算子而没有 sort 算子,说明索引回表后不需要再对 c 列进行排序。因为索引是按照 b、c、d、a 有序,但在扫描结果中,b 是一个常量 1,那么返回数据本身就是按照 c、d、a 有序的, order by c 自然也就不需要通过 sort 算子进行排序了。注意这里 is_index_back=false 说明索引扫描完成之后不需要回表,直接就可以输出结果,因为索引里已经包含了所查询的所有列。 -``` -explain select /*+index(test idx_b_c_d)*/ * from test where b = 1 order by c; -+-------------------------------------------------------------------------------------------------+ -| Query Plan | -+-------------------------------------------------------------------------------------------------+ -| =========================================================== | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| ----------------------------------------------------------- | -| |0 |TABLE RANGE SCAN|test(idx_b_c_d)|1 |2 | | -| =========================================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b], [test.c], [test.d]), filter(nil), rowset=256 | -| access([test.a], [test.b], [test.c], [test.d]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([test.b], [test.c], [test.d], [test.a]), range(1,MIN,MIN,MIN ; 1,MAX,MAX,MAX), | -| range_cond([test.b = 1]) | -+-------------------------------------------------------------------------------------------------+ -``` -  下面这条 SQL 就需要排序了。这里多了一个 or,看计划会通过索引去扫描出两批数据(因为 range 有两个 (1,MIN,MIN,MIN ; 1,MAX,MAX,MAX) 和 (2,MIN,MIN,MIN ; 2,MAX,MAX,MAX)),虽然两批数据内部都是有序的,但是两批数据之间却是无序的,所以在 table scan 上层还会再分配一个 sort 算子用于对 c 列进行排序。 -``` -explain select /*+index(test idx_b_c_d)*/ * from test where b = 1 or b = 2 order by c; -+----------------------------------------------------------------------------------------------------------------------------------+ -| Query Plan | -+----------------------------------------------------------------------------------------------------------------------------------+ -| ============================================================= | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------------------- | -| |0 |SORT | |1 |2 | | -| |1 |└─TABLE RANGE SCAN|test(idx_b_c_d)|1 |2 | | -| ============================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b], [test.c], [test.d]), filter(nil), rowset=256 | -| sort_keys([test.c, ASC]) | -| 1 - output([test.a], [test.b], [test.c], [test.d]), filter(nil), rowset=256 | -| access([test.a], [test.b], [test.c], [test.d]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([test.b], [test.c], [test.d], [test.a]), range(1,MIN,MIN,MIN ; 1,MAX,MAX,MAX), (2,MIN,MIN,MIN ; 2,MAX,MAX,MAX), | -| range_cond([test.b = 1 OR test.b = 2]) | -+----------------------------------------------------------------------------------------------------------------------------------+ -``` -  这条 SQL 与第一条类似,同理,也不需要排序。 -``` -explain select /*+index(test idx_b_c_d)*/ * from test where b = 1 and c = 2 order by c; -+---------------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------------+ -| =========================================================== | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| ----------------------------------------------------------- | -| |0 |TABLE RANGE SCAN|test(idx_b_c_d)|1 |2 | | -| =========================================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b], [test.c], [test.d]), filter(nil), rowset=256 | -| access([test.a], [test.b], [test.c], [test.d]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([test.b], [test.c], [test.d], [test.a]), range(1,2,MIN,MIN ; 1,2,MAX,MAX), | -| range_cond([test.b = 1], [test.c = 2]) | -+---------------------------------------------------------------------------------------------+ -``` -  下面这条 SQL 里的 c 是一个常量,索引是按照 b、c、d、a 有序的。因此,如果要求按照 b、d 去排序,直接在索引表上利用 c = 1 这个过滤条件(filter([test.c = 1]))查询就好了。
  例如索引中 b 列有两个不同的值 1 和 2,那么利用 c = 1 这个过滤条件过滤之后,会返回索引上两批离散的数据,一批数据是 b = 1,c = 1,d、a,另一批数据是 b = 2,c = 1,d、a,这两批数据虽然在索引上可能是离散的,但是各批数据内,以及各批数据间,都是有序的,所以就不需要通过再分配 sort 算子去进行排序了。 -``` -explain select /*+index(test idx_b_c_d)*/ * from test where c = 1 order by b, d; -+--------------------------------------------------------------------------------------------------------------+ -| Query Plan | -+--------------------------------------------------------------------------------------------------------------+ -| ========================================================== | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| ---------------------------------------------------------- | -| |0 |TABLE FULL SCAN|test(idx_b_c_d)|1 |2 | | -| ========================================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b], [test.c], [test.d]), filter([test.c = 1]), rowset=256 | -| access([test.a], [test.c], [test.b], [test.d]), partitions(p0) | -| is_index_back=false, is_global_index=false, filter_before_indexback[false], | -| range_key([test.b], [test.c], [test.d], [test.a]), range(MIN,MIN,MIN,MIN ; MAX,MAX,MAX,MAX)always true | -+--------------------------------------------------------------------------------------------------------------+ -``` - -## 查询指定列时,相比主表可以扫描更少的数据 -  这点比较好理解,例如一张大宽表,有 100 个列,OB 目前(截止到 2023.10.16)还只有行存(列存预计在 4.3 版本会支持)。如果经常要查询其中一个列,最好在这个列上创建索引,索引一般只会包含少数的几个列,可以有效避免每次都进行全表扫描。
  例如下面这条 SQL,计划中的 NAME 中显示 test(idx_b),说明查询用到了索引 idx_b,避免了扫描多余列 a、c、d 的数据。 -``` -create table test(a int, b int, c int, d int, key idx_b(b)); - -explain select b from test; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| ====================================================== | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------------ | -| |0 |TABLE FULL SCAN|test(idx_b)|1 |2 | | -| ====================================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.b]), filter(nil), rowset=256 | -| access([test.b]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([test.b], [test.__pk_increment]), range(MIN,MIN ; MAX,MAX)always true | -+---------------------------------------------------------------------------------------+ -11 rows in set (0.041 sec) -``` -  又例如下面这条 SQL,优化器会选择列数最少的索引 idx_b(或者说数据量最小的索引)进行扫描。 -``` -create table test(a int, b int, c int, d int, key idx_b(b)); - -explain select count(*) from test; -+-----------------------------------------------------------------------------------------+ -| Query Plan | -+-----------------------------------------------------------------------------------------+ -| ======================================================== | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| -------------------------------------------------------- | -| |0 |SCALAR GROUP BY | |1 |2 | | -| |1 |└─TABLE FULL SCAN|test(idx_b)|1 |2 | | -| ======================================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([T_FUN_COUNT_SUM(T_FUN_COUNT(*))]), filter(nil), rowset=256 | -| group(nil), agg_func([T_FUN_COUNT_SUM(T_FUN_COUNT(*))]) | -| 1 - output([T_FUN_COUNT(*)]), filter(nil), rowset=256 | -| access(nil), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([test.b], [test.__pk_increment]), range(MIN,MIN ; MAX,MAX)always true, | -| pushdown_aggregation([T_FUN_COUNT(*)]) | -+-----------------------------------------------------------------------------------------+ -``` -这里也有一个劣势,就是如果查询的列比较多时,如果走了索引,就需要拿着从索引上得到的主表的主键列回到主表查询其余列(索引回表)。**索引回表的代价是很高的,一般索引回表的性能只有直接全表扫的十分之一,如果过滤条件的过滤很差但是依然走了索引,索引回表的代价就无法被忽略了。**
  例如下面几条 SQL,索引建在 b 上,查询 a、b 的所有行,如果不走索引直接全表扫,优化器估计的代价 EST.TIME 是 2us。 -``` -create table test(a int, b int, c int, d int, key idx_b(b)); - -explain select a, b from test; -+---------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------+ -| =============================================== | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ----------------------------------------------- | -| |0 |TABLE FULL SCAN|test|1 |2 | | -| =============================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b]), filter(nil), rowset=256 | -| access([test.a], [test.b]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([test.__pk_increment]), range(MIN ; MAX)always true | -+---------------------------------------------------------------------+ -``` -  如果通过指定 hint 强制走索引,优化器估计的代价 EST.TIME 是 5us,反倒比不走索引更慢了,这就是索引回表(is_index_back=true)带来的额外开销。 -``` -explain select /*+index(test idx_b)*/ a, b from test; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| ====================================================== | -| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------------ | -| |0 |TABLE FULL SCAN|test(idx_b)|1 |5 | | -| ====================================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([test.a], [test.b]), filter(nil), rowset=256 | -| access([test.__pk_increment], [test.a], [test.b]), partitions(p0) | -| is_index_back=true, is_global_index=false, | -| range_key([test.b], [test.__pk_increment]), range(MIN,MIN ; MAX,MAX)always true | -+---------------------------------------------------------------------------------------+ -``` - -# 如何衡量走索引的耗时时间 -走索引的耗时时间由两部分构成: - -1. 扫描索引的时间(由扫描的数据行数决定) -2. 索引回表的时间(由需要回表的数据行数决定) - -  假设这张表有 10000 行数据,扫描索引的时间是 1 ms 1000 行,索引回表的时间是 1 ms 100 行(大概是十倍的关系)。 -``` -create table test(a int primary key, b int, c int, d int, e int, key idx_b_e_c(b, e, c)); -``` - -- 用 b = 1 这个过滤条件进行过滤,会返回 1000 行数据; -- 用 b = 1 and c = 1 这个过滤条件进行过滤,会返回 100 行数据; - -  如果我们执行 select * from test where b = 1; 这条 SQL,走索引的话,开销就是:在索引上扫描 1000 行的数据,大概是 1 ms。 -``` -explain select /*+index(test idx_b_c_d)*/ * from test where b = 1; -``` -  不走索引的话,开销就是:在主表上扫描 10000 行的数据,大概是 10 ms。 -``` -explain select /*+index(test primary)*/ * from test where b = 1; -``` -  所以在这种场景下,还是走索引更快,如果生成的计划没有走索引,就可以自己指定个 hint /\*+index(test idx_b_c_d)*/ 强制让它走索引。
  如果我们执行 select * from test where b = 1 and c = 1; 这条 SQL,走 idx_b_c_d 这个索引的开销就是:在索引上扫描 1000 行的数据 + 索引回表 1000 行的数据,大概是 1 ms + 10 ms = 11 ms。 -``` -explain select /*+index(test idx_b_c_d)*/ * from test where b = 1 and c = 1; -``` -  这条 SQL 不走索引的开销就是:在主表示索引上 10000 行的数据,耗时大概是 10 ms。 -``` -explain select /*+index(test primary)*/ * from test where b = 1 and c = 1; -``` -  所以在这种场景下,不走索引而走主表反倒更快,如果生成的计划走了索引,就可以自己指定个 hint /*+index(test primary)*/ 强制让它走主表。
  如何获取类似于 “用 b = 1 and c = 1 这个过滤条件进行过滤,会返回 100 行数据” 这种信息?执行一条 SQL 看下 count 就好了。 -``` -select count(*) from test where b = 1 and c = 1; -+----------+ -| count(*) | -+----------+ -| 100| -+----------+ -``` - -# 创建索引的策略 -大体可以用下面两句话总结: - -- **将存在等值条件的列放在索引的前面,将存在范围条件的列放在索引的后面。** -- **有多个列上存在范围条件时过滤性强的列放在前面。** - -  例如一条 SQL 中存在三个过滤条件,分别是 a = 1、b > 0、c between 1 and 12。其中 b > 0 可以过滤掉 30% 的数据,c between 1 and 12 可以过滤掉 90% 的数据,那么按照我们的基础策略,对于这条 SQL 可以在 (a, c, b) 上建一个索引进行优化。
  大家可以思考下为什么要这么创建索引?索引中前两列是 a 和 c 很好理解,最后在索引中加上 b 列的原因我个人理解是为了在 select b 的时候,可以消除回表的开销。 \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/performance_tuning/join_optimize.md b/docs/user_manual/user_best_practices/performance_tuning/join_optimize.md deleted file mode 100644 index 2dbb8c576..000000000 --- a/docs/user_manual/user_best_practices/performance_tuning/join_optimize.md +++ /dev/null @@ -1,398 +0,0 @@ ---- -title: SQL 性能调优 —— 连接(JOIN)方式调优 -weight: 4 ---- - -# 连接(JOIN)方式调优 -在 OceanBase 数据库中,有三种基础的连接算法: Nested-Loop Join、 Merge Join 以及 Hash Join: - -1. Nested-Loop Join:首先把 join 左侧的数据扫描出来,然后用左侧的每一行去遍历一次右表的数据,从里面找到所有能连接上的数据行做连接。它的代价 = 左表扫描的代价 + 左表的行数 * 左表每一行遍历右表的代价,即:cost(NLJ) = cost(left) + N(left) * cost(right),时间复杂度是 O(m * n)。 -2. Merge Join(这个应该也可以叫做 sort merge join):先对左表和右表的连接键分别排序,然后用类似移动指针的方式不断地调整指针,找到匹配行做连接。它的代价 = 左右表排序的代价 + 左右表扫描的代价,即:cost(MJ) = sort(left) + sort(right) + cost(left) + cost(right),时间复杂度就是排序的时间复杂度 O(n * logn)。 -3. Hash Join:扫描左表并对每一行建哈希表,扫描右表并哈希表中做探测,匹配并连接。它的代价 = 扫描左表的代价 + 左表的行数 * 每一行建哈希表的代价 + 扫描右表的代价 + 右表的行数 * 每一行探测哈希表的代价,即:cost(HJ) = cost(left) + N(left) * create_hash_cost + cost(right) + N(right) * probe_hash_cost。 - -# Nested-Loop Join -  OceanBase 里的 Nested-Loop Join 有两种执行方式,分别为非条件下压的 Nested-Loop Join 和条件下压的 Nested-Loop Join。
  我们接下来会看一下非条件下压的 NLJ 和条件下压的 NLJ 的开销有什么不同。开始前,我们做一些准备工作,先创建两张表 t1 和 t2,通过 recursive cte 分别插入 1000 行数据,然后通过系统包函数 dbms_stats.gather_table_stats 收集一下统计信息。 -``` -drop table t1; - -drop table t2; - -CREATE TABLE t1 -WITH RECURSIVE my_cte(a, b, c) AS -( - SELECT 1, 0, 0 - UNION ALL - SELECT a + 1, round((a + 1) / 2, 0), round((a + 1) / 3, 0) FROM my_cte WHERE a < 1000 -) -SELECT * FROM my_cte; - -alter table t1 add primary key(a); - -CREATE TABLE t2 -WITH RECURSIVE my_cte(a, b, c) AS -( - SELECT 1, 0, 0 - UNION ALL - SELECT a + 1, round((a + 1) / 2, 0), round((a + 1) / 3, 0) FROM my_cte WHERE a < 1000 -) -SELECT * FROM my_cte; - -alter table t2 add primary key(a); - -call dbms_stats.gather_table_stats('TEST', 'T1', method_opt=>'for all columns size auto', estimate_percent=>100); - -call dbms_stats.gather_table_stats('TEST', 'T2', method_opt=>'for all columns size auto', estimate_percent=>100); -``` - -## 非条件下压的 Nested-Loop Join -  我们通过指定 hint /\*+use_nl(t1, t2)*/ 的方式强制让下面这条 SQL 生成 NESTED-LOOP JOIN 的计划,t2 上没有合适的索引可用,主键中也没有包含 b 列,就需要先扫描 t2 的全部数据,然后通过 material 算子将它物化到内存里。意味着接下来在处理 t1 的每一行时,都要完整地遍历 t2 的所有行,相当于做了笛卡尔积,时间复杂度是 O(m * n),所以性能非常差。
  OB 中只会出现有条件下压的 NLJ,理论上不应该出现这种非条件下压的 NLJ。 -``` -explain select /*+use_nl(t1, t2)*/ * from t1, t2 where t1.b = t2.b; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| =================================================== | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| --------------------------------------------------- | -| |0 |NESTED-LOOP JOIN | |1877 |11578 | | -| |1 |├─TABLE FULL SCAN |t1 |1000 |84 | | -| |2 |└─MATERIAL | |1000 |179 | | -| |3 | └─TABLE FULL SCAN|t2 |1000 |84 | | -| =================================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a], [t1.b], [t1.c], [t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| conds([t1.b = t2.b]), nl_params_(nil), use_batch=false | -| 1 - output([t1.a], [t1.b], [t1.c]), filter(nil), rowset=256 | -| access([t1.a], [t1.b], [t1.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.a]), range(MIN ; MAX)always true | -| 2 - output([t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| 3 - output([t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| access([t2.a], [t2.b], [t2.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t2.a]), range(MIN ; MAX)always true | -+---------------------------------------------------------------------------------------+ -21 rows in set (0.050 sec) -``` - -## 条件下压的 Nested-Loop Join -  我们改变连接条件为 t1.a = t2.a,并通过指定 hint /\*+use_nl(t1, t2)*/ 的方式强制让下面这条 SQL 生成 NESTED-LOOP JOIN 的计划。
  可以看到在 nl_params 里面有 t1.a,意味着执行过程中会首先扫描 join 的左支(t1 表),然后把获取到的 t1 每一行的 a 值当做过滤条件,到右支上利用 t1.a = t2.a 作为 range_cond 去进行的 table get(主键查询)。因为右支 t2 表在 a 列上有主键,所以可以直接通过 table get 快速获取到任何一个具体的值,时间复杂度只有 O(m)。 -``` -explain select /*+use_nl(t1, t2)*/ * from t1, t2 where t1.a = t2.a; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| ======================================================= | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------------- | -| |0 |NESTED-LOOP JOIN | |1000 |16274 | | -| |1 |├─TABLE FULL SCAN |t1 |1000 |84 | | -| |2 |└─DISTRIBUTED TABLE GET|t2 |1 |16 | | -| ======================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a], [t1.b], [t1.c], [t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| conds(nil), nl_params_([t1.a(:0)]), use_batch=true | -| 1 - output([t1.a], [t1.b], [t1.c]), filter(nil), rowset=256 | -| access([t1.a], [t1.b], [t1.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.a]), range(MIN ; MAX)always true | -| 2 - output([t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| access([GROUP_ID], [t2.a], [t2.b], [t2.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t2.a]), range(MIN ; MAX), | -| range_cond([:0 = t2.a]) | -+---------------------------------------------------------------------------------------+ -``` -  在 OceanBase 中,一般情况下都只会选择条件下压的 Nested-Loop Join。除非没有等值连接条件,并且 Nested-Loop Join 也没有合适的索引可用,才有可能会考虑生成非条件下压的 Nested-Loop Join,生成这种非条件下压的 NLJ 的概率非常小,一般都会用 HJ 或 MJ 代替,如果出现,就要仔细分析下是否合理了。 - -## Subplan Filter -  这里需要多提一句和子查询相关的 subplan filter 算子,这个算子的执行方式跟 Nested Loop Join 类似,和 NLJ 一样,也需要创建合适的索引或者主键,让条件能够下压。
  我们还继续用之前创建的两张表 t1 和 t2,主键都建在两张表的 a 列上。下面这条 SQL 是 subplan filter 没有合适的索引或主键的情况,计划和没有条件下压的 NLJ 几乎一模一样,这里不再赘述了: -``` -explain select /*+no_rewrite*/ a from t1 where b > (select b from t2 where t1.b = t2.b); -+--------------------------------------------------------------------------------------------+ -| Query Plan | -+--------------------------------------------------------------------------------------------+ -| ================================================= | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------- | -| |0 |SUBPLAN FILTER | |334 |45415 | | -| |1 |├─TABLE FULL SCAN|t1 |1000 |60 | | -| |2 |└─TABLE FULL SCAN|t2 |2 |46 | | -| ================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a]), filter([t1.b > subquery(1)]), rowset=256 | -| exec_params_([t1.b(:0)]), onetime_exprs_(nil), init_plan_idxs_(nil), use_batch=false | -| 1 - output([t1.a], [t1.b]), filter(nil), rowset=256 | -| access([t1.a], [t1.b]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.a]), range(MIN ; MAX)always true | -| 2 - output([t2.b]), filter([:0 = t2.b]), rowset=256 | -| access([t2.b]), partitions(p0) | -| is_index_back=false, is_global_index=false, filter_before_indexback[false], | -| range_key([t2.a]), range(MIN ; MAX)always true | -+--------------------------------------------------------------------------------------------+ -``` -  下面这条 SQL 是 subplan filter 有合适主键的情况,计划和有条件下压的 NLJ 几乎一模一样,这里不再赘述了: -``` -explain select /*+no_rewrite*/ a from t1 where b > (select b from t2 where t1.a = t2.a); -+-------------------------------------------------------------------------------------------+ -| Query Plan | -+-------------------------------------------------------------------------------------------+ -| ======================================================= | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------------- | -| |0 |SUBPLAN FILTER | |334 |18043 | | -| |1 |├─TABLE FULL SCAN |t1 |1000 |60 | | -| |2 |└─DISTRIBUTED TABLE GET|t2 |1 |18 | | -| ======================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a]), filter([t1.b > subquery(1)]), rowset=256 | -| exec_params_([t1.a(:0)]), onetime_exprs_(nil), init_plan_idxs_(nil), use_batch=true | -| 1 - output([t1.a], [t1.b]), filter(nil), rowset=256 | -| access([t1.a], [t1.b]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.a]), range(MIN ; MAX)always true | -| 2 - output([t2.b]), filter(nil), rowset=256 | -| access([GROUP_ID], [t2.b]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t2.a]), range(MIN ; MAX)always true, | -| range_cond([:0 = t2.a]) | -+-------------------------------------------------------------------------------------------+ -``` -  在 OceanBase 中,并不是所有的子查询都能被 unnest,有时候根据 sql 的语义,只能用 subplan filter 算子进行计算。**subplan filter 的执行方式跟 Nested Loop Join 类似,所以也需要创建合适的索引避免出现非条件下压的 subplan filter。** - -# Hash Join - -- cost(NLJ) = cost(left) + N(left) * cost(right) -- cost(HJ) = cost(left) + N(left) * create_hash_cost + cost(right) + N(right) * probe_hash_cost - -  上面列出了 NLJ 和 HJ 的代价计算公式,这里先免去数学推导的过程,直接说结论 “ OB 的优化器如果要在 NLJ 和 HJ 中进行选择,在满足下面两个条件时,才会选择 NLJ ” : - -1. **右表有合适的索引或者主键。** -2. **右表的行数 / 左表的行数超过一定的阈值,在 OB 中,大概是 20 这样**(20 是博士和义博他们给出的经验值,实测不止 20,大概在 100 的样子,这里不乱猜测,后面有空儿了会学习一下代价计算这块儿的代码)。 - -  我们来验证一下上面的结论,先创建两张表:第一张无主键表 t1 有 10 行;第二张有主键表 t2 主键是 a 列,有 1000 行。 -``` -drop table t1; - -drop table t2; - -CREATE TABLE t1 -WITH RECURSIVE my_cte(a, b, c) AS -( - SELECT 1, 0, 0 - UNION ALL - SELECT a + 1, round((a + 1) / 2, 0), round((a + 1) / 3, 0) FROM my_cte WHERE a < 10 -) -SELECT * FROM my_cte; - -CREATE TABLE t2 -WITH RECURSIVE my_cte(a, b, c) AS -( - SELECT 1, 0, 0 - UNION ALL - SELECT a + 1, round((a + 1) / 2, 0), round((a + 1) / 3, 0) FROM my_cte WHERE a < 1000 -) -SELECT * FROM my_cte; - -alter table t2 add primary key(a); - -call dbms_stats.gather_table_stats('TEST', 'T1', method_opt=>'for all columns size auto', estimate_percent=>100); - -call dbms_stats.gather_table_stats('TEST', 'T2', method_opt=>'for all columns size auto', estimate_percent=>100); -``` -  当用不上 t2 表的主键时,如果要生成 NLJ,则会生成非条件下压的 NLJ,显然代价会很大,所以这里会生成一个 HJ 的计划: -``` -explain select * from t1, t2 where t1.b = t2.b; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| ================================================= | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------- | -| |0 |HASH JOIN | |1 |4 | | -| |1 |├─TABLE FULL SCAN|t1 |1 |2 | | -| |2 |└─TABLE FULL SCAN|t2 |1 |2 | | -| ================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a], [t1.b], [t1.c], [t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| equal_conds([t1.b = t2.b]), other_conds(nil) | -| 1 - output([t1.a], [t1.b], [t1.c]), filter(nil), rowset=256 | -| access([t1.a], [t1.b], [t1.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.a]), range(MIN ; MAX)always true | -| 2 - output([t2.b], [t2.a], [t2.c]), filter(nil), rowset=256 | -| access([t2.b], [t2.a], [t2.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t2.__pk_increment]), range(MIN ; MAX)always true | -+---------------------------------------------------------------------------------------+ -``` -  当能用上 t2 表的主键列 t2.a 去进行 table get,而且右表和左表有明显的大小表关系时(右表 t2 有 1000 行,左表 t1 只有 10 行),这里就会生成一个 NLJ 的计划: -``` -explain select * from t1, t2 where t1.a = t2.a; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| ======================================================= | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------------- | -| |0 |NESTED-LOOP JOIN | |10 |165 | | -| |1 |├─TABLE FULL SCAN |t1 |10 |3 | | -| |2 |└─DISTRIBUTED TABLE GET|t2 |1 |16 | | -| ======================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a], [t1.b], [t1.c], [t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| conds(nil), nl_params_([t1.a(:0)]), use_batch=true | -| 1 - output([t1.a], [t1.b], [t1.c]), filter(nil), rowset=256 | -| access([t1.a], [t1.b], [t1.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.__pk_increment]), range(MIN ; MAX)always true | -| 2 - output([t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| access([GROUP_ID], [t2.a], [t2.b], [t2.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t2.a]), range(MIN ; MAX), | -| range_cond([:0 = t2.a]) | -+---------------------------------------------------------------------------------------+ -``` - - -# Merge Join - -- cost(MJ) = cost(left) + cost(right) + sort(left) + sort(right) -- cost(HJ) = cost(left) + N(left) * hash_cost + cost(right) + N(right) * probe_cost - -  上面列出了 NLJ 和 HJ 的代价计算公式,它们都需要完整地扫描左表和右表,区别在于 Merge Join 要分别对两侧在连接键上进行排序,而哈希则是对左侧建哈希表、对右侧做哈希探测。相比于构建哈希表和哈希探测(O(n))来说,做排序的代价会更高(O(nlogn))。**因此,在一般情况下,一定是 Hash Join 优于 Merge Join。**
  只有在一些非常特殊的场景下,才会选择 Merge Join 。比如两侧都有序时,就可以省去排序的代价,直接做一次归并就好了。
  还是拿一开始的两张表 t1 和 t2 做实验,t1 和 t2 都有建在 a 列上的主键。 -``` -drop table t1; - -drop table t2; - -CREATE TABLE t1 -WITH RECURSIVE my_cte(a, b, c) AS -( - SELECT 1, 0, 0 - UNION ALL - SELECT a + 1, round((a + 1) / 2, 0), round((a + 1) / 3, 0) FROM my_cte WHERE a < 1000 -) -SELECT * FROM my_cte; - -alter table t1 add primary key(a); - -CREATE TABLE t2 -WITH RECURSIVE my_cte(a, b, c) AS -( - SELECT 1, 0, 0 - UNION ALL - SELECT a + 1, round((a + 1) / 2, 0), round((a + 1) / 3, 0) FROM my_cte WHERE a < 1000 -) -SELECT * FROM my_cte; - -alter table t2 add primary key(a); - -call dbms_stats.gather_table_stats('TEST', 'T1', method_opt=>'for all columns size auto', estimate_percent=>100); - -call dbms_stats.gather_table_stats('TEST', 'T2', method_opt=>'for all columns size auto', estimate_percent=>100); -``` -  如果连接条件都是本来就有序的主键 a 列,则会生成 merge join。 -``` -explain select * from t1, t2 where t1.a = t2.a; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| ================================================= | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------- | -| |0 |MERGE JOIN | |1000 |301 | | -| |1 |├─TABLE FULL SCAN|t1 |1000 |84 | | -| |2 |└─TABLE FULL SCAN|t2 |1000 |84 | | -| ================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a], [t1.b], [t1.c], [t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| equal_conds([t1.a = t2.a]), other_conds(nil) | -| merge_directions([ASC]) | -| 1 - output([t1.a], [t1.b], [t1.c]), filter(nil), rowset=256 | -| access([t1.a], [t1.b], [t1.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.a]), range(MIN ; MAX)always true | -| 2 - output([t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| access([t2.a], [t2.b], [t2.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t2.a]), range(MIN ; MAX)always true | -+---------------------------------------------------------------------------------------+ -``` -  如果连接条件都是无序的 b 列,则会生成 hash join。 -``` -explain select * from t1, t2 where t1.b = t2.b; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| ================================================= | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| ------------------------------------------------- | -| |0 |HASH JOIN | |1877 |481 | | -| |1 |├─TABLE FULL SCAN|t1 |1000 |84 | | -| |2 |└─TABLE FULL SCAN|t2 |1000 |84 | | -| ================================================= | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a], [t1.b], [t1.c], [t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| equal_conds([t1.b = t2.b]), other_conds(nil) | -| 1 - output([t1.a], [t1.b], [t1.c]), filter(nil), rowset=256 | -| access([t1.a], [t1.b], [t1.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.a]), range(MIN ; MAX)always true | -| 2 - output([t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| access([t2.a], [t2.b], [t2.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t2.a]), range(MIN ; MAX)always true | -+---------------------------------------------------------------------------------------+ -``` -  如果连接条件都是无序的 b 列,并通过指定 hint 强制要求生成 merge join 的计划,那么执行计划中一定会被先分配 sort 算子,通过 sort 算子进行排序后再进行 merge join,这种计划的代价往往会比 hash join 高。 -``` -explain select /*+ USE_MERGE(t1 t2) */ * from t1, t2 where t1.b = t2.b; -+---------------------------------------------------------------------------------------+ -| Query Plan | -+---------------------------------------------------------------------------------------+ -| =================================================== | -| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | -| --------------------------------------------------- | -| |0 |MERGE JOIN | |1877 |750 | | -| |1 |├─SORT | |1000 |294 | | -| |2 |│ └─TABLE FULL SCAN|t1 |1000 |84 | | -| |3 |└─SORT | |1000 |294 | | -| |4 | └─TABLE FULL SCAN|t2 |1000 |84 | | -| =================================================== | -| Outputs & filters: | -| ------------------------------------- | -| 0 - output([t1.a], [t1.b], [t1.c], [t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| equal_conds([t1.b = t2.b]), other_conds(nil) | -| merge_directions([ASC]) | -| 1 - output([t1.a], [t1.b], [t1.c]), filter(nil), rowset=256 | -| sort_keys([t1.b, ASC]) | -| 2 - output([t1.a], [t1.b], [t1.c]), filter(nil), rowset=256 | -| access([t1.a], [t1.b], [t1.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t1.a]), range(MIN ; MAX)always true | -| 3 - output([t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| sort_keys([t2.b, ASC]) | -| 4 - output([t2.a], [t2.b], [t2.c]), filter(nil), rowset=256 | -| access([t2.a], [t2.b], [t2.c]), partitions(p0) | -| is_index_back=false, is_global_index=false, | -| range_key([t2.a]), range(MIN ; MAX)always true | -+---------------------------------------------------------------------------------------+ -``` - -# 总结 -这三个 join 方式是数据库最基础的知识点,最后简单地总结一下学完之后需要记住的几个点: - -1. 计划里绝大多数的情况都只会选择有条件下压的 Nested-Loop Join,如果选择了非条件下压的 NLJ,需要创建合适的索引让计划变成有条件下压的 NLJ,或者指定 hint 变更 join 的方式。与联接操作相关的 Hint 详见官网链接:[https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000222744](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000222744) -2. subplan filter 的执行方式跟 Nested Loop Join 类似,所以也需要创建合适的索引避免出现非条件下压的 subplan filter。 -3. 计划里如果有 merge join,往往是可以利用下层算子的有序性。如果下层算子都是无序的,计划中在 merge 前还专门分配了一些 sort 进行排序,需要分析下是否需要通过 hint 改成使用 hash join 进行连接。 -4. 如果没有可用的索引和主键,也没有有序性,那么一般 hash join 的代价是最低的。 \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/performance_tuning/sql_audit.md b/docs/user_manual/user_best_practices/performance_tuning/sql_audit.md deleted file mode 100644 index 5f0b2a90f..000000000 --- a/docs/user_manual/user_best_practices/performance_tuning/sql_audit.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: SQL 审计视图 -weight: 2 ---- -# **SQL 审计视图** - -SQL 审计视图可以查看在 OceanBase 数据库里执行过的所有 SQL(包含执行失败 SQL)。这对开发同学了解自己的业务 SQL 和定位问题细节非常有帮助。 - -## **SQL 审计视图概述** - -SQL 审计视图 `gv$ob_sql_audit` 是虚拟表,是内存中一个 FIFO 队列。OceanBase 数据库 3.x 版本是 `gv$sql_audit` 虚拟表。 - -功能的开启和数据大小是通过下面的 OceanBase 集群参数控制的。 - -| 参数名 | 参数值 | 参数含义 | -| --- | --- | --- | -| enable_sql_audit | TRUE | 指定是否开启 SQL 审计。默认 TRUE 是开启。FALSE 是关闭。 | - -SQL 审计能保留的数据大小和租户内存资源的大小也有关系,通常不会特别大。建议自行实时将 SQL 审计视图的数据抽取走,之后做二次分析。 - -您可以在租户里执行如下命令开启或关闭 SQL 审计功能。 - -```sql -set global ob_enable_sql_audit = on; -``` - -视图列定义如下: - -| 列名 | 含义 | -| --- | --- | -| SVR_IP | SQL 执行的 OBServer 节点 IP | -| SVR_PORT | SQL 执行的 OBServer 节点端口 | -| REQUEST_ID | 唯一标识 | -| TRACE_ID | 该 SQL 的 TRACE_ID 信息,在 OBServer 节点日志里可以关联查询相关日志 | -| SID | SQL 执行的 OBServer 节点上的会话 ID | -| CLIENT_IP | 该 SQL 执行的客户端 IP,通常是 OBProxy IP | -| TENANT_ID | 该 SQL 执行的租户 ID | -| TENANT_NAME | 该 SQL 执行的租户名称 | -| USER_NAME | 该 SQL 执行的租户内部用户名 | -| USER_CLIENT_IP | 该 SQL 执行的实际客户端 IP | -| DB_ID | 该 SQL 执行的数据库 ID | -| DB_NAME | 该 SQL 执行的数据库名称 | -| SQL_ID | 该 SQL 的 SQL_ID | -| QUERY_SQL | 该 SQL 的文本,如果太长会截断 | -| PLAN_ID | 该 SQL 的执行计划 ID | -| AFFECTED_ROWS | 该 SQL 的写影响行数 | -| RETURN_ROWS | 该 SQL 的返回行数 | -| PARTITION_CNT | 该 SQL 访问的分区数量 | -| RET_CODE | 该 SQL 的返回代码 | -| EVENT | 该 SQL 的主要等待事件 | -| PLAN_TYPE | 该 SQL 的执行计划类型 1:本地 SQL;2:远程 SQL;3:分布式 SQL | -| IS_HIT_PLAN | 是否命中执行计划 | -| REQUEST_TIME | 该 SQL 执行时间点(时间戳类型,可通过 usec_to_time 转换为可读时间格式) | -| ELAPSED_TIME | 该 SQL 执行总耗时 | -| NET_TIME | 该 SQL 执行网络消耗时间 | -| QUEUE_TIME | 该 SQL 执行内部排队时间 | -| DECODE_TIME | 出队列后 decode 时间 | -| GET_PLAN_TIME | 该 SQL 执行计划生成时间 | -| EXECUTE_TIME | 该 SQL 实际内部执行时间(不包括 CPU 排队时间) | - -## **如何查看 SQL 审计视图** - -您可在 sys 租户或业务租户中执行如下命令查看 SQL 审计视图,在 sys 租户中执行时可以查看所有租户的 SQL 数据,在业务租户中执行仅可查看自身租户的 SQL 数据。 - -- 查看近期所有 SQL - - ```sql - SELECT /*+ read_consistency(weak) ob_querytimeout(100000000) */ substr(usec_to_time(request_time),1,19) request_time_, s.svr_ip, s.client_Ip, s.sid,s.tenant_id, s.tenant_name, s.user_name, s.db_name, s.query_sql, s.affected_rows, s.return_rows, s.ret_code, s.event, s.elapsed_time, s.queue_time, s.execute_time, round(s.request_memory_used/1024/1024/1024,2) req_mem_mb, plan_type, is_executor_rpc, is_inner_sql, trace_id - FROM gv$ob_sql_audit s - WHERE 1=1 and s.tenant_id = 1002 - and user_name='u_tpcc' - and request_time >= time_to_usec(DATE_SUB(current_timestamp, INTERVAL 30 MINUTE) ) - ORDER BY request_time DESC - LIMIT 100; - ``` - - request_time 是时间戳,可通过函数 `usec_to_time` 和 `time_to_usec` 进行时间戳和微秒数的转换。 - -- 分析统计近期所有 SQL - - 根据 sql_id 统计平均总耗时、平均执行时间等。 - - ```sql - SELECT sql_id, count(*), round(avg(elapsed_time)) avg_elapsed_time, round(avg(execute_time)) avg_exec_time - FROM gv$ob_sql_audit s - WHERE 1=1 and s.tenant_id = 1002 - and user_name='u_tpcc' - and request_time >= time_to_usec(DATE_SUB(current_timestamp, INTERVAL 30 MINUTE) ) - GROUP BY sql_id - order by avg_elapsed_time desc - ; - ``` - -- 查看报错的 SQL - - ret_code 是 SQL 执行报错时的错误码,无报错时为 0,出现报错时错误码为负数。 - - ```sql - SELECT /*+ read_consistency(weak) ob_querytimeout(100000000) */ substr(usec_to_time(request_time),1,19) request_time_, s.svr_ip, s.client_Ip, s.sid,s.tenant_id, s.tenant_name, s.user_name, s.db_name, s.sql_id, s.query_sql, s.affected_rows, s.return_rows, s.ret_code, s.event, s.elapsed_time, s.queue_time, s.execute_time, round(s.request_memory_used/1024/1024/1024,2) req_mem_mb, plan_type, is_executor_rpc, is_inner_sql, trace_id - FROM gv$ob_sql_audit s - WHERE 1=1 and s.tenant_id = 1002 - and user_name='u_tpcc' - and ret_code < 0 - and request_time >= time_to_usec(DATE_SUB(current_timestamp, INTERVAL 30 MINUTE) ) - ORDER BY request_time DESC - LIMIT 500; - ``` - -- 查看远程 SQL 和分布式 SQL - - plan_type 的值有三个:1 表示本地 SQL;2 表示远程 SQL;3 表示分布式 SQL。 - - ```sql - SELECT /*+ read_consistency(weak) ob_querytimeout(100000000) */ substr(usec_to_time(request_time),1,19) request_time_, s.svr_ip, s.client_Ip, s.sid,s.tenant_id, s.tenant_name, s.user_name, s.db_name, s.sql_id, s.query_sql, s.affected_rows, s.return_rows, s.ret_code, s.event, s.elapsed_time, s.queue_time, s.execute_time, round(s.request_memory_used/1024/1024/1024,2) req_mem_mb, plan_type, is_executor_rpc, is_inner_sql, trace_id - FROM gv$ob_sql_audit s - WHERE 1=1 and s.tenant_id = 1002 - and user_name='u_tpcc' - and plan_type > 1 - and request_time >= time_to_usec(DATE_SUB(current_timestamp, INTERVAL 30 MINUTE) ) - ORDER BY request_time DESC - LIMIT 500; - ``` - - 远程 SQL 的出现需要结合事务的业务逻辑分析。 diff --git a/docs/user_manual/user_best_practices/solutions/_index.md b/docs/user_manual/user_best_practices/solutions/_index.md deleted file mode 100644 index 7f124f9aa..000000000 --- a/docs/user_manual/user_best_practices/solutions/_index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 解决方案 -bookCollapseSection: false -weight: 8 ---- \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/solutions/high_availability_scenario.md b/docs/user_manual/user_best_practices/solutions/high_availability_scenario.md deleted file mode 100644 index ed8717c30..000000000 --- a/docs/user_manual/user_best_practices/solutions/high_availability_scenario.md +++ /dev/null @@ -1,267 +0,0 @@ ---- -title: 异地多活场景 -weight: 1 ---- - -> 关键字:高可用 | 多地多活 | 单元化丨极致弹性 -> -> 通过 OceanBase 强大的异地部署能力和灵活的副本变换以及负载均衡能力,帮助企业在关键核心场景中进行单元化部署,结合上层调度系统实现任意单元流量在城市和机房间的灵活调拨与切换,实现单元内故障不影响全局服务的效果。 - - - -# 异地多活场景 - -  各行各业随着业务数据量和并发访问量呈现指数级的增长,大量业务需要具备城市级别的容灾能力以满足监管的要求,进而达到支撑多种应用系统(例如银行业的存贷汇核心系统)的管理与使用需求。 - - -# 行业现状与挑战 - -- 数据丢失风险高:传统 IT 系统高可用系统的实现主要是以主备的方式进行部署, 这种方案有着非常广泛的应用,该方案虽然已经过长时间的验证,但仍然无法很好解决例如故障发生后切换数据不丢失的需求。 -- 故障恢复时效差:传统的双活架构,由于异步复制机制问题,主中心故障发生后不敢切、不能切的情况时有发生。 -- 故障爆炸半径大:由于传统架构下业务系统整体强耦合,数据库层面也经常发生单点故障影响全站用户的情况,严重影响业务连续性。 - - -# 解决方案 - - - -## 方案描述 - -- 利用 OceanBase 的多地多副本架构,以及高效的 Paxos 一致性协议工程实现,将数据库集群进行“三地五中心”部署,数据副本分别存储在同城和异地,实现异地容灾。 -- 在“三地五中心”架构下,OceanBase 可以实现单元化部署,配合应用微服务中间件改造(例如蚂蚁分布式中间件 SOFA),实现单个业务单元发生故障后,业务影响爆炸半径最低可降至 1%,并且即使遇到城市级故障也能在 1 分钟内自动恢复,同时保证零数据丢失。 - -![image.png](/img/solutions/high_availability_scenario/1.png) - - -## 方案优势 - -- “三地五中心”部署 :任意机房/ 城市级故障 1 分钟内自动恢复,零数据丢失。 -- 单元化架构设计:每个单元的业务和数据自包含,可以结合上层调度系统实现任意单元流量在城市和机房间的灵活调拨与切换,单元内故障不影响全局服务。 -- 极致弹性:在业务高峰例如大促期间,通过弹出任意比例的单元流量到低负载的机房,实现数据库容量的跨云弹性伸缩。 -- 强劲吞吐能力:满足海量数据处理的需要, 结合 OceanBase 原生分布式的并发读写能力,大幅提高批处理性能吞吐,每个批次查询超过百万条,每批次可并发处理百亿账户, 数据量达到 PB 级。 - - -# OceanBase 高可用技术介绍 - -  这一部分会介绍 OceanBase 数据库如何保证高可用,以及一些常见问题。 - - -## 名词解释 - -| **名词** | **解释** | -| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| RTO | 系统发生故障(例如掉电、宕机、进程误杀、系统 Crash、机房故障、灾难等)以后,从故障发生影响数据对外提供服务的时间点,到数据恢复可用,可以对外提供服务的时间点,这两点之间的时间段,叫做服务不可用时间。RTO 即服务恢复时间目标,主要指的是业务能够容忍的最大服务不可用时间。 | -| RPO | 系统在发生故障(例如掉电、宕机、进程误杀、系统 Crash、机房故障、灾难等)后再恢复可用。系统在恢复可用之后的数据与故障发生前的数据差别就是数据丢失量。RPO 即是数据恢复目标,主要指的是业务系统所能容忍的数据丢失量。 | - - - -## 高可用概述 - -  IT 系统的高可用(High Availability)指的是系统无中断地执行其功能的能力,代表系统的可用性程度。高可用是在系统设计、工程实践、产品能力各个环节上的综合作用,保证业务的持续连续性。而保障系统高可用最重要的焦点就是尽量消除或者冗余单点故障(Single Point of Failure)并且在单点或者系统出现不可用之后提供急速恢复的能力。
-  故障恢复是指当系统出现短时可恢复故障时,系统恢复可用性、恢复数据服务的一系列过程。灾难恢复是指当机房甚至城市出现灾难或故障事件导致机房或者该城市内机房长时间的故障,不能立即恢复可用时,整个系统恢复可用的一系列过程。企业级应用为了保证业务的连续可用,通常会对系统的可用性有很高的要求,需要保证在故障或者灾难发生时,系统的 RTO 尽量低,RPO 尽量接近 0。
-  高可用是分布式系统设计中必须考虑的因素,OceanBase 数据库作为一款原生的分布式数据库能够对外提供一致的、高可用的数据服务。OceanBase 数据库的事务一致性,存储持久化保证在出现 OBServer 退出重启情况下能够恢复到重启前相同的数据和状态。另外,OceanBase 数据库的备份恢复、主备库解决方案也保证了在不同场景下 OceanBase 数据库的高可用能力。 - - -## OceanBase 用于保证高可用的相关产品能力 - - - -### OceanBase 数据库分布式选举 - - - -#### 解决的故障场景 - -  OceanBase 数据库少数派因为各种原因造成不可用的故障恢复:例如,3 副本 3 台 OBServer 组成的 OceanBase 集群中 1 台 OBServer 宕机、退出;部署在不同机房的少数派副本机房故障;部署在不同城市的少数派副本城市发生灾难。 - - -#### 技术原理 - -  OceanBase 数据库的选举模块保证选举出唯一的主副本对外提供数据服务。同时,通过 Paxos 协议实现了多数派 Clog 强一致同步持久化,在 Paxos 组中任意少数派副本发生故障的情况下,剩下的多数派副本都能保证有最新的 Clog,因此就能避免个别硬件故障带来的数据损失,保证了数据可靠性。当整个 OceanBase 集群中的少数派出现故障时,如果是非 Leader 副本的少数派不可用,不会影响系统的可用性和数据库的性能。如果少数派故障中有 Leader 副本,OceanBase 数据库能够保证从剩余副本选出唯一新主提供数据服务。取决于 OceanBase 集群的搭建部署模式,如果 OBServer 的多个 Zone 分布在不同机房、不同城市时,通过 OceanBase 集群的分布式选举加上 OceanBase Paxos Clog 同步就可以达到跨机房高可用或者是跨城市的高可用方案。 更多信息请参见 [OceanBase 数据库选举](https://www.oceanbase.com/knowledge-base/oceanbase-database-20000000075)。 - - - -### OceanBase Clog 及存储引擎 - - - -#### 解决的故障场景 - -  OBServer 多数派故障需要重启;维护计划内的 OBServer 集群重启。 - - -#### 技术原理 - -  OceanBase 数据库的存储引擎将基线数据保存在 SSTable,增量数据保存在 MemTable 中。 OceanBase Clog 是数据的 Redo Log。当有数据修改发生时,请求会到达 Leader 副本所在节点,将数据更改请求更新到 MemTable,事务进行提交,Leader 更新本地 Clog 并通过 Paxos 协议将日志同步写到其他 Follower 的副本节点上。当有多数派节点的日志写盘成功后,数据修改成功,返回客户端。Follower 副本会将 Clog 回放到本地 MemTable 上提供弱一致读服务。
-  当 MemTable 到达阈值后,会触发冻结和转储,持久到 SSTable 层。而此时的 Clog 回放位点会推进,类似于做了 Checkpoint。在 OBServer 重启时,能够做到从 SSTable 中源数据还原、更新到最新信息。然后,从分区的元信息中获取 Clog 的回放位点,开始回放 Clog 日志生成 MemTable 中。至此,OceanBase 数据库可以将磁盘中的持久化信息恢复到宕机前的状态,保证了数据的完整性。 当 OceanBase 数据库由于故障(软件退出、异常重启、断电、机器故障等)重启或者计划内停机维护重启时,OBServer 能够在启动中恢复,将 OBServer Store 目录下的日志和数据还原到内存中,将进程的状态恢复到宕机前的状态。如果多数派副本故障并需要重启,数据服务会发生中断,OceanBase 数据库保证在多数派宕机重启后数据可以完全恢复到宕机之前。
-  OBServer 对于多数派宕机重启也进行了进一步的优化处理,加速数据副本恢复加载到 MemTable 的速度,尽快对外提供数据服务。在整个集群重启的情景下,需要将磁盘上最新的 Clog 重新回放到 MemTable 里后对外提供数据服务,在 OceanBase 数据库能够重新恢复数据服务时,数据恢复到集群重启前。 - - - -### OceanBase 数据库备份恢复 - - - -#### 解决的故障场景 - -  OceanBase 数据库出现数据损坏、节点 Crash 或者集群故障,OceanBase 数据库可以从备份的基线数据和 Clog 备份中恢复。 - - -#### 技术原理 - -  当 OceanBase 数据库出现数据损坏、节点 Crash 或者集群故障,OceanBase 数据库可以从备份的基线数据和 Clog 备份中恢复。 - - - -### OceanBase 数据库主备库 - - - -#### 解决的故障场景 - -  机房级的故障或城市级的灾难恢复。 - - -#### 技术原理 - -  OceanBase 数据库也支持传统的主备库架构。 OceanBase 集群的多副本机制可以提供丰富的容灾能力,在机器级、机房级、城市级故障情况下,可以实现自动切换,并且不丢数据,RPO = 0。
  OceanBase 数据库的主备库高可用架构是 OceanBase 数据库高可用能力的重要补充。当主集群出现计划内或计划外(多数派副本故障)的不可用情况时,备集群可以接管服务,并且提供无损切换(RPO = 0)和有损切换(RPO > 0)两种容灾能力,最大限度降低服务停机时间。 OceanBase 数据库支持创建、维护、管理和监控一个或多个备集群。备集群是生产库数据的热备份。管理员可以选择将资源密集型的报表操作分配到备集群,以便提高系统的性能和资源利用率。 - - - -## OceanBase 高可用相关问题答疑 - - - -### 分布式部署时,如果少数派宕机会发生什么?多快可以恢复? - -  OceanBase 数据库利用了基于 Paxos 分布式一致性协议保证了在任一时刻只有当多数派副本达成一致时,才能推选一个 Leader, 保证主副本的唯一性来对外提供数据服务。如果正在提供服务的 Leader 副本遇到故障而无法继续提供服务,只要其余的 Follower 副本满足多数派并且达成一致,就可以推选一个新的 Leader 来接管服务,而正在提供服务的 Leader 自己无法满足多数派条件,将自动失去 Leader 的资格。当 Leader 副本出现故障时,Follower 能在多长时间内感知到 Leader 的故障并推选出新的 Leader,这个时间直接决定了 RTO 的大小。
-  OceanBase 数据库的选举模型是依赖时钟的选举方案, 同个分区的多个全能副本(及日志型副本)在一个选举周期内进行预投票, 投票,计票广播以及结束投票,最终敲定唯一的主副本。当选举成功后,每个副本会签订认定 Leader 的租约(Lease)。在租约过期前,Leader 会不断发起连任,正常情况下能够一直连任成功。如果 Leader 没有连任成功,在租约到期后会周期性的发起无主选举,保证副本的高可用。 在三副本(全能副本)场景下,当一个副本出现异常(例如该节点机器故障,OBServer 下线等单点故障),OceanBase 数据库选举模块能够做到:如果原主副本连任成功,原主可以连续提供主副本服务;如果原主副本下线,Leader 连任失败,OceanBase 数据库进行无主选举,新主上任的时间在 30s 内完成。
-  同时,OceanBase 数据库通过 Paxos 协议实现了多数派 Clog 强一致同步持久化,在 Paxos 组中任意少数派副本发生故障的情况下,剩下的多数派副本都能保证有最新的 Clog,因此就能避免个别硬件故障带来的数据损失,保证了数据可靠性。 - - -### 分布式选举如何避免脑裂(Split Brain) 问题? - -  在高可用方案中,一个经典的需要面对的问题就是脑裂问题: 如果两个数据副本因为网络问题互相不知晓对方的状态,并且分别认为各自应当作为主副本提供数据服务,那么这时候就会出现典型的脑裂现象,最后会导致系统混乱,数据损坏。
-  OceanBase 数据库利用了 Paxos 协议中的多数派共识机制来保证数据的可靠性以及主副本的唯一性:在任一时刻只有多数派副本达成一致时,才能推选一个 Leader。如果正在提供服务的 Leader 副本遇到故障而无法继续提供服务,只要其余的 Follower 副本满足多数派并且达成一致,他们就可以推选一个新的 Leader 来接管服务,而正在提供服务的 Leader 自己无法满足多数派条件,将自动失去 Leader 的资格。
-  因此,我们可以看到 OceanBase 数据库的分布式选举在高可用方面有明显的优势:从理论基础上就保证了任一时刻至多有一个 Leader,彻底杜绝了脑裂的情况。由于不再担心脑裂,当 Leader 故障而无法提供服务时,Follower 可以自动触发选举来产生新的 Leader 并接管服务,全程无须人工介入。这样一来,不但从根本上解决了脑裂的问题,还可以利用自动重新选举大大缩短 RTO。当然,这里面还有一个很重要的因素,那就是 Leader 出现故障时,Follower 能在多长时间内感知到 Leader 的故障并推选出新的 Leader,这个时间直接决定了 RTO 的大小。 - - -### 分布式部署时,当多数派故障的时候是否依旧可以服务? - -  当 OceanBase 数据库中多数派故障,那么对应的分区是无法对外提供数据服务的。为了保证数据的高可用,在整个系统架构设计和运维时,还应当考虑和优化两个副本不相关的连续故障发生的时间。最终保证 MTTF(Mean Time To Failure)应当相较于故障的修复时间 MTTR(Mean Time To Repair)足够的小,以保证整个数据服务的高可用性。当 OceanBase 集群多数派失败的时候,在保留问题分析需要的信息和日志的前提下,应当采取应急措施将集群尽快恢复可用。 - - -### 副本自动补齐功能是如何工作的?副本自动补齐是否能够保证即使在节点宕机的情况下,相应的分区副本依旧齐全? - -  当 observer 进程异常终止,若终止时间小于 server_permanent_offline_time,则不作处理,此时有些分区的副本数只有 2 个了(三副本情况下);当终止时间超过 server_permanent_offline_time 时,则对该 Server 做永久下线处理,OceanBase 数据库会在同个 Zone 的其他 Server 开辟区域(资源充裕的 Server),维持副本个数。当有足够的资源时就会发起 Unit 迁移。 - - -### 是否支持多机房多地区的部署方式?在部署的时候有哪些基础设施的要求?应该如何选择方案? - -  在 OceanBase 集群的搭建中,允许将多个副本(节点)分散到多个机房、多个城市中,跨机房/跨城市实现 Paxos Group。在选择多机房、多城市集群架构时,通常也是为了获取机房级容灾甚至是城市级容灾的能力。从技术上来说,只要支撑 OceanBase 数据库不同副本节点的基础设施足够好,在合理的副本分布下,OceanBase 数据库就可以将数据分布在不同的节点上对外提供数据服务。
-  通常情况下,OceanBase 数据库集群对基础设施的基本要求如下: - -- 节点间的网络延迟时间(单向延迟)最好保证在 50ms 以内,最差(单向延迟)也要在 100ms 以内。 -- OBServer 环境中的时钟服务应该保证时钟同步的偏差在 100ms 以内,且不能够产生 1s 及以上的时钟跳变。 - -  通常情况下,进行多机房/多城市的的部署方案最重要的是考虑业务和各方面产生的架构需求。因为多机房/多城市的部署方式也会直接带来整个系统成本的大幅提高(例如,跨城市的网络专线部署等)。所以不同的部署方案是一个基于成本、需求、产品能力、方案可行性等多个维度的权衡结果产出。
-  从技术上来讲,可以解决机房级容灾或者城市级容灾的集群解决方案如下: - -- 同城三机房在同城三机房中,三个机房能力对等,都可以承担全部(或者部分)数据的主副本(Leader)角色,同时也承担其他数据的备份副本 (Follower) 的角色。同城三机房集群架构如下图所示。![image.png](/img/solutions/high_availability_scenario/2.png) -- 三地五中心五副本城市 1 和城市 2 能力对等,可以承担全部(或者部分)数据的主副本(Leader)角色,同时也承担其他数据的备份副本 (Follower) 的角色,城市 3 仅承担从副本角色。三地五中心五副本集群架构如下图所示。 - - ![image.png](/img/solutions/high_availability_scenario/3.png)
- -  在实际的部署方案中,OceanBase 团队也根据客户的需求定制过同城双机房和两地三中心的方案。两种部署方案都各自解决了一部分相应场景对于系统高可用的需求,但是同时也存在一定容灾能力的短板。在一定时期有可能作为系统架构的中间态成为客户在初始部署的选择,客户也有可能与 OceanBase 数据库主备库架构搭配使用一起达到期望的容灾能力。具体的方案探讨需要考虑多个因素和方面,如果有实际具体的场景,请联系 OceanBase 数据库技术架构师进行方案讨论和对接。 - - -# 用户案例 - - - -## 网商银行 - - - -### 业务挑战 - -- 网商银行致力于为小微企业、三农用户、大众消费者、中小金融机构提供普惠金融服务,从成立之初就提出低成本、高可用、高弹性的要求。 -- 随着互联网金融业务的发展,网商银行的数据量和并发访问量呈现指数级的增长,需要具备城市级别的容灾能力,满足监管要求。 -- 需要标准、安全和高效的数据库多租户隔离环境及管理工具,满足全行多应用系统,如存贷汇核心系统,的管理与使用需求。 - - -### 解决方案 - -- 采用“三地五中心”部署架构,构建异地多活,每个城市都有全量数据,通过不同数据库的读写点交叉,由多个城市共同承担用户的流量访问。 -- 凭借混合云架构、高可用等特性,通过分布式中间件、金融套件、移动开发平台集成解决方案,支撑网商银行核心系统数字化转型。 - -![image.png](/img/solutions/high_availability_scenario/4.png) - - -### 用户收益 - -- 通过“三地五中心”的逻辑架构部署,实现 RPO = 0,RTO < 30 s。一旦其中一到两个中心发生故障,甚至一个城市出现问题,业务也能够实现在 30 秒内自动恢复。 -- 分库分表与分区相结合,提高并发跑批的能力,每个批次查询 5 千~ 100 万条,每批次并发最大可处理 130 亿账户。目前集群规模 100 多套,数据量达到 PB 级。 -- 混合云的弹性架构,实现大促期间在一朵云资源耗尽的情况下,弹出 20% 的流量到新云上,保证了负载的跨云弹性伸缩。 - - -## 工商银行 - - - -### 业务挑战 - -- 容灾标准高 :理财业务支撑着企业客户万亿级别的资产,需要满足 7x24 小时持续服务,高可用容灾要求达到 5 级。 -- 建设成本高:原有业务系统基于传统大机和 DB2 数据库的封闭模式运行架构,业务容灾系统建设成本高昂。 -- 备机房资源浪费:近年来随着业务并发量的不断增加,数据库系统处理能力不足的问题凸显。冷备机房随时待命但不提供数据服务,资源利用率低。 - - -### 解决方案 - -- OceanBase 支持数据多副本,节点间通过 Paxos 协议同步,实现集群高可用和多地灾备。结合中国工商银行实际情况,搭建跨“两地三中心”的分布式集群,以“五副本 +主备”模式进行部署。 -- 集群统一管理调度所有服务器资源,实时动态计算,将业务负载调度到最空闲合理的服务器上运行。故障管理服务自动排查故障机器,调度事务到健康机器上执行,保证全局事务强一致性,无需人工干预。 - -![image.png](/img/solutions/high_availability_scenario/5.png) - - -### 用户收益 - -- 数据库服务器资源利用率达到 75%,在系统处理能力遭遇瓶颈时,可进行便捷的水平扩展,增加集群计算资源来提升处理能力。 -- 实现数据库同城双活、异地 RPO=0。机房级容灾达到 RPO=0,RTO < 30s, 即故障发生后,从 IT 系统宕机导致业务停顿到系统恢复至可以支持各部门的运作时间,少于 30 秒。  达到工商银行 5 级容灾要求,满足 7x24 小时服务要求。 -- 提升高可用水平,为业务提供强连续性保障,支撑万亿级资金交易,并且在保证系统性能和稳定性的前提下,有效降低了成本。 -- 系统从大型主机下移到国产化 ARM 服务器,降低整个系统的投入成本。国产服务器+国产操作系统+完全自研的分布式数据库,实现核心系统的分布式改造。 - - - -## 高德地图 - - - -### 业务挑战 - -- 亿级 DAU 的高德,每时每刻都在生产大量的数据,如何处理这些数据并降低后续扩容迁移成本、存储成本,成为了摆在高德面前的重要问题 -- 随着高德深入本地服务,需要在业务发展期提前做好技术布局,保障现在和未来服务可用性、稳定性,以应对未来业务的飞速成长。 - - -### 解决方案 - -- OceanBase 采用无共享的多副本架构,系统没有单点障碍,保证系统持续可用。支持“两地三中心”和“三地五中心”部署,对要求 CP 的结算类业务来说,可保障数据的城市级容灾。这里附一篇文章:[《促科技创新:高德数据优化篇之 OceanBase 最佳实践》](https://mp.weixin.qq.com/s/8nMqORGIPXF6IrHVAzG-9A) -- OceanBase 作为准内存数据库,采用 LSM-Tree 存储,增量数据操作内存,减少随机写,读写性能超传统关系型数据库。 -- 部署架构: - - 多点写入 - - 三地读写 - - 无网络延迟 - - 同城双主库容灾,异地多活容灾 - - 同城数据库侧容灾切流 - - 三地业务侧容灾切流 - - 三地六向数据同步 - - 三地六向秒级同步 - -![image.png](/img/solutions/high_availability_scenario/6.png) - - -### 用户收益 - -- 利用 OceanBase “三地五中心”的部署和强一致特性,业务可实现城市级别的容灾,保证数据不丢,极大提升了业务的稳定性。 -- 利用 OceanBase 的分布式特性,业务系统的数据存储具备了动态扩容的能力,业务无感知的平滑扩容,保证业务不停机。并节省了业务提量猛增后的数据库扩容和迁移成本,极大降低了数据库容量不足的风险。 -- 相比 MySQL,迁移至 OceanBase 后,读性能平均提升 2 倍,写性能平均提升 3 倍。 diff --git a/docs/user_manual/user_best_practices/solutions/htap.md b/docs/user_manual/user_best_practices/solutions/htap.md deleted file mode 100644 index 22f3043b6..000000000 --- a/docs/user_manual/user_best_practices/solutions/htap.md +++ /dev/null @@ -1,312 +0,0 @@ ---- -title: HTAP 一体化混合负载场景 -weight: 4 ---- - -> 关键字:T+0 混合负载 | 分布式执行框架 | 多模场景支持 -> -> HTAP 混合事务与实时分析处理是行业强诉求,OceanBase 基于分布式架构做好交易处理场景的同时,能够完成分析、跑批等分析性场景,一套引擎支持 OLAP + OLTP 工作负载,同时实现两套系统功能,真正通过“一个系统”提供同时处理交易及实时分析,“一份数据”用于不同的工作负载,从根本上保持数据的一致性并最大程度降低数据冗余,帮助企业大幅降低成本。 - - -# 场景 -  企业级应用的业务场景通常可以分为两个类别:联机交易和实时分析,我们通常称为 OLTP 和 OLAP 的业务应用。 -
![image.png](/img/solutions/htap/1.png) -
  大型企业往往会选择多款数据库产品分别支持 OLTP 和 OLAP 类的应用场景。这种组合式的解决方案需要数据在不同系统间进行流转,数据同步的过程会带来时间延迟和数据不一致的风险;同时,多套不同的系统也会产生冗余数据,增加存储成本。
-![image.png](/img/solutions/htap/2.png) - -# 解决方案 - -## 行业现状与挑战 - -- 离线数仓实时性差:离线数仓往往仅能提供 T+1 的数据能力,数据更新能力较弱,导致在线业务团队的实时分析、会员营销等需求无法及时满足。而单独建设实时数仓对中小企业又相对复杂,还需要投入额外人力成本维护数据同步链路。 -- HTAP 隔离性差:HTAP 的核心在于一套引擎支持多种负载,而资源的隔离性如果无法得到保障,承担关键交易的数据库一旦被分析型、批处理类的业务影响,往往直接导致在线交易下跌,业务严重受损。 -- 传统主备能力单一 :依靠主备复制的读写分离方案,备节点仅能处理只读查询,无法进行批处理操作 ;数据实时性也强依赖同步延迟,当遇到大型事务/DDL 时无法保证实时一致性。再加上本身能力有限的 SQL 优化器,无法灵活支持跨分片的多表复杂分析。 - -## 方案描述 - -- 通过 OMS 将异构数据库分库分表同步至 OceanBase 原生分区表(多表汇聚同步)。 -- 迁移至 OceanBase 后多个实例融合为一个实例,摆脱维护中间件的苦恼,大幅提升存储可扩展性。 -- OceanBase 的 HTAP 模式,满足了业务上分析查询的业务得以前置,无需等待 T+1 数据,直接于在线库实现实时营销决策等分析需求。 - -![image.png](/img/solutions/htap/3.png) - -## 方案优势 - -- 强大的数据更新能力:基于关系型数据库的更新能力,副本间毫秒级极低延迟。 -- HTAP 一体化:一套引擎处理 OLTP 和基本的 OLAP 场景,同时基于资源组隔离技术,提供 OLTP/OLAP 业务资源隔离的可靠方案,免去复杂的实时数仓建设。 -- 分布式并行计算引擎:强大的 SQL 优化器和执行器,支持向量化计算和海量数据的并行计算分析。 -- 多模场景支持:完全兼容 MySQL 语法 , 同时支持 HBase 和 Table 模型 API ,并且支持 JSON 半结构化数据格式和场景。强大的 CDC 能力支持,方便多种类型的下游进行数据消费。 -- 灵活的动态扩容:单集群最大可超过上千节点,数据容量超过 PB 级。 - -![image.png](/img/solutions/htap/4.png)
  OceanBase 通过 Flink CDC 组件作为流处理引擎,同时以 OceanBase 的 PL/SQL + Job Package 实现批处理任务,完成数据集成以及数据建模的流批一体处理。同时通过向量引擎 + 多副本架构,实现数据与业务的协同以及时效性的保障。 -
![image.png](/img/solutions/htap/5.png) - -# OceanBase HTAP 关键技术介绍 - -## 读写分离策略 -  数据库的读写分离策略是一种将数据库的查询操作和写入操作分离的方案,目的是降低读写操作的相互影响并提升资源利用率。在 HTAP 数据库中,读写分离的应用场景非常普及,只读的业务场景主要包括 BI 查询业务、大数据 ETL 取数、缓存的拉取等。两种类型的业务同时跑在一套数据库集群上,这对数据库的配置等要求就相对较高,因此我们一般会采用读写分离的方式,将一部分的读请求,路由到 Follower 副本上,从而降低复杂分析计算对资源的侵占,影响在线业务的响应延迟。
  OceanBase 数据库天然支持读写分离的功能,即通过 [OBProxy 代理服务](https://open.oceanbase.com/blog/10900290)和修改 OBServer 的配置即可实现业务的读写分离策略。
  OceanBase 数据库在读取数据时,提供了两种一致性级别:强一致性和弱一致性。强一致性是指请求路由给主副本读取最新数据;[弱一致性](https://www.oceanbase.com/docs/common-oceanbase-database-1000000000035084)是指请求优先路由给备副本,不要求读取最新数据。
  OceanBase 通过应用侧为执行的 SQL 添加 SQL Hint 来显性开启弱一致性读就可以实现基于注释的读写分离功能,同时也衍生出如下三种常用的读写分离策略,用户还可以根据实际情况,对读写分离策略进行灵活的配置: - -### 备优先读 - -1. 默认情况下,强一致性读以及增删改的 SQL,会访问 Leader 副本。 -2. 可以通过修改 OBproxy 的路由策略为 follower_first ,将业务读流量指定到该 OBProxy 从而保证读请求优先访问 follower 副本,对应 OBProxy 默认会将请求路由到本地 Follower 副本。如果本地该数据的副本为 Leader 副本,则会自动路由到同 Region 中其他 IDC 的 Follower 副本。 -![image.png](/img/solutions/htap/6.png)
-  **优点**:配置相对简单,只修改 OBProxy 配置,无需修改 OBserver 配置;读流量均摊到全部 follower 副本上。
-  **缺点**:如果本地副本是 Leader 副本,读请求则会跨 IDC(zone) 访问;在不调整副本 leader 的情况下,同 zone 或同 server 下不同副本的 leader 和 follower 可能共存,不能完全实现 zone 级别或者 server 级别的读写隔离。 - - -### 只读 zone -**路由策略:** - -1. 设置 Primary Zone为 zone1,zone2;zone3,注意这里 zone1 和 zone2 之间是逗号,zone2 和 zone3 之间是分号,表示 zone1 和 zone2 会被设置为 primary zone,因此所有的 Leader 副本都被迁移到了 zone1 和 zone2 中,zone3 默认情况下都为 Follower 副本,那么 zone3 的副本就可以只给弱一致性读的分析计算类SQL提供服务。 -2. 需要弱一致性读的 SQL,连接设置了弱读的 OBProxy;其余 SQL 连接正常 OBProxy。 -3. 通过连接弱读的 OBProxy 的所有 SQL,会基于 LDC 路由策略,以及 FOLLOWER_FIRST 策略,自动访问本地的 Follower 副本。 - -![image.png](/img/solutions/htap/7.png)
-  **优点**:通过设置只读 zone 实现了 zone 级别隔离读写请求,隔离性相比备优先读的方案更高。
-  **缺点**:需要人工设置 Primary Zone,写流量会被集中打到 zone1 和 zone2。
-  **适用场景**:读写比较均衡,存在少量分析的场景。 - - -### 只读副本 -OceanBase 中除了默认的全功能性副本之外,还有一种只读型副本,只读型副本的名称为 READONLY,简称 R,区别于全功能副本,只读副本提供读的能力,不提供写的能力,只能作为日志流的 Follower 副本,不参与选举及日志的投票,不能当选为日志流的 Leader 副本。利用只读副本,我们就可以专门再配置一个zone,只放只读型副本,专门提供给OLAP分析计算类请求,并且只读副本出现故障,并不会影响主集群服务。
  **路由策略:** - -1. 主集群正常提供服务。 -2. AP 类请求走独立的OBProxy,访问只读型副本。 - -![image.png](/img/solutions/htap/8.png) - -  **优点**:OLAP 与 OLTP 的请求可以做到完全隔离,互相不受任何影响。
-  **缺点**:需要为只读型副本提供更多资源。
-  **适用场景**:业务读请求远大于写请求,且大部分读请求对实时性要求不高,或者有大量的 AP 分析场景。 - -## 资源隔离能力 -  资源隔离并不是一个新概念,传统方式下不共享物理资源,可以理解为物理资源隔离方案。这种方案下不同租户或同一租户内 OLAP 和 OLTP 使用不同的副本,只读副本服务 OLAP,全功能副本服务 OLTP,两种业务不共享物理资源。如果不考虑成本,物理资源隔离无疑是更好的选择。
  但现实中,大部分客户都会考虑硬件成本及其资源利用率。一方面,数据库硬件的购买和维护成本高昂,而所有硬件都需要定期换新;另一方面,数据库硬件在进行单项业务处理时,平均占用率水平较低。如果不能充分利用硬件资源,无疑会造成巨大的资源浪费。
  而要充分利用硬件资源,不同租户或同一租户内 OLAP 和 OLTP 共享物理资源的逻辑资源隔离方案,自然脱颖而出。实际上,物理资源隔离和逻辑资源隔离不是二选一,而是互为补充的关系。理想的资源隔离方案是在完全物理隔离和逻辑隔离中找到平衡点,OceanBase 会给用户更多自由,帮助用户在面对各类场景下都可以做出最合适的选择。 - -### 资源隔离的种类 -  资源按照使用情况有刚性和弹性的区别,资源隔离的对象通常是弹性资源。刚性资源是保障程序完成功能必须的资源,一旦被占用,短时间内也难以释放。刚性资源的典型是磁盘空间和内存空间,连接数等,这类资源做好静态规划后,每个组可以使用的资源数量就会固定下来。弹性资源是指和程序功能无关,但是和性能有关的资源,比如 IOPS、CPU 时间、网络带宽等,这类资源一般可以抢占或被迅速释放,因此资源调度策略可以介入,实现闲时共享,忙时隔离。刚性资源比较重要的是**内存和磁盘空间**,弹性资源比较重要的是 **CPU 时间,IOPS 和网络带宽**,OceanBase 对以上资源均支持资源隔离。
-![image.png](/img/solutions/htap/9.png)
  除此以外,OceanBase 也提供了 SQL 级的资源隔离。通常适用的场景是,业务中存在多个账号,处理一个账号的一个订单时,会开启一个事务,然后执行一批与该账号相关的 SQL (通常是在 WHERE 条件中指定账号的值)。账号中同时存在大账号(数据量较大)和小账号(数据量较小),为了避免大账号把 CPU 资源用完导致小账号的订单无法得到处理,可以将处理不同订单的 SQL 绑定到不同的资源组,绑定后不同订单的 SQL 就会使用不同资源组的资源。 - -### 配置资源隔离的步骤 -在 OceanBase 中进行 HTAP 资源隔离分为以下两个步骤: - -- 定义资源组以及资源组的 QoS,对数据库来说租户就是最常见的资源组,另外 AP 和 TP 也可以是两个不同的资源组。 -- 按定义好的 QoS 制定实施资源隔离的策略。 - -#### 定义资源组 -  OceanBase 通过 unit config 描述租户的资源要求,比如创建一个租户之前要创建 resource pool(资源池) 、resource pool 的规格描述里就指定了各种资源的限制,详见:《[租户的资源管理](https://www.oceanbase.com/docs/common-oceanbase-database-1000000000034091)》。 -```sql -create resource unit box1 -max_cpu 4, max_memory 21474836480, max_iops 128, max_disk_size '5G', -max_session_num 64, min_cpu=4, min_memory=21474836480, min_iops=128; -``` - -#### 制定 OLTP 和 OLAP 资源规划 -  怎样管理租户内 OLTP 和 OLAP 的资源使用计划?OceanBase 参考了 Oracle 经典的 Resource Manager 系统包提供的管理接口。我们观察到,很多客户的跑批业务会安排在业务低峰期,如午夜或者凌晨,此时不用过于担心 OLAP 会影响到 OLTP 类业务,我们可以把集群绝大部分资源分配给 OLAP 类业务,给 OLTP 留下最小资源保证即可。在白天的业务高峰期,通过调整资源隔离方案,可以确保 OLTP 业务资源充足,同时按照预设资源满足基本的 AP 类查询。在 OceanBase 里,我们只需要预设两套资源管理计划,白天激活 DAYTIME 计划,夜间激活 NIGHT 计划,就可以实现满足基本的隔离需求的同时实现资源利用率的最大化。
-![image.png](/img/solutions/htap/10.png)
  比如我们可以通过 [DBMS_RESOURCE_MANAGER 系统包](https://www.oceanbase.com/docs/common-oceanbase-database-1000000000033906)来定义一个白天资源使用计划(resource plan),并且制定了此计划下 OLTP (interactive_group)和 OLAP (batch_group) 的资源百分比。80% 的资源用于 TP,剩下 20% 资源用于 AP。 -```plsql -// 定义 DAYTIME 资源使用计划 -DBMS_RESOURCE_MANAGER.CREATE_PLAN( - PLAN => 'DAYTIME', - COMMENT => 'More resources for OLTP applications'); - -DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE ( - PLAN => 'DAYTIME', - GROUP_OR_SUBPLAN => 'interactive_group', - COMMENT => 'OLTP group', - MGMT_P1 => 80, - UTILIZATION_LIMIT => 100); - -DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE ( - PLAN => 'DAYTIME', - GROUP_OR_SUBPLAN => 'batch_group', - COMMENT => 'OLAP group', - MGMT_P1 => 20, - UTILIZATION_LIMIT => 20); - -// 激活 DAYTIME 资源使用计划 -ALTER SYSTEM SET RESOURCE_MANAGER_PLAN = 'DAYTIME'; -``` -  按照类似的方式,我们可以定义夜晚的资源使用计划,并在业务低峰期激活它。假设这个业务白天的时候 TP 负载高,AP 集中在晚上。因此我们为白天和晚上设置两个不同的资源使用计划,白天的时候我们希望 80% 的资源用于 TP,剩下 20% 资源用于 AP,夜晚的时候希望 50% 的资源用于 TP,剩下 50% 资源用于 TP。
  我们按照上面的描述进行从白天计划切换到夜晚计划的测试。从下图可以看出,切换为夜晚计划后,AP 的 CPU 资源占比变大后,AP 的 QPS 明显变高,而 TP 的 QPS 则会降低。下图中 AP 和 TP 的 QPS 发生变化的点(15:32:30)就是切换资源使用计划的时间。
-![image.png](/img/solutions/htap/11.png)
  看起来 TP 的 QPS 降低比较少,和 AP 的 QPS 变化比起来不明显。这里要注意,按理想情况来算,TP 的 QPS 变化本来就要比 AP 的变化小,因为 AP 是从 0.3 到 0.5,增加了 66.7%, TP 从 0.7 到 0.5,降了 28.5%。我们算一下,TP 下降了 24.7%(19000 到 14300),和理论上的数值实际差距并不大。 - -#### 配置 QoS 的资源隔离语义 min/max/weight -  QoS(Quality of Service,服务质量)作为一种安全机制,可以在资源过载时保障关键进程的平稳运行。以下我们通过权重分配、资源上限和保留资源来描述 QoS。在不同的时间段,业务的流量会有波动,所以 QoS 描述需要有一定的弹性,如果像公有云上的 ECS 一样指定一个固定的 CPU 核数和 IO 带宽,在业务高峰的时候容易出现数据库容量不够而导致的故障。
  我们假设这样一个场景:总带宽是 100M,由租户 A 和租户 B 共同使用,基于资源的闲时共享和忙时隔离原则,我们尝试让租户 A 和租户 B 互不干扰地共同使用总带宽。
  如果两者的重要程度不同,怎样保证重要进程的优先运行?此时我们可以每个租户的重要程度分配使用资源的比例,如给租户 A 和租户 B 分配 1:3 的权重比,当这两个租户都需要 CPU 时,A 将得到 1 份 CPU 时间,B 将得到 3 份 CPU 时间。这一操作我们称为权重分配,或 < weight >。
  有时候物理资源较为充裕,低权重租户可能会占用大量并不需要的资源,如何限制它的使用呢?我们可以在权重分配的基础上给不同的租户定义资源上限 < max >,如租户 A 按照权重比 1/4 时,能使用的带宽最多为 25M,当给它设置资源上限参数 20M 后,它最多能使用 20M 的带宽。
  租户数量增减会引发权重配比改变,如何直观判断各租户最低资源需求的满足情况?此时我们可以给各租户设置保留资源 < min >,这样不仅可以保证所有租户基本功能的运行,也能直观清晰地描述 QoS。 - -### 资源隔离效果测试 -  接下来我们用单元测试对 OceanBase 的磁盘 IO 隔离能力进行了一项仿真实验: -- 设置 4 个租户,每个租户启动 64 个线程发送 IO 请求,IO 请求固定为 16KB 随机读。 -- 租户 1、2、4 的负载持续 20 秒。 -- 租户 3 的负载从第 10 秒开始,持续 10 秒。 -- 实验磁盘 IOPS 上限大概在 6w,如果不加限制,任意一个租户单独都可以打满磁盘。 - -#### 组户间的资源隔离 -  首先验证租户间磁盘 IO 隔离,各租户通过 DBMS_RESOURCE_MANAGER 进行如下配置:
-![image.png](/img/solutions/htap/12.png) - -实验结果如下图所示:
-![image.png](/img/solutions/htap/13.png)
  可以看到: - -- 在第 10 秒磁盘已经打满时,新加入的租户 3 依然拥有 1 万 IOPS,因为其通过 MIN_IOPS 预留了 1 万; -- 租户 4 的 IOPS 没有超过 5 千,因为其通过 MAX_IOPS 设置了资源上限; -- 无论负载如何变化,租户 1 和租户 2 的 IOPS 比值大概为 2:1,正如权重比例要求。 - - -#### 组户内的资源隔离 -接下来,我们将验证租户内负载的隔离。我们在租户 2 内设置了 4 个类别的负载,各负载的配置如下表所示:
-![image.png](/img/solutions/htap/14.png) - -实验结果如下图所示:
-![image.png](/img/solutions/htap/15.png)
  可以看到租户 2 的 4 类负载: - -- B 负载稳定在近 2000 IOPS,哪怕其权重为 0,因为 B 负载通过 MIN_PERCENT 预留了租户 MIN_IOPS 97% 的资源; -- A 负载稳定在 1000 IOPS 左右,因为其 MAX_PERCENT 为 1,最多只能使用租户 MAX_IOPS 1% 的资源; -- C、D 负载的 IOPS 比例始终保持大约 2:1,因为其权重为 50:25。 - -  从上述实验可以看出,OceanBase 在支持租户间资源隔离的同时,还支持租户内负载间的资源隔离,且都满足 QoS 的 reservation(MIN),limitation(MAX),proportion(WEIGHT)三种隔离语义。 - - -## 大查询队列 -  对于数据库来说,相比于单条复杂大查询,让大量的 DML 和短查询尽快返回更有意义。为了避免一条大查询阻塞大量简单请求而导致系统吞吐量暴跌,当大查询和短请求同时争抢 CPU 时,OceanBase 会限制大查询的 CPU 使用。当一个线程执行的 SQL 查询耗时太长,这条查询就会被判定为大查询,一旦判定为大查询,就会进入大查询队列,然后执行大查询的线程会等在一个 Pthread Condition 上,为其它的租户工作线程让出 CPU 资源。
-![image.png](/img/solutions/htap/16.png)
  配置大查询的参数为 large_query_threshold,执行时间超过这个参数设置的阈值,则认为是大查询。 - -| **属性** | **描述** | -| --- | --- | -| 默认值 | 5s | -| 取值范围 | [1ms, +∞) | - -  如果系统中同时运行着大查询和小查询,OceanBase 数据库会将一部分 CPU 资源分配给大查询,并通过配置参数 large_query_worker_percentage(默认值为 30%)来限制执行大查询最多可以使用的租户活跃工作线程数。 - -| **属性** | **描述** | -| --- | --- | -| 默认值 | 30 | -| 取值范围 | [0, 100] | - -  OceanBase 数据库通过限制大查询能使用的租户活跃工作线程数来约束大查询最多能够使用的 CPU 资源,以此来保证系统还会有足够的 CPU 资源来执行 OLTP(例如交易型的小事务)负载,通过这样的方式来保证对响应时间比较敏感的 OLTP 负载能够得到足够多的 CPU 资源尽快地被执行。 - - -## 高效的 SQL 引擎 - -### 向量化执行技术 -  要让 HTAP 数据库具备 OLAP 的能力,尤其是大数据量 OLAP 的能力,除原生分布式架构、资源隔离,还需要给复杂查询和大数据量查询找到最优解。OceanBase 执行引擎的向量化执行,就是解决这个问题的核心技术之一。
  为了更好地提高 CPU 利用率,减少 SQL 执行时的资源等待(Memory/Resource Stall) ,向量化引擎被提出并应用到现代数据库引擎设计中。 与数据库传统的火山模型迭代类似,向量化模型也是通过 PULL 模式从算子树的根节点层层拉取数据。区别于 next 调用一次传递一行数据,向量化引擎一次传递一批数据,并尽量保证该批数据在内存上紧凑排列。由于数据连续,CPU 可以通过预取指令快速把数据加载到 level 2 cache 中,减少 memory stall 现象,从而提升 CPU 的利用率。其次由于数据在内存上是紧密连续排列的,可以通过 SIMD 指令一次处理多个数据,充分发挥现代 CPU 的计算能力。
  向量化引擎大幅减少了框架函数的调用次数。假设一张表有 1 亿行数据,按火山模型的处理方式需要执行 1 亿次迭代才能完成查询。使用向量化引擎返回一批数据 ,假设设置向量大小为 1024,则执行一次查询的函数调用次数降低为小于 10 万次( 1 亿/1024 = 97657 ),大大降低了函数调用次数。在算子函数内部,函数不再一次处理一行数据,而是通过循环遍历的方式处理一批数据。通过批量处理连续数据的方式提升 CPU DCache 和 ICache 的友好性,减少 Cache Miss。
  由于数据库 SQL 引擎逻辑十分复杂,在火山模型下条件判断逻辑往往不可避免。但向量引擎可以在算子内部最大限度地避免条件判断,例如向量引擎可以通过默认覆盖写的操作,避免在 for 循环内部出现 if 判断,从而避免分支预测失败对 CPU 流水线的破坏,大幅提升 CPU 的处理能力。 - -#### 存储的向量化实现 -  OceanBase 的存储系统的最小单元是微块,每个微块是一个默认 64KB(大小可调)的 IO 块。在每个微块内部,数据按照列存放。查询时,存储直接把微块上的数据按列批量投影到 SQL 引擎的内存上。由于数据紧密排列,有着较好的 cache 友好性,同时投影过程都可以使用 SIMD 指令进行加速。由于向量化引擎内部不再维护物理行的概念,和存储格式十分契合,数据处理也更加简单高效。整个存储的投影逻辑如下图:
-![image.png](/img/solutions/htap/17.png) - -#### SQL 向量引擎的数据组织 -  SQL 引擎的向量化先从的数据组织和内存编排说起。在 SQL 引擎内部,所有数据都被存放在表达式上,表达式的内存通过 Data Frame 管理。Data Frame 是一块连续内存(大小不超过 2MB), 负责存放参与 SQL 查询的所有表达式的数据。SQL 引擎从 Data Frame 上分配所需内存,内存编排如下图。
-![image.png](/img/solutions/htap/18.png)
  在非向量化引擎下,一个表达式一次只能处理一个数据(Cell)(上图左)。
  向量化引擎下,每个表达式不再存储一个 Cell 数据,而是存放一组 Cell 数据,Cell 数据紧密排列(上图右)。这样表达式的计算都从单行计算变成了批量计算,对 CPU 的 cache 更友好,数据紧密排列也非常方便的使用 SIMD 指令进行计算加速。另外每个表达式分配 Cell 的个数即向量大小, 根据 CPU Level2 Cache 大小和 SQL 中表达式的多少动态调整。调整的原则是尽量保证参与计算的 Cell 都能存在 CPU 的level2 cache 上,减少 memory stalling 对性能的影响。 - -### 并行执行技术 -  SQL 的执行引擎需要处理很多情况,为什么要对这些情况进行细分呢?是因为 OceanBase 希望在每种情况下都能自适应地做到最优。从最大的层面上来说,每一条 SQL 的执行都有两种模式:串行执行或并行执行,可以通过在 SQL 中增加 hint /*+ parallel(N) */ 来决定是否走并行执行以及并行度时多少。
-![image.png](/img/solutions/htap/19.png) - -#### 串行执行 -  如果这张表或者这个分区是位于本机的,这条路线和单机 SQL 的处理是没有任何区别的。如果所访问的是另外一台节点上的数据,有两种做法:一种是当数据量比较小时,会把数据从远程拉取到本机来,OceanBase 中叫做远程数据获取服务;当数据量比较大时,会自适应地选择远程执行,把这条 SQL 发送到数据所在节点上,将整条 SQL 代理给这个远程节点,执行结束之后再从远程节点返回结果。如果单条 SQL 要访问的数据位于很多个节点上,会把计算压到每个节点上,并且为了能够达到串行执行(在单机情况下开销最小)的效果,还会提供分布式执行能力,即把计算压给每个节点,让它在本机做处理,最后做汇总,并行度只有 1,不会因为分布式执行而增加资源额外的消耗。
  对于串行的执行,一般开销最小。这种执行计划,在单机做串行的扫描,既没有上下文切换,也没有远程数据的访问,是非常高效的。 - -#### 并行执行 -  并行执行同时支持 DML 写入操作的并行执行和查询的并行执行,对并行查询分还会再去自适应地选择是本机并行执行还是分布式并行执行。
  对于当前很多小规模业务来说,串行执行的处理方式足够。但如果需要访问大量数据时,可以在 OceanBase 单机内引入并行能力,目前,这个能力很多开源的单机数据库还不支持,但只要有足够多的 CPU,是可以通过并行的方式使得单条 SQL 处理能力线性地缩短时间,只要有一个高性能多核服务器增加并行就可以了。
  针对同样形式的分布式执行计划,可以让它在多机上分布式去做并行,这样可以支撑更大的规模,突破单机 CPU 的数目,去做更大规模的并行,比如从几百核到几千核的能力。![image.png](/img/solutions/htap/20.png)
  OceanBase 的并行执行框架可以自适应地处理单机内并行和分布式并行。这里所有并行处理的 Worker 既可以是本机上多个线程,也可以是位于很多个节点上的线程。我们在分布式执行框架里有一层自适应数据的传输层,对于单机内的并行,传输层会自动把线程之间的数据交互转换成内存拷贝。这样把不同的两种场景完全由数据传输层抽象掉了,实际上并行执行引擎对于单机内的并行和分布式并行,在调度层的实现上是没有区别的。
-![image.png](/img/solutions/htap/21.png) - -#### 适用并行执行的场景 -  并行执行通过充分利用多个 CPU 和 IO 资源,达到降低 SQL 执行时间的目的。当满足下列条件时,使用并行执行会优于串行执行: - -- 访问的数据量大 -- SQL 并发低 -- 要求低延迟 -- 有充足的硬件资源 - -并行执行用多个处理器协同并发处理同一任务,在这样的系统中会有收益: - -- 多处理器系统(SMPs)、集群 -- IO 带宽足够 -- 内存富余(可用于处理内存密集型操作,如排序、建 hash 表等) -- 系统负载不高,或有峰谷特征(如系统负载一般在30%以下) - -  并行执行不仅适用于离线数据仓库、实时报表、在线大数据分析等 AP 分析型系统,而且在 OLTP 领域也能发挥作用,可用于加速 DDL 操作、以及数据跑批工作等。但是,对于 OLTP 系统中的普通 SELECT 和 DML 语句,并行执行并不适用。 - -#### 适用串行执行的场景 -  串行执行使用单个线程来执行数据库操作,在下面这些场景下使用串行执行会优于并行执行: - -- Query 访问的数据量很小 -- 高并发 -- Query 执行时间小于 100 毫秒 - -并行执行一般不适用于如下场景: - -- 系统中的典型 SQL 执行时间都在毫秒级。并行查询本身有毫秒级的调度开销,对于短查询来说,并行执行带来的收益完全会被调度开销所抵消。 -- 系统负载本就很高。并行执行的设计目标就是去充分利用系统的空余资源,如果系统本身已经没有空余资源,那么并行执行并不能带来额外收益,相反还会影响系统整体性能。 - - -## 列存引擎(4.3.0 版本的功能预告) -  对于分析类查询,列存可以极大地提升查询性能,也是 OceanBase 做好 HTAP 的一项不可缺少的功能。列存数据通常是静态的,很难被原地更新、而 OceanBase 的 LSM Tree 架构中 SSTable 是静态的,天然就适合列存的实现。MemTable 是动态的,仍然是行存,对于事务处理不会造成额外影响。因此 OceanBase 可以很好地兼顾 TP 类和 AP 类查询的性能。
  列存引擎已经完成开发,并在 2023 年 8 月初发布了 Alpha 版本供部分用户进行 poc 验证。即将发布的 4.3.0 版本中会正式支持列存引擎,敬请大家期待。 - - -# 客户案例 - -## 南京银行 - -### 业务挑战 - -- 线上业务增长受传统架构局限:随着南京银行大力发展线上金融业务,传统架构的并行处理能力、存储能力都不足以承接业务增长的高并发挑战。增长规模过快,将严重影响整体系统稳定性。 -- IT资源成本高:传统的端应用大部分部署在物理服务器上,只有20%左右使用 VMware虚拟化部署。整体来看,IT资源利用率平均只在10%左右,空闲资源高,整体IT资源成本高。 - -### 解决方案 - -- 强大的混合负载管理能力:客户有很多业务具有在线事务处理和海量数据分析的复杂使用场景。现在市场上只有像 Oracle,DB2 这样的大型商业数据库能够支撑在同一份数据上做实时的计算,而一般通用的做法就是把数据导一份到数据仓库里做离线的计算,那么对资源本身其实是一种浪费。OceanBase 自研的 SQL 引擎和分布式并行计算框架,可以很好地支撑这样的场景。同时 OceanBase 已经在内部经过了众多金融场景多年的打磨,在保障业务事务特性的同时,能够提供海量数据分析能力,以更高效率、更低成本满足多元化的用户需求,是一款真正成熟的金融级分布式数据库产品。 -- 良好的水平扩展能力:OceanBase 满足金融场景事务一致性、容灾、高并发的需求,基于其 Share-nothing 大规模并行处理模式,实现数据库的线性扩展能力,突破传统数据库瓶颈,真正实现系统的平滑扩缩容。 -- 业务迁移的无损化:OceanBase 打造的 OMS 数据迁移服务(OceanBase Migration Service),结合蚂蚁十年数据库架构升级的先进经验,真正做到了秒级的数据校验,秒级即时回滚,同时支持多种数据库类型,整个数据迁移链路和回滚机制的搭建基本上都是通过一键操作完成,使用简便。解决了用户的迁移痛点,降低业务迁移成本。 - -![image.png](/img/solutions/htap/22.png) - -### 用户收益 - -- 相较传统核心,“鑫云+” 平台的处理性能提高了 3~5 倍,2000 万贷款借据的计提时间控制在半小时以内; -- 鑫云+平台运用新技术通过 X86 服务器代替了 IBM 高端服务器,轻资产模式使得单账户管理成本约为传统 IOE 架构的 1/5 至 1/10; -- 人员维护成本大幅下降,“鑫云+”平台的维护人员较传统银行业务系统约为 1/5 左右。 - -## 中国石化 - -### 业务挑战 - -- 中国石化基于新基建技术构建新一代智慧加油站,推进中国石化生活综合服务商转型战略,原有加油卡系统无法适应互联网化客户营销服务体验和模式创新需求。 -- 随着数据应用的层次更深更广,数据类型更多,对数据库提出新要求。现为异构分散式系统,无法满足业务转型所需低系统性风险、管理运维和自主创新。 - -### 解决方案 - -- 基于 OceanBase 强大的 HTAP 混合负载能力:通过读写分离、资源隔离等技术实现了负载均衡和 OLAP 查询性能提升。通过存储引擎的 LSM tree 架构、事务引擎的提前解行锁等技术提升了 OLTP 事务性能。 -- 通过“数据+平台+应用”的架构设计,将现有加油卡系统从分省运维的 23 套 Sybase 和 Oracle 单体数据库,统一集中至一套 OceanBase分布式数据库集群中。 -- OceanBase对传统集中式数据库具备优异的兼容能力,帮助中国石化原始数据库应用无损迁移,过渡方案确保柔性切割。 - -![image.png](/img/solutions/htap/23.png) - -### 用户收益 - -- 转型:电子券、返利实时化,单一支付方式向多种支付方式转变,有力推进中国石化生活综合服务商战略转型。 -- 提效:OceanBase 支撑全国近 3 万个加油站的业务流量,对内支持交易流水由天级降低到秒级,实现一体化班日结和报表需求。数据查询时间由分钟级降低到秒级,支持每分钟 50,000 笔业务交易。 -- 降本:23 套分散系统运维降低至 1 套 OceanBase,大幅度降低了软硬件和运维成本,实现 8 倍存储成本节约。 -- 可靠:故障恢复时间从小时级降低到分钟级,业务连续性达到 99.99%,安全级别达到网络安全等级保护 2.0 要求。 - - -## 易仓科技 - -### 业务挑战 - -- 复杂 SQL 查询性能瓶颈:数据库主要承载物流、仓储、供应链相关信息,要求稳定性及高可用能力,目前多表关联 SQL 较多,复杂 SQL 查询耗时长。 -- 单租户成本高:原数据库 RDS 有上百个实例数,单实例数据量大,资源碎片率高,需要不定时通过迁移至新实例释放碎片。随着实例数增多,单租户成本攀升。 - -### 解决方案 - -- 借助 OceanBase 的 SQL 执行引擎的并行执行技术,通过均衡分布数据分片到所有服务器,大幅缩短查询响应时间。 -- 完整技术降本方案。高级压缩技术降低数据存储成本,多租户混部充分利用系统资源,以及 HTAP 混合负载减少数据冗余。 -- OceanBase 多租户能力,实现数据库内核级虚拟化,对 CPU、内存、数据等资源提供隔离机制,并可以根据应用负载灵活配置租户资源占比,将多个单实例整合至 OceanBase 集群中,统一管理、灵活调度,有效提高资源利用率。 -- OceanBase 支持 MySQL 语法、存储过程、函数,完全兼容易仓科技当前所使用的 SQL 语法与数据类型,应用系统得以平滑迁移。 - -![image.png](/img/solutions/htap/24.png) - -### 用户收益 - -- 复杂 SQL 查询性能提升明显,在没有做任何表结构调整与设计的情况下,大部分均有不同程度提升。其中一条在 RDS 中需 43 秒,OceanBase 仅需 0.01 秒。 -- 高级压缩技术让易仓科技从 RDS 1.2T 数据迁移至 OceanBase,仅需 140G,数据压缩比高达 88%。加上多租户大集群模式提升资源利用率,使得迁移系统的数据库整体拥有成本降低 60%; -- 通过 OMS,易仓科技从 RDS 迁移至 OceanBase MySQL 模式,共1.2T 数据,数小时应用级别即完成迁移。 \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/solutions/migration_service.md b/docs/user_manual/user_best_practices/solutions/migration_service.md deleted file mode 100644 index 78ca192ea..000000000 --- a/docs/user_manual/user_best_practices/solutions/migration_service.md +++ /dev/null @@ -1,272 +0,0 @@ ---- -title: 一站式传统数据库替换场景 -weight: 5 ---- - -> 关键字:全面画像评估 | 异构透明迁移丨数据回源保护 -> -> OceanBase 一站式数据库迁移解决方案提供了迁移可行性评估、迁移成本评估、迁移性能评估、结构对象迁移、数据迁移、数据校验和反向回源保护等功能。通过 OceanBase 一站式数据库迁移解决方案,内置的专家经验和策略为业务应用迁移提供决策支持和全流程服务保障,让企业可以方便、快速、安全地将业务应用迁移到 OceanBase 数据库。 - - - -# 场景 - -  在传统的数据库迁移方案中,为了保障迁移任务的稳定性和数据的一致性,通常通过停机迁移的方式进行数据迁移。停机期间需要暂时停止写入数据至源端数据库,待迁移完成并确认数据一致性后,再切换业务数据库。停机迁移的耗时和业务数据量、网络状况相关,在业务量巨大的情况下,数据库的停机迁移可能耗时数天,对业务影响较大。 - - - -# 解决方案 - - - -## 行业现状与挑战 - -- 异构迁移复杂度高:无法保障异构数据库兼容性,全量业务梳理和兼容性评估周期长、成本高,无法全面掌握迁移成本和风险点。数据库性能评估:无法准确评估数据库容量和资源需求,无法提前识别和规避性能风险点,切换后导致业务 SLA 下降。 -- 数据质量无保障:无法保证关键数据迁移过程中的迁移效率和数据质量,长时间的业务停写通常是不可避免的,缺乏整体可回滚能力。 - - -## 方案描述 - -- OMA 集合了对象静态评估(DDL/存储过程等)、SQL 动态分析、流量负载回放的能力,可全方位对要迁移的数据库进行兼容性评估和风险识别。 -- OMS 支持 Oracle、MySQL、DB2 等关系型数据库与 OceanBase 双向自动迁移和数据校验,内置蚂蚁集团多年 DBA 专家经验,大幅提升迁移效率,降低风险。 -- 同时 OMS 还提供丰富的数据订阅 CDC 能力,支持数据流到 Kafka、MQ 等消息队列的同步。 - -![image.png](/img/solutions/migration_service/1.png) - - -## 方案优势 - -- 全面迁移评估:OMA 支持数据库迁移评估分析,形成全面数据库画像,掌握数据库拓扑、应用拓扑、整体负载和兼容性分析报告,为迁移策略提供决策支撑。 -- 负载回放验证:OMA 还支持负载流量回放,支持负载采集、WCR 回放,全面分析业务性能瓶颈和风险点,为容量评估提供决策支撑,为业务提供性能优化改进建议。 -- 高效数据迁移:OMS 支持结构对象迁移、全量数据迁移、增量数据同步,支持主流数据库的一站式数据迁移,快速高效的将数据迁移到 OceanBase。 -- 数据质量保障:OMS 迁移的数据校验功能,提供源数据库和目标库的全列数据一致性校验能力,保障数据迁移质量。反向链路的数据同步为无损回滚能力提供保证,确保业务的连续性不受影响。 - - -## 方案 FAQ - - - -### Q1:支持哪些数据库到 OceanBase 的迁移? - -  OceanBase 一站式数据库迁移解决方案支持从 Oracle、MySQL、DB2、Sybase 到 OceanBase 的迁移,并且这些数据库的迁移都在各行业实际客户实践并成功落地。 - - -### Q2:在实施过程中是否会影响源端数据库和在线业务? - -  OceanBase 一站式数据库迁移解决方案在进行实时增量数据同步时,只需从源端数据库日志中获取变化数据,不会直接访问源端数据库,所以不会影响源端在线业务。同时,OceanBase 一站式数据库迁移解决方案支持在全量数据迁移过程中动态调整并发度,只会占用源端极少量的 CPU 和存储 I/O,对源端数据库和在线业务的影响基本没有影响。 - - -# OceanBase 迁移服务介绍 - -  OceanBase 迁移服务(OceanBase Migration Service,OMS)是 OceanBase 数据库提供的一种支持同构或异构数据源与 OceanBase 数据库之间进行数据交互的服务,具备在线迁移存量数据和实时同步增量数据的能力。
  OMS 提供可视化的集中管控平台,只需要进行简单的配置即可实时迁移数据。OMS 可以帮助用户低风险、低成本、高效率地实现同构或异构数据库向 OceanBase 进行实时数据迁移和数据同步。 - - -## OMS 特性与优势 - -![image.png](/img/solutions/migration_service/2.png) - -- 支持多种数据源 OMS 支持 MySQL、Kafka 等多种类型的数据源与 OceanBase 数据库进行实时数据传输,具体功能会因为源端数据库的类型和目标端下游的终端类型不同,而有所区别。 -- 在线不停服迁移,业务应用无感知在不停服的情况下,您可以通过 OMS 无缝迁移数据至 OceanBase。应用切换至 OceanBase 数据库后,OceanBase 数据库上所有的变更数据会实时同步至切换前的源端数据库。OMS 可以降低业务迁移的风险,助力企业用户构建高可用、高可靠的数据体系架构。 -- 高性能的数据迁移,安全可靠 OMS 能够准实时实现异构 IT 基础结构之间大量数据的秒级延迟复制。因此 OMS 可以应用于数据迁移、跨城异地数据灾备、应急系统、实时数据同步、容灾、数据库升级和移植等多个场景。OMS 能够实现在业务应用无感知和不中断的前提下,运行数据迁移和数据同步任务,并保证数据的完整性和事务的一致性。全量迁移性能可以达到 100 MB/s,20 万 TPS,数据同步性能可以达到 50000 RPS。同时,OMS 提供高可用的部署架构方案,为数据迁移和实时同步提供稳定可靠的传输项目。 -- 一站式交互支持数据迁移的全生命周期管理。您可以在管理控制台界面操作完成数据迁移任务的创建、配置和监控,交互简便。 -- 实时数据同步助力业务解耦 OMS 支持 OceanBase 两种租户与自建 Kafka、RocketMQ 之间的数据实时同步,可以应用于实时数据仓库搭建、数据查询和报表分流等业务场景。 -- 多重数据校验提供多种数据一致性校验方式,更加全面、省时、高效地保证数据质量。同时展示差异数据,提供快速订正的途径。 - - - -## OMS 应用场景 - - - -### 数据库不停服迁移 - -![image.png](/img/solutions/migration_service/3.png)
  OMS 提供不停服数据迁移功能,不影响迁移过程中源数据库持续对外提供服务,能够最小化数据迁移对业务的影响。在完成结构迁移、全量数据迁移和增量数据迁移后,源数据库的全量和增量数据均已实时同步至目标数据库中,数据校验通过后,业务可以从源端切换至目标端。 - - -### 实时数据同步 - -  OMS 的数据同步功能支持实时同步 OceanBase 等数据库的增量数据至自建的 Kafka、RocketMQ 等消息队列中,提升消息处理能力,扩展业务在监控数据聚合、流式数据处理、在线和离线分析等大数据领域的全方位应用。
  OMS 支持 OceanBase 物理表和自建的 Kafka 等数据源之间的数据实时同步,您可以用于云 BI、实时数据仓库搭建、数据查询和报表分流等多种业务场景。 - - -### 高可用、灵活部署 - -- 单节点支持 HA 功能的组件包括 Store 和 Incr-Sync。当 Store 或 Incr-Sync 组件发生故障时,对异常进程进行重启。 - -![image.png](/img/solutions/migration_service/4.png) - -- 支持机房维度的容灾。 - - 机器宕机情况下,本机内全部组件迁移至合适机器。 - - 机器资源指标异常情景下,尝试负载均衡部分组件。 - - Store 故障时,在机器资源充足情况下,尝试重启。在机器资源不足时,进行负载均衡,或删除冗余 Store。 - - Incr-Sync 故障时,对异常进程进行重启。 - -![image.png](/img/solutions/migration_service/5.png) - -- 支持区域数据中心维度的容灾。OMS 支持增量日志读取和增量同步写入分开部署,通过多个传输控制协议(Transmission Control Protocol,TCP)连接并行传输、数据压缩和数据加密等优化手段,克服了远距离、劣网条件以及数据安全性等挑战(暂不支持跨地域调度)。 - -![image.png](/img/solutions/migration_service/6.png) - - - -## OMS 系统架构 - -OceanBase 迁移服务(OceanBase Migration Service,OMS)连接的两端分别是待迁移的源业务数据库和目标端 OceanBase 数据库。OMS 内部主要包含: - -- 管理控制台:进行一站式迁移调度。 -- 基础服务组件:集群管理、资源池管理、高可用组件和元数据管理等多个组件,以保证迁移模块的高效调度和稳定运行。 -- DBCat:数据对象采集和转换组件。 -- 全量导入组件 Full-Import 和全量校验组件 Full-Verification、增量拉取组件 Store、增量同步组件 Incr-Sync。 - -![image.png](/img/solutions/migration_service/7.png) - -OceanBase 迁移服务(OceanBase Migration Service,OMS)的功能分层体系如下:
-![image.png](/img/solutions/migration_service/8.png) - -- 服务接入层主要包括传输项目的管理、各种类型数据源的管理、OMS 各个组件模块的运维和监控,以及告警设置等。 -- 流程编排层主要负责实现上层表结构迁移、启动全量数据迁移/同步、增量数据迁移/同步、数据校检和数据订正,以及链路切换等任务的执行细节。 -- 组件层组件层包括以下模块: - - OMS 结构迁移的核心组件(DBCat)作为 OceanBase 数据库原生的 Schema 转换引擎,可以根据源端、目标端具体的数据源类型和字符编码类型,进行精确的数据类型映射或转换。OMS 的结构迁移组件支持转换、迁移数据库中的表、约束、索引和视图等多种对象。 - - 增量拉取组件 Store 从源实例读取原始数据,经过解析、过滤,以及对数据进行格式化,最终将数据在本地持久化存储。 - - 全量导入组件 Full-Import 负责源库表对象中存量数据的迁移和部分增量数据的同步。 - - 增量同步组件 Incr-Sync 从增量拉取组件中请求增量数据,并根据用户配置的同步对象进行数据过滤,然后在保证事务时序性及事务一致性的前提下,将日志记录同步至目标实例。 - - 全量校验组件 Full-Verification 负责迁移表中的行记录进行全字段校验,并针对不一致的数据生成订正语句。 - - Supervisor 负责对上述组件进行监控。 - - -## OMS 基础组件 - -  OMS 组件包括 DBCat、Store、Full-Import、Incr-Sync 和 Supervisor 组件,Supervisor 组件负责对其它组件进行监控。
-![image.png](/img/solutions/migration_service/9.png) - - - -### 结构迁移核心组件 DBCat - -  OMS 结构迁移的核心组件(DBCat)作为 OceanBase 原生的 Schema 转换引擎,可以根据源端、目标端具体的数据源类型和字符编码类型,进行精确的数据类型映射或转换。OMS 的结构迁移组件支持转换、迁移数据库中的表、约束、索引和视图等多种对象。
  同时,DBCat 可以严格对齐和兼容 OceanBase 的租户类型。例如,OceanBase 的某个版本暂时不支持源端数据库中的部分数据源类型,DBCat 会选择最兼容的数据类型进行转换映射。
  由于异构数据库迁移可能涉及以下问题,可以通过 DBCat 自动对 Schema 进行转换。 - -- 数据库引擎变化 -- 不同数据库的数据对象定义差别较大 -- 目标数据库中不支持部分源数据库的数据对象 -- 相同字段类型精度不匹配 - -  对于复杂的异构数据库迁移,您可以通过 DBCat 产生基础脚本,数据库管理员(Database Administrator,DBA)再进行人工加工,最终在目标数据库生成 Schema。 - - -### 增量拉取组件 OMS Store - -  不同类型数据库的 Store 增量拉取组件的实现方式不同,例如 OMS Store 模块的实现方式是依赖于 OceanBase 数据库的 Liboblog 工具。
  Liboblog 是 OceanBase 数据库的增量数据同步工具,通过 RPC 方式拉取 OceanBase 数据库各个分区的 Redo 日志后,结合各个表和列的 Schema 信息,转换 Redo 日志为中间定义的数据格式,最后以事务的方式输出修改的数据。 - - -### 全量导入组件 Full-Import - -  Full-Import 组件负责将静态数据和部分增量数据从项目配置的源端迁移至目标端,迁移的每张表都会经过数据切片、数据读取、数据加工和数据应用等步骤。
  Full-Import 组件会根据表结构,选择合适的切片方式,将单表的数据并发迁移至目标端。对于全量的大部分数据源都实现了流式读取,减少对于源端的读取压力。
  全量数据在目标端的应用使用了批量插入的方式,对于 OceanBase 数据库的分区表实现了根据分区写入聚合的逻辑,以提升写入效率。选取的表会进行并发迁移,表内会通过切片位点 ACK 机制确保重启数据的完整性。 - - -### 增量同步组件 Incr-Sync - -  Incr-Sync 组件负责将 Store 组件产生的增量数据应用至目标端。OMS 将增量同步的 Record 主要抽象为 DML、DDL 和 HB,DML 包括 INSERT、UPDATE 和 DELETE。OMS 通过将这些数据高效正确地应用至目标端,来确保数据的最终一致性。 - -- 对于唯一约束表:OMS 通过主键操作的幂等性确保最终一致性。 -- 对于无主键表:OMS 通过事务表机制防止事务重放确保一致性。 -- 目前 MySQL 数据库至 OceanBase 数据库 MySQL 租户的无主键表迁移,不保证数据的最终一致性。 - - -### 全量校验组件 Full-Verification - -  Full-Verification 组件负责从源端和目标端读取数据,并根据映射关系(依赖索引信息)对两端的数据进行全字段对比,对比结果包括差异数据文件和订正 SQL 文件。
  Full-Verification 组件会根据表结构选择合适的切片方式,从数据库中读取数据,能够对大部分数据源实现流式读取,减少读取压力。
  Full-Verification 组件支持各种异构数据源之间的数据比对,内部会对数据进行格式化,确保数据比对的性能和一致性。同时,全量校验支持数据多轮复检,以降低由于数据同步延迟导致的误报。 - - -## OMS 迁移时序 - -![image.png](/img/solutions/migration_service/10.png) - - -## OMS 产品能力 - - - -### 数据迁移 - -  数据迁移属于一次性任务,迁移完成后即可释放项目资源。您可以通过数据迁移功能,实现同构或异构数据源之间的数据迁移,适用于数据库升级、跨实例数据迁移、数据库拆分、扩容等业务场景。数据迁移项目是 OMS 数据迁移功能的基本单元。创建迁移项目时,可以指定的最大迁移范围是数据库级别,最小迁移范围是表级别。详细信息见 OceanBase 官网中的[数据迁移概述](https://www.oceanbase.com/docs/enterprise-oms-doc-cn-1000000000091371)部分。 - - -### 数据同步/订阅 - -  数据同步/订阅属于持续性动作,项目创建后会一直同步数据,保持源端和目标端的数据一致性,实现关键业务的数据实时流动。可以通过数据同步功能,实现数据源之间的数据实时同步,适用于数据异地多活、数据异地灾备、数据聚合和实时数据仓库等多种业务场景。详细信息见 OceanBase 官网中的[数据同步概述](https://www.oceanbase.com/docs/community-oms-cn-1000000000046516)部分。 -![image.png](/img/solutions/migration_service/11.png) - - - -# Oceanbase 旁路导入能力介绍 - -  OceanBase 数据库支持旁路导入的方式向数据库插入数据,即 OceanBase 数据库支持向 data 文件中直接写入数据的功能。旁路导入可以绕过 SQL 层的接口,直接在 data 文件中直接分配空间并插入数据,从而提高数据导入的效率。
![image.png](/img/solutions/migration_service/12.png)
  根据上图所示,数据导入可采用两种路径。橙色箭头所示的传统路径需要经过 SQL 查询、事务处理、数据存储等一系列模块。而绿色箭头所示旁路导入路径主要是对导入的数据进行类型转换,然后按照主键进行排序(如果有的话),最后将排序后的数据写入到 major SSTable 中。
  旁路导入是一条短路径,能减少大量的系统资源消耗,加快导入速度。目前 OceanBase 数据库支持以下语句进行旁路导入: - -- [LOAD DATA /_+ direct _/](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000033194) -- [INSERT /_+ append _/ INTO SELECT](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000033192) - -旁路导入功能可以在下面的场景中使用: - -- 数据迁移与同步。对于数据迁移和同步,通常需要将大量的各种格式的数据从不同的数据源向 OceanBase 数据库进行迁移,传统的 SQL 接口性能可能在时效性上无法得到满足。 -- 传统 ETL。当数据在源端进行了抽取和转化之后,在装载到目标端时,通常需要在短时间内加载大量的数据,使用旁路导入技术,会提升数据导入的性能。而对于 ELT 技术,在装载数据的过程中,也可以通过旁路导入技术提高效率。 -- 从文本文件或者其他数据源向 OceanBase 数据库加载数据,利用旁路导入技术,也能够提升加载数据效率。 - - - -# 客户案例 - - - -## 中国人寿 - - - -### 业务挑战 - -- 快速增长的业务使得数据大规模增长,传统集中式数据库面对海量数据,难以维持性能。 -- 保险业务的处理复杂,单一业务需多个系统完成,调用链比其他行业的业务更长、更复杂,确保复杂集合大交易量的稳定是保险业务数据库的挑战。 -- 期望实现数据库技术可控,摆脱对外依赖。与此同时,期望升级后实现数据库使用成本、维护成本的双降低。 - - -### 解决方案 - -- 语法全面兼容,功能完全覆盖原有功能点,提供迁移工具与解决方案,避免上千万行代码重写,实现应用真正平滑迁移,高效完成核心数据库替换。 -- OceanBase 多租户能力,实现动态弹性调整租户计算资源,敏捷的应对业务负载要求的变化,顺利通过季度、年度开门红等业务冲刺高峰。 -- OceanBase 大集群管理模式,整合竖井式应用,集中化管控,平台化管理,统一资源分配,避免集中式架构的资源浪费。集群通过 F5 提供应用访问,多个集群使用同一套 OCP 集群进行运维管控。 - -![image.png](/img/solutions/migration_service/13.png) - - -### 用户收益 - -- 速度:金融行业数据库升级整合范围最大、速度最快的大型金融机构,升级整合超 300 套旧数据库,覆盖全部业务系统,迁移数据量达数百 TB,数据库装机量超过 2 万服务器核数。 -- 成本:平台化管理能力提升,整合竖井式应用,集中化管控,统一资源分配和运维,业务数据库容量瘦身 78%,具有相同业务承载能力的 OceanBase 数据库软硬件成本缩减 75%。 -- 效率:分析型大数据加工处理能力提升 300%,实现一份数据既做交易又做分析,不仅提高业务决策效率,整体资源利用效率和处理能力大幅提升。 -- 安全:实现 RPO=0 的高级别跨城市容灾能力,满足金融行业分布式技术架构转型要求,提高数据全链路防泄漏能力。 - - -## 中国石化 - - - -### 业务挑战 - -- 中国石化基于新基建技术构建新一代智慧加油站,推进中国石化生活综合服务商转型战略,原有加油卡系统无法适应互联网化客户营销服务体验和模式创新需求。 -- 随着数据应用的层次更深更广,数据类型更多,对数据库提出新要求。现为异构分散式系统,无法满足业务转型所需低系统性风险、管理运维和自主创新。 - - -### 解决方案 - -- 基于 OMS 打造一站式数据迁移解决方案,具有多类型数据库支持、分钟级即时回滚、负载回放验证、秒级数据验证和一键完成迁移等特点。 -- OceanBase 对传统集中式数据库具备优异的兼容能力,帮助中国石化的 Oracle 和 Sybase 数据库应用无损迁移,过渡方案确保柔性切割。 -- 通过“数据+平台+应用”的架构设计,将现有加油卡系统从分省运维的 23 套 Sybase 和 Oracle 单体数据库,统一集中至一套 OceanBase 分布式数据库集群中。 - -![image.png](/img/solutions/migration_service/14.png) - - - -### 用户收益 - -- 转型:电子券、返利实时化,单一支付方式向多种支付方式转变,有力推进中国石化生活综合服务商战略转型。 -- 提效:OceanBase 支撑全国近 3 万个加油站的业务流量,对内支持交易流水由天级降低到秒级,实现一体化班日结和报表需求。数据查询时间由分钟级降低到秒级,支持每分钟 50,000 笔业务交易。 -- 降本:23 套分散系统运维降低至 1 套 OceanBase,大幅度降低了软硬件和运维成本,实现 8 倍存储成本节约。 -- 可靠:故障恢复时间从小时级降低到分钟级,业务连续性达到 99.99%,安全级别达到网络安全等级保护 2.0 要求。 diff --git a/docs/user_manual/user_best_practices/solutions/multi_tenant.md b/docs/user_manual/user_best_practices/solutions/multi_tenant.md deleted file mode 100644 index bd6e6e858..000000000 --- a/docs/user_manual/user_best_practices/solutions/multi_tenant.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: SaaS 资源整合场景 -weight: 3 ---- - -> 关键字:资源池化 | 可管理性 | 降低成本 -> -> OceanBase 的原生多租户架构,支持一个集群中同时运行多个数据库租户,每个租户可以视为一个独立的数据库服务。租户间数据和资源互相隔离,并且在集群内统一调度。支持在创建租户时选择不同的兼容模式,每个租户可单独配置数据副本数量、副本类型、存储位置及计算资源等。 - - -# SaaS 资源整合场景 - -## 微服务架构 -  随着企业内业务系统越来越复杂,原来的单体服务在工程和管理上变的越来越不堪重负。使用微服务架构,新增和调整功能只需要增加新的微服务节点。但是每个微服务需要使用不同的数据库,数据库的数量大大增加,可靠性和运维管理都带来了挑战。使用 OceanBase 多租户特性,管理员只需要运维少量集群,既能保证租户之间数据和资源互相隔离,又提升了数据库的稳定性。 - -## 多租户 SaaS 服务 -  云上的 SaaS 服务商,往往提供的是多租户的服务。多个业务租户的数据库如果在一个单机数据库中做逻辑名字空间隔离,大小租户之间互相影响。如果每个业务租户使用一个独立的数据库,成本高,几十到上百套分散数据库环境,运维工作复杂,同时扩展性受限。使用 OceanBase数据库内原生多租户,能更好地平衡隔离性和成本,而且大小租户可以独立扩缩容。 - -# 行业现状与挑战 - -- 实例碎片化:企业内部多个不同业务应用,或SaaS 企业面向不同客户的资源隔离需求,会导致需要部署大量的数据库实例,造成严重的碎片化,在某个关键业务或关键客户需求激增时,难以灵活的弹性扩展,性能和可用性难以保证。 -- 资源浪费:由于数据库资源的碎片化部署,单个实例为了保证一定的性能弹性空间,往往会多划出一部分容量作为短时间内请求增长的预备资源。这种资源预留在整体业务视角来看实际上造成了巨大的资源浪费,抬高了企业资源成本。 -- 管理复杂:大量的数据库实例同时带来的还有管理效率的降低, 数据库相关团队难以对成百甚至上千的实例进行精细化管理, 出现故障、抖动等事件时恢复时效差,对整体资源水位的把控难以全局掌控,抬高了企业的人力管理成本。 - -# 解决方案 - -## 方案描述 -  基于 OceanBase 的多租户架构,帮助企业将多个不同业务的数据库实例集中整合,提升资源利用率,同时基于 Paxos 的多副本机制可以保证每个资源单元的高可用能力。
-  既适用于中大型企业内部大量不同业务链路的资源池化,也适用于各个行业 SaaS 服务商,为不同客户提供不同规格的实例,保证资源隔离性的同时降低成本。
-![image.png](/img/solutions/multi_tenant/1.png) - -## 方案优势 - -- 多租户池化: OceanBase 集群原生实现了多租户下的资源隔离和虚拟化,一个集群中可以部署上百个数据库实例,每个实例数据和资源隔离,计算资源原地升配秒级生效。 -- 多维度弹性:基于 OceanBase 的多节点分布式架构,用户可以实现单机原地升配、多机弹性扩展,多机流量均衡多个维度的扩容操作,并且扩容对上层应用透明。对于涉及数据迁移的扩展无需人工值守,OceanBase 自动完成迁移以及多维度数据校验。 -- 统一管理:将多个零散的实例统一部署在 OceanBase 后,运维管理的复杂度大大降低,DBA 从之前管理几百个分散实例,到目前管理几个 OceanBase 集群,负载、告警、调优全部统一至集群级别,常规故障能够自动恢复,大幅提升业务支撑效率和应急响应能力。 -- 降本增效:不但通过多租户实现计算资源池化,提高总体利用率,OceanBase 的高级压缩能力使得存储成本仅为原来传统数据库的 1/3。经过大量的客户反馈和广泛案例实践统计,OceanBase 的资源整合方案能够帮助中型及以上企业降低 TCO 约 30%-50%。 - -# OceanBase 多租户特性介绍 - -## 多租户概述 -  租户是一个逻辑概念。在 OceanBase 数据库中,租户是资源分配的单位,是数据库对象管理和资源管理的基础,对于系统运维,尤其是对于云数据库的运维有着重要的影响。租户在一定程度上相当于传统数据库的"实例"概念。租户之间是完全隔离的。
-  在数据安全方面,OceanBase 数据库不允许跨租户的数据访问,以确保用户的数据资产没有被其他租户窃取的风险。在资源使用方面,OceanBase 数据库表现为租户"独占"其资源配额。总体上来说,租户(tenant)既是各类数据库对象的容器,又是资源(CPU、Memory、IO 等)的容器。
-  OceanBase 数据库通过租户实现资源隔离,让每个数据库服务的实例不感知其他实例的存在,并通过权限控制确保租户数据的安全性,配合 OceanBase 数据库强大的可扩展性,能够提供安全、灵活的 DBaaS 服务。
-  OceanBase 数据库采用了单集群多租户设计,天然支持云数据库架构,支持公有云、私有云、混合云等多种部署形式。
-![image.png](/img/solutions/multi_tenant/2.png) - - -## 租户和资源池 -  租户是集群之上的递进概念,OceanBase 数据库采用了多租户架构。多租户架构适用于资源整合(Resource Consolidation)、SaaS 服务等场景,同时也降低了运维复杂度。
-  集群偏向于部署层面的物理概念,是 Zone 和节点的集合,Zone 和节点具有部署地域(称为 Region)等属性;而租户则偏向于资源层面的逻辑概念,是在物理节点上划分的资源单元,可以指定其资源规格,包括 CPU、内存、日志盘空间、IOPS 等。
-  租户类似于传统数据库的数据库实例,租户通过资源池与资源关联,从而独占一定的资源配额,可以动态调整资源配额。在租户下可以创建 Database、表、用户等数据库对象。
-  要描述清楚租户的概念,首先需要描述清楚资源规格、资源单元(Unit)、资源池等前置概念。资源规格对应的视图是 DBA_OB_Unit_CONFIGS,资源池对应的视图是 DBA_OB_RESOURCE_POOLS,资源单元对应的视图是 DBA_OB_UnitS,租户对应的视图是 DBA_OB_TENANTS。
-  资源规格资源规格定义了常见物理资源项的大小,包括 CPU、内存、磁盘空间、IOPS 等。创建资源池时指定其资源规格,从而根据定义创建资源单元。
-  资源单元(Unit)Unit 是租户管理中非常重要的概念。OceanBase 按照 Unit 来管理物理资源,是 CPU、内存、存储空间、IOPS 等物理资源的集合。Unit 也是资源调度的基本单位,其具有节点、Zone、Region 等位置属性,节点是服务器的抽象,Zone 是机房的抽象,Region 是地域的抽象,通过调整 Unit 的位置属性从而调整租户的部署方式。
-  资源池每个 Unit 都归属于一个资源池,每个资源池由若干个 Unit 组成,资源池是资源分配的基本单位,同一个资源池内的各个 Unit 具有相同的资源规格,即该资源池内 Unit 的物理资源大小都相同。 -![image.png](/img/solutions/multi_tenant/3.png)
-  如上图,展示了一个由 6 个 Unit 组成的资源池 a_pool,该资源池具有如下重要属性:
-- ZONE_LIST:描述了该资源池中的 Unit 分布在哪些 Zone,本例为 ZONE_LIST='zone1,zone2,zone3'。
-- UNIT_NUM:描述了 ZONE_LIST 中每个 Zone 中的 Unit 个数,本例为 UNIT_NUM=2。
-- UNIT_CONFIG_ID:描述了该资源池关联的资源规格,从而决定该资源池中每个 Unit 的物理资源大小,包括 CPU、内存、日志盘空间、IOPS 等。
- -  通过 Unit 的概念,我们将 OceanBase 数据库的物理概念和逻辑概念进行了关联。每个租户有若干 Unit ,分布于若干 Zone 的若干节点上。而每个节点上分布有若干个 Unit ,这些 Unit 归属于不同租户。概括的讲:集群由节点组成,节点是 Unit 的容器。租户由 Unit 组成,Unit 是数据库对象的容器。
-  创建租户时通过设置 RESOURCE_POOL_LIST,可以指定该租户关联到的资源池,从而该租户拥有指定资源池的 Unit。例如:设置租户 a 的 RESOURCE_POOL_LIST=('a_pool'),其部署图如下:
-![image.png](/img/solutions/multi_tenant/4.png)
-  该租户部署于 3 个 Zone,每个 Zone 有 2 个 Unit,可以通过调整 a_pool 的 UNIT_CONFIG_ID:描述了该资源池关联的资源规格,从而决定该资源池中每个 UNIT_CONFIG_ID 参数来动态调整租户的物理资源。
-  还可以通过调整 Unit 在同一个 Zone 内不同节点的分布(称为 Unit 迁移),从而达到 Zone 内不同节点间的负载均衡。节点故障时通过将其上的 Unit 迁移到同 Zone 内其他节点上,从而达到自动容灾恢复的目的。通过调整 Unit 在不同 Zone 的分布(变更租户的 Locality 属性),从而调整租户的部署模式,例如 “同城三中心”、“两地三中心”、“三地五中心” 等,从而具备不同的容灾等级。 - -## 体验多租户特性 -  详见 OceanBase 官网文档:[https://www.oceanbase.com/docs/common-oceanbase-database-1000000000033292](https://www.oceanbase.com/docs/common-oceanbase-database-1000000000033292) - -## 多租户间的隔离性 -  很多同学好奇 Oceanbase 怎么实现多租户之间的隔离。其实租户就像一个容器,这套容器里包含了 table,database,user 等逻辑对象;也包含了分给这个租户的内存,cpu,磁盘空间等物理资源。 - -### 逻辑对象隔离 -  不同租户的逻辑对象一定要处在不同的名字空间。逻辑对象实际是记录在内部表中的数据,为了保证名字空间独立,OceanBase 的每个租户都有自己一套完整的内部表,用于记录租户内逻辑对象的元数据信息,以此保证逻辑对象的隔离性。
-![image.png](/img/solutions/multi_tenant/5.png) - - -### 物理资源隔离 -  物理资源可以分为两类:一类是弹性资源,比如CPU,磁盘带宽等;另一类是刚性资源,比如内存,磁盘空间等。弹性资源是可以抢占的,刚性资源一旦被占用,除非占有者主动释放,否则是无法抢占的。
-  从定义上来说,刚性资源无法超卖,必须要强隔离;弹性资源可以一定程度上超卖。无论是刚性资源还是弹性资源,在observer里都有租户级别的记账,通过视图可以方便地查看。 -```bash -select * from DBA_OB_UNIT_CONFIGS; -+----------------+-----------------+---------+---------+-------------+---------------+----------+----------+-------------+ -| unit_config_id | NAME | MAX_CPU | MIN_CPU | MEMORY_SIZE | LOG_DISK_SIZE | MAX_IOPS | MIN_IOPS | IOPS_WEIGHT | -+----------------+-----------------+---------+---------+-------------+---------------+----------+----------+-------------+ -| 1 | sys_unit_config | 1 | 1 | 1073741824 | 2684354560 | 10000 | 10000 | 1 | -| 1001 | 1c1g | 1 | 1 | 1073741824 | 3221225472 | 10000 | 10000 | 1 | -| 1002 | 1c2g | 1 | 1 | 2147483648 | 6442450944 | 10000 | 10000 | 1 | -| 1003 | 2c2g | 2 | 2 | 2147483648 | 6442450944 | 20000 | 20000 | 2 | -| 1004 | 2c4g | 2 | 2 | 4294967296 | 12884901888 | 20000 | 20000 | 2 | -+----------------+-----------------+---------+---------+-------------+---------------+----------+----------+-------------+ -``` - -#### 内存和磁盘空间隔离 -  内存和磁盘空间的隔离机制比较简单,一旦超过配额,后续的资源申请就会失败。为了降低资源耗尽的情况,observer 内部会根据内存和磁盘空间的消耗情况控制写入的速度。 - -#### CPU 隔离 -  observer 最基础的 CPU 隔离是通过用户态调度,控制活跃线程数来实现的。每个租户有独立的线程池,线程池的规格是由租户规格和一些配置参数来决定的。
-![image.png](/img/solutions/multi_tenant/6.png)
-  同时,observer 也支持配置 cgroup,来实现 CPU 的隔离优化。cgroup 能对线程的 CPU 使用率进行精准的限制,达到租户之间 CPU 强隔离的效果。
![image.png](/img/solutions/multi_tenant/7.png) -```bash -observer - ├── tenant1 - │ └── tasks - │ ├── thread1 - │ ├── thread2 - │ └── ... - ├── tenant2 - │ └── tasks - │ ├── thread1 - │ ├── thread2 - │ └── ... - └── other -``` -  CPU 隔离能力详见 OceanBase 社区里的这篇博客[《为什么资源隔离对HTAP至关重要》](https://open.oceanbase.com/blog/10900412?_gl=1*5e9nw*_ga*MTk2NDc5MjkwMS4xNjc2ODgyMjQw*_ga_T35KTM57DZ*MTY5MjI0MDM3MC40NC4xLjE2OTIyNDE5MzAuNC4wLjA.)中 “OceanBase 资源隔离的实现效果” 部分。 - - -#### 磁盘 I/O 资源隔离 -  observer 内所有的 IO 都是异步 IO,并且是绕过 OS 的 direct IO,磁盘带宽的隔离是通过控制 observer 提交异步 IO 的时间间隔来实现的。具体的控制策略比较复杂,这里不作详述。
-  磁盘 IO 隔离能力详见 OceanBase 社区里的这篇博客[《OceanBase 4.1 解读:给用户足够灵活简单的IO隔离体验》](https://open.oceanbase.com/blog/3105048832?_gl=1*l1fhnb*_ga*MTk2NDc5MjkwMS4xNjc2ODgyMjQw*_ga_T35KTM57DZ*MTY5MjI0MDM3MC40NC4xLjE2OTIyNDE5MjQuMTAuMC4w)。 - - -# 用户案例 - -## 超大型金融机构某某人寿 - -### 业务挑战 - -- 快速增长的业务使得数据大规模增长,传统集中式数据库面对海量数据,难以维持性能; -- 保险业务的处理复杂,单一业务需多个系统完成,调用链比其他行业的业务更长、更复杂,确保复杂集合大交易量的稳定是保险业务数据库的挑战。 -- 期望实现数据库技术可控,摆脱对外依赖。与此同时,期望升级后实现数据库使用成本、维护成本的双降低。 - -### 解决方案 - -- 语法全面兼容,功能完全覆盖原有功能点,提供迁移工具与解决方案,避免上千万行代码重写,实现应用真正平滑迁移,高效完成核心数据库替换; -- OceanBase 多租户能力,实现动态弹性调整租户计算资源,敏捷的应对业务负载要求的变化,顺利通过季度、年度开门红等业务冲刺高峰; -- OceanBase 大集群管理模式,整合竖井式应用,集中化管控,平台化管理,统一资源分配,避免集中式架构的资源浪费。集群通过 F5 提供应用访问,多个集群使用同一套 OCP 集群进行运维管控。 - -![image.png](/img/solutions/multi_tenant/8.png) - -### 用户收益 - -- 速度:金融行业数据库升级整合范围最大、速度最快的大型金融机构,升级整合超 300 套原数据库,覆盖全部业务系统,迁移数据量达数百 TB,数据库装机量超过 2 万服务器核数; -- 成本:平台化管理能力提升,整合竖井式应用,集中化管控,统一资源分配和运维,业务数据库容量瘦身 78%,具有相同业务承载能力的 OceanBase 数据库软硬件成本缩减 75%; -- 效率:分析型大数据加工处理能力提升 300%,实现一份数据既做交易又做分析,不仅提高业务决策效率,整体资源利用效率和处理能力大幅提升; -- 安全:实现 RPO=0 的高级别跨城市容灾能力,满足金融行业分布式技术架构转型要求,全链路透明加密,提高数据全链路防泄漏能力; -- 绿色:数据库服务器及存储机柜数量利用率提高 300%,设备功率下降至原有 1/3。经测算,全年可节约电力约近千万度,有力践行国家双碳战略。 - - -## 多点 DMALL - -### 业务挑战 -  多点 DMALL 服务对象横跨国内、海外等众多零售商,在国内有物美、中百等大型商超,覆盖麦德龙、Seven Eleven 711 便利店等跨国零售商。同时,还为众多海内外品牌商提供服务,让品牌商、供应商、零售商能够链接起来,让数据和信息更好地流动,让服务对象能够更好地支撑服务 C 端用户。从生产商、品牌商、供应商,再到各个商场门店的零售商,最后到消费者,不难想象,超长的服务链路会产生超级庞大的数据量,系统的复杂度也将随着数据量呈指数级增长。在此背景下,多点零售 SaaS 系统数据库面临三大难题: - -- 运维复杂度高:多点 DMALL 使用微服务架构,全流程业务环节多,系统应用规模大,对应数据库的数量超过了 500 个。且随着系统不断迭代,数据的规模还在持续增加,运维管理难度越来越大。 -- 业务增长快,水平扩展需求增多:随着业务增的长,多点制定了出海战略,需要在海外展开业务,基于地区数据安全法的要求,需要独立部署一整套全新的系统去承接海外业务流量。在初始的部署阶段,对后期业务规模及数据增长速度是难以准确预估的。因此,数据库资源在初始阶段的分配就变得十分困难。为了节约成本,常见的做法是给到较少的部署资源。但很快多点发现,业务的快速增长,数据的增长特别迅猛,带给多点的是如何快速扩容这一难题。 -- 需要在同一个集群中服务大量商家:便利店/连锁商超的 SKU(最小存货单位)规模,从几千到几万,多点很难做到给每个商家独立部署一套系统。因此,多点 SaaS 系统支持上百个中小商家客户,所有商家产生的数据,在底层共享数据库资源。还有一个显著特点,在多点系统中存在非常大的单个租户,比如大型连锁商超,多点希望能让大型连锁商超所在的租户与其他租户有一定的资源隔离。 - -### 解决方案 - -- OceanBase 的大集群模式,将多个单实例整合到 OceanBase 集群中,进行统一管理,灵活调度,有效提高资源利用率。 -- 使用租户进行资源隔离,多个业务模块之间数据独立,按需透明升降配。 -- 通过 OceanBase 集群强大丰富的 Leader 分布和读写路由策略,将蚂蚁集团多年沉淀的高并发最佳实践输出给用户。 -- 借助 OMS ,在不停机的情况下全站业务向 OceanBase 实现高效快捷的迁移,业务仅需极少改造甚至零改造。 -- 通过 OceanBase 强大的智能管控平台,典型问题自动分析和感知,运维效率大幅提升。 - -![image.png](/img/solutions/multi_tenant/9.png) - -### 用户收益 - -- 基于大集群多租户,实现秒级的数据库实例资源扩缩容,在整体集群资源使用不变的前提下,稳定承载大量业务的高峰压力。 -- 在解决了业务扩展性难题的同时,也大大降低了 DBA 的运维成本以及出错概率。在机房级容灾方面,实现 RPO = 0,RTO < 8s,稳定支撑多点上亿级的用户量。 -- 基于 OceanBase 的高级压缩技术,在保证性能的同时,多点的数据存储空间相比原先节约了近 75%。同等硬件投入的前提下,多点可存储更多数据。 -- OceanBase 高度兼容 MySQL 的 SQL、存储过程及触发器等高级特性,对传统数据库具备高兼容能力,让多点可以平稳、平滑迁移过往数据,保证在过渡时期的大量业务不受影响。 \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/solutions/storage_compression.md b/docs/user_manual/user_best_practices/solutions/storage_compression.md deleted file mode 100644 index 1bc454923..000000000 --- a/docs/user_manual/user_best_practices/solutions/storage_compression.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -title: 历史数据归档场景 -weight: 2 ---- - -> 关键字:生命周期自动管理 | 低成本 | 超大容量 -> -> 通过 OceanBase 智能化的历史库迁移平台,帮助用户快速、安全的完成冷数据归档,一次配置即可实现自动化的周期管控。OceanBase 高压缩比的分布式存储引擎, 摒弃了传统数据库的定长数据块存储,采用基于 LSM-Tree 的存储架构和自适应压缩技术,创造性地解决了传统数据库无法平衡“性能”和“压缩比”的难题, - - - -# 历史数据归档场景 - -  在订单、交易、日志等业务场景中,数据总量会不断增加。而对于这些数据的访问往往和时间有很强的相关性,通常与当前时间越接近的数据越“热”,也就是说,这些数据可能会被频繁地修改与点查。热数据的访问更多是事务型负载和实时分析负载,其数据量在整个系统中的占比相对较低。而在系统中已经存在了一段时间的数据,被称为冷数据,这些数据的被查询次数相对没有那么频繁,也很少被修改。冷数据的访问通常是少量的事务型负载和一些 ad-hoc 或报表统计等分析型负载,而且冷数据通常是稳定运行的 IT 系统中数据量的主要部分。
  由于冷热数据有着明显的区别,将它们放在一套相同规格的环境中同等处理显然会浪费系统的资源,单个数据库的容量上限还可能会限制数据的存储。但是,将冷数据定期归档到更经济的存储介质中,访问数据时采用从归档数据中还原的方法,又会对历史数据的查询性能和系统的复杂度带来负面影响。因此,将数据分为线上库和历史库,将在线数据定期同步到历史库中的做法成为了越来越多系统的解决方案,通过在存储和计算成本更低的环境上部署历史库来降低成本和满足业务需求。 - - -# 解决方案 - - - -## 行业现状与挑战 - -- 数据增长加速:面对快速增长的在线数据,尤其例如新零售、支付等订单和交易场景,数据往往多呈现为流水型特征,即写入一段时间后不会再次访问或更新。 -- 成本高效率低:低频或零访问数据占用在线业务库的固态存储空间,造成大量硬件资源浪费,堆高企业 IT 成本,导致在线数据库体积臃肿、查询效率降低,给后续数据变更、扩展造成阻碍。 -- 传统方案风险高:传统数据归档方案往往是业务研发或 DBA 采用脚本或简单的同步工具进行,难以在并发和效率上有效控制,很容易对在线数据库产生影响,严重的甚至导致生产数据误删事故。 -- 运维管理复杂:多个业务对应的不同数据库、甚至不同表都可能有各异的归档周期和限定条件,会导致大量定时任务的逻辑维护复杂,耗时耗力。 - - -## 方案描述 - -- 基于 OceanBase 对低端硬件的友好兼容, OceanBase 历史库平台实现了归档任务配置图形化,周期管控自动化,数据迁移 + 校验 + 删除一键自动灰度执行等能力。稳定性方面提供了防导爆、智能限速、多粒度流控等机制,真正实现了数据归档的智能化运维。 -- 此方案历经蚂蚁集团核心业务场景验证,交易支付历史库单实例数据超过 6PB,采用上百台大容量机械盘的低成本硬件支撑,磁盘水位自动均衡,平稳运行多年,节省了大量机器资源。 - -![image.png](/img/solutions/storage_compression/1.png) - - -## 方案优势 - -- 可视化管理:任务创建与运行、进度大盘、一键暂停/恢复等基础操作图形化。 -- 智能化运维:令牌桶算法限速控制、断点续传、任务调度自动化管控等机制,以及宕机自动替换、自动扩缩容、防导爆等自愈手段,实现运维零干预。 -- 低成本:大容量 SATA 盘机型友好,结合 OceanBase 高压缩存储能力,单节点最大即可存储相当于传统数据库 400TB 数据。 -- 海量存储:适用在线业务瘦身,真正做到为数据归档减负。历史库集群可作为大容量关系型数据库使用,能稳定支撑写入量巨大但低频访问的业务查询需求,如监控、日志、审计核对等场景。 - - -# OceanBase 存储引擎和数据压缩技术介绍 - -  作为一款 HTAP 数据库产品, OceanBase 使用基于 LSM-Tree 架构的存储引擎,同时支持 OLTP 与 OLAP 负载,这种存储架构提供了优秀的数据压缩能力。在 OceanBase 中,增量数据会写入 clog 和 memtable 中, OceanBase 的 memtable 是内存中的 B+ 树索引,提供高效的事务处理能力。
  memtable 会定期通过 compaction 生成硬盘持久化数据 sstable ,多层 sstable 会采用 leveled compaction 策略进行增量数据重整。sstable 中数据块的存储分为两层,其中 2M 定长的数据块(宏块)作为 sstable 写入 I / O 的最小单元,存储在宏块中的变长数据块(微块)作为数据块压缩和读 I / O 的最小单元。
  ![image.png](/img/solutions/storage_compression/2.png)
  在这样的存储架构下, OceanBase 的数据压缩集中发生在 compaction 过程中 sstable 的写入时,数据的在线更新与压缩得到了解耦。批量落盘的特性使其采用更激进的压缩策略。OceanBase 从 2.0 版本开始引入了行列混存的微块存储格式( PAX ),充分利用了同一列数据的局部性和类型特征,在微块内部对一组行以列存的方式存储,并针对数据特征按列进行编码。变长的数据块和连续批量压缩的数据也可以让 OceanBase 通过同一个 sstable 中已经完成压缩的数据块的先验知识,对下一个数据块的压缩进行指导,在数据块中压缩尽量多的数据行,并选择更优的编码算法。
  与部分在 schema 上指定数据编码的数据库实现不同, OceanBase 选择了用户不感知的数据自适应编码,在给用户带来更小负担的同时降低了存储成本,从历史库角度而言,用户也不需要针对历史库数据做出过多压缩与编码相关的配置调整。 OceanBase 之所以能够在事务性能和压缩率之间取得更好的平衡,都得益于 LSM-Tree 的存储架构。
  当然, LSM-Tree 架构不是解决数据库压缩所有问题的万金油,如何通过数据压缩降低成本、提升性能是业界一直在讨论的话题。对 B+ 树类的存储引擎进行更高效的压缩也有很多探索,比如基于可计算存储硬件的工作,利用存储硬件内部的透明压缩能力对 B+ 树类存储引擎的数据压缩进行优化,使其写放大达到了接近 LSM-Tree 架构存储引擎的效果。但 LSM-tree 中内存数据页更新与数据块落盘解耦,和 sstable 数据紧凑排布的特点,使得 LSM-tree 相对 B+ 树类存储引擎,仍然更适合在对查询/更新带来更少负面影响的前提下实现更高效的数据压缩。 - - -## OceanBase 的数据库压缩技术 - -  OceanBase 支持不感知数据特征的通用压缩 ( compression ) 和感知数据特征并按列进行压缩的数据编码 ( encoding )。这两种压缩方式是正交的,也就是说,可以对一个数据块先进行编码,然后再进行通用压缩,来实现更高的压缩率。
  OceanBase 中的通用压缩是在不感知微块内部数据格式的前提下,将整个微块通过通用压缩算法进行压缩,依赖通用压缩算法来检测并消除微块中的数据冗余。目前 OceanBase 支持用户选择 zlib 、 snappy 、 zstd 、 lz4 算法进行通用压缩。用户可以根据表的应用场景,通过 DDL 对指定表的通用压缩算法进行配置和变更。
  由于通用压缩后的数据块在读取进行扫描前需要对整个微块进行解压,会消耗一定 CPU 并带来 overhead。为了降低解压数据块对于查询性能的影响, OceanBase 将解压数据的动作交给异步 I / O 线程来进行,并按需将解压后的数据块放在 block cache 中。这样结合查询时对预读 ( prefetching ) 技术的应用,可以为查询处理线程提供数据块的流水线,消除解压带来的额外开销。
  通用压缩的优点是对被压缩的数据没有任何假设,任何数据都可能找到模式并压缩,但往往出于平衡压缩性能和压缩率的考虑,通用压缩算法会放弃对一些复杂数据冗余模式的探测和压缩。对于关系型数据库来说,系统对数据库内存储的结构化数据有着更多的先验知识,利用这些先验知识可以对数据进行更高效的压缩。 - - -### OceanBase 的数据编码算法 - -- 当通过一列数据存储城市、性别、产品分类等具有类型属性的值时,这些列数据块内部数据的基数( cardinality )也会比较小,这时数据库可以直接在用户数据字段上建立字典,来实现更高的压缩率; -- 当数据按时序插入数据库,这些插入的数据行中的时间相关字段、自增序列等数据的值域会相对较小,也会有单调递增等特性,利用这些特性,数据库可以更方便地为这些数据做 bit-packing 、差值等编码。 - -  为了实现更高的压缩比,帮助用户大幅降低存储成本, OceanBase 设计了多种编码算法,最终在 OceanBase 的负载上实现了很好的压缩效果。 OceanBase 根据实际业务场景需求实现了单列数据的 bit-packing 编码、字符串 HEX 编码、字典编码、 RLE 编码、常量编码、数值差值编码、定长字符串差值编码,同时,创新地引入了列间等值编码和列间子串编码,能够分别对数据库中一列数据或几列数据间可能产生的不同类型数据冗余进行压缩。 - - -#### 降低存储的位宽:Bit-packing 和 HEX 编码 - -  Bit-packing 和 HEX 编码类似,都是在压缩数据的基数较小时,通过更小位宽的编码来表示原数据。而且这两种编码可以与其他编码叠加,对于其他编码产生的数值或字符串数据,都可以再通过 bit-packing 或 HEX 编码进一步去除冗余。
  ![image.png](/img/solutions/storage_compression/3.0.png)
  (bit-packing)
  ![image.png](/img/solutions/storage_compression/3.5.png)
  ( HEX 编码) - - -#### 单列数据去重:字典编码和 RLE 编码等 - -  字典编码则可以通过在数据块内建立字典,来对低基数的数据进行压缩。当低基数的数据在微块内的分布符合对应的特征时,也可以使用游程编码/常量编码等方法进行进一步的压缩。
  ![image.png](/img/solutions/storage_compression/4.png)
  (字典编码/RLE 编码) - - -#### 利用数据的值域压缩:差值编码等 - -  差值编码也是常用的编码方法, OceanBase 中的差值编码分为数值差值编码和定长字符串差值编码。数值差值编码主要用来对值域较小的数值类数据类型进行压缩。对于日期、时间戳等数据,或其他临近数据差值较小的数值类数据,可以只存储最小值,每行存储原数据与最小值的差值。定长字符串编码则可以比较好地对人工生成的 ID,如订单号/身份证号、url 等有一定模式的字符串进行压缩,对一个微块的数据存储一个模式串,每行额外存储与模式串不同的子串差值,来达到更好的压缩效果。
  ![image.png](/img/solutions/storage_compression/5.png)
  (整形差值)
  ![image.png](/img/solutions/storage_compression/6.png)
  (字符串差值) - - -#### 减小多列数据冗余:列间编码 - -  为了利用不同列间数据的相似性增强压缩效果,OceanBase 引入了列间编码。通常情况下,列存数据库只会对数据在列内部进行编码,但在实际应用中有很多表除了同一列数据之间存在相似性,不同列的数据之间也可能有一定的关系,利用这种关系可以通过一列数据表示另外一列数据的部分信息。
  列间编码可以对复合列、系统生成的数据做出更好的压缩,也能够降低在数据表设计范式上的问题导致的数据冗余。 - - -#### 自适应压缩技术:让数据库选择编码算法 - -  数据编码的压缩效果不仅与表的 schema 相关,同时还与数据的分布,微块内数据值域等数据本身的特征相关,这也就意味着比较难以在用户设计表数据模型时指定列编码来实现最好的压缩效果。为了减轻用户的使用负担,也为了实现更好的压缩效果,OceanBase 支持在合并过程中分析数据类型、值域、NDV 等特征,结合 compaction 任务中上一个微块对应列选择的编码算法和压缩率自适应地探测合适的编码,对同一列在不同数据块中支持使用不同的算法来进行编码,也保证了选择编码算法的开销在可接受的区间内。 - - -### 降低数据编码对查询性能影响的能力 - -  为了能够更好地平衡压缩效果和查询的性能,我们在设计数据编码格式时也考虑到了对查询性能带来的影响。 - - -#### 行级粒度数据随机访问 - -  通用压缩中如果要访问一个压缩块中的一部分数据通常需要将整个数据块解压后访问,某些分析型系统的数据编码大多面向扫描的场景,点查的场景比较少,因此采用了在访问某一行数据时需要对相邻数据行或数据块内读取行之前所有行进行解码计算的数据编码的格式(如 PFor 等差值编码)。
  OceanBase 需要更好地支持事务型负载,就意味着要支持相对更高效的点查,因此 OceanBase 在设计数据编码格式时保证了编码后的数据是可以以行为粒度随机访问的。也就是在对某一行进行点查时只需要对这一行相关的元数据进行访问并解码,减小了随机点查时的计算放大。同时对于编码格式的微块,解码数据所需要的所有元数据都存储在微块内,让数据微块有自解释的能力,也在解码时提供了更好的内存局部性。 - - -#### 缓存解码器 - -  在 OceanBase 目前的数据解码实现中,每一列数据都需要初始化一个解码器对象来解析数据,构造解码器时会需要进行一些计算和内存分配,为了进一步减小访问编码数据时的 RT ,OceanBase 会将数据的解码器和数据块一起缓存在 block cache 中,访问 cache 中数据块时可以直接通过缓存的解码器解析数据。当不能命中 block cache 中缓存的解码器时,OceanBase 还会为解码器用到的元数据内存和对象构建缓存池,在不同查询间复用这些内存和对象。
  通过上述细节上的优化,行列混存格式的 sstable 编码数据也可以很好地支持事务型负载。而且由于编码数据行列混存的格式,使得在分析型查询的处理上,编码数据有着和列存数据相似的特性,数据分布更紧凑,对 CPU cache 更加友好。这些特性使列存常用的优化手段也能应用于分析型查询优化中,充分利用 SIMD 等方法来提供更高效的分析型负载处理。 - - -#### 计算下推 - -  由于编码数据中会存储有序字典、 null bitmap 、常量等可以描述数据分布的元数据,在扫描数据时可以利用这些数据对于部分过滤,聚合算子的执行过程进行优化,实现在未解码的数据上直接进行计算。 OceanBase 对分析处理能力进行了大幅的优化,其中包括聚合与过滤计算下推到存储层执行,和在向量化引擎中利用编码数据的列存特征进行向量化的批量解码等特性。在查询时充分利用了编码元数据和编码数据列存储的局部性,在编码数据上直接进行计算,大幅提高了下推算子的执行效率和向量化引擎中的数据解码效率。基于数据编码的计算下推和向量化解码也成为了支持 OceanBase 高效处理分析型负载,在 TPC-H benchmark 中达到优秀性能指标的重要功能。
  ![image.png](/img/solutions/storage_compression/7.png) - - -## 据编码压缩的基础测试 - -  不同的压缩方式如何影响 OceanBase 的压缩效果,以下通过一个简单的测试进行观察。使用 OceanBase 4.0 版本分别在交易场景的 TPC-H 10g 的数据模型和用户行为日志场景的 IJCAI-16 Brick-and-Mortar Store Recommendation Dataset 数据集上对 OceanBase 的压缩率进行测试。 - -- TPC-H 是对订单,交易场景的建模,对 TPC-H 模型中数据量比较大的两张表,即存储订单的 ORDERS 表和存储商品信息的 LINEITEM 表的压缩率进行统计。在 OceanBase 默认配置( zstd + encoding )下,这两张表的压缩率可以达到 4.6 左右,相较只开启 encoding 或 zstd 压缩时提升明显。 -- IJCAI-16 taobao user log 则是淘宝脱敏后的真实业务数据,存储了用户浏览商品时的行为日志。在 OceanBase 默认配置( zstd + encoding )下压缩率可以达到 9.9 ,只开启 encoding 压缩率可以达到 8.3 ,只开启 zstd 压缩率为 6.0 。 - -![image.png](/img/solutions/storage_compression/8.png) - - -## OceanBase 存储引擎技术为何能更好地支持历史库场景? - -  OceanBase 存储引擎的数据库压缩功能在设计上希望能够在用户少感知、不感知存储格式的前提下,在不降低事务型负载性能的同时降低存储空间和存储成本,同时提升分析型负载的性能。这样的设计与历史库的设计不谋而合。高度压缩的数据既能够帮助历史库数据降低至少 50% 的存储成本,高效的写入查询和统一的配置接口又能够帮助业务增效。
  对于历史库数据同步的需求, OceanBase 的 LSM-Tree 存储引擎天生具有高效的写入性能,既能够通过旁路导入高效处理定期的批量数据同步,又能够承载一些实时数据同步和历史库数据修改的场景。
  对于历史库数据的定期跑批报表,和一些 ad-hoc 的分析型查询带来的大量数据扫描的需求,因为历史库中增量数据较少,所以绝大多数数据都存储在基线的 SSTable 中,这时计算下推可以只扫描基线数据,绕过了 LSM-Tree 架构常见的读放大问题。而且支持在压缩数据上执行下推算子和向量化解码的压缩格式可以轻松地处理大量数据查询和计算。
  对于大量历史数据存储的需求, OceanBase 的 SSTable 存储格式和数据编码压缩功能可以使 OceanBase 更轻松地支持超大容量的数据存储。而且高度压缩的数据和在同等硬件下更高效的查询性能也能够大幅度降低存储和计算的成本。
  此外,企业可以选择将历史库所在的集群部署在更经济的硬件上,但是对数据库进行运维基本不需要感知数据编码与压缩的相关配置,应用开发也可以做到在线库和历史库使用完全相同的访问接口,简化应用代码和架构。
  这些特点让越来越多的企业开始在历史库场景使用 OceanBase 进行降本增效的实践。OceanBase 也不断在存储架构,降本增效方面做出更多的探索。 - - - -# 用户案例 - - - -## 支付宝 - - - -### 业务挑战 - -- 从 2013 年开始,支付宝交易核心已经面临架构上的水平拆分上限了,如果保持当前架构下仅针对业务进行水平拆分扩容,需要购买更多的 Oracle 数据库,这将带来数据库成本近乎直线的攀升。该如何平衡成本和稳定性?这个问题是彼时支付宝工程师面对的难题。要么购买更多的机器并投入更大的精力进行业务拆分,能够保证短期内的数据库性能与稳定性,要么重新选择一款不丢数据且稳定性高的数据库。 -- 在 2014 年,支付宝原有的数据库系统已经扩容到最大集群量,但仍然无法满足历史库的需求。当时作为备选的蚂蚁集团完全自研的原生分布式数据库 OceanBase 经受住了试验,支撑住了系统的稳定性。此举使支付宝的交易系统数据库切换为 OceanBase,保障了历史库系统的稳定性与高性能。 - - -### 解决方案 - -  历史库目的是为了解决因为业务增长引发的数据库存储空间问题。通过性能换成本的方式,将过去不再使用的业务数据或查询很少的数据,搬迁到性能低但存储量大的机型构成的集群中,降低线上数据库存储带来的开销。针对历史库的需求,需要一个迁移程序将冷数据从在线库迁移至历史库,并且保证在线库和历史库都持续可用,不需要停机切流。因此,有几点特殊的需求: - -- 考虑数据量比较大,需要支持断点续传。 -- 由于交易历史库有一些表之间有关联,需要具备主子表维度迁移的功能。 -- 需要具备删除已经迁移的数据的功能。 - -  迁移工具 OMS 包括迁移、校验、删除三种任务模式。通过多线程启动对应的任务,并将相关迁移任务、进度和结果写入 metadb ,以便监控任务进度和支持断点续传。 - - -#### 历史库平台架构 - -  历史库平台为数据提供了更长生命周期管理能力。历史库平台通常由在线数据库、历史库客户端、历史库管控平台、历史数据库集群组成,为用户提供一站式的数据存储、归档解决方案。通过历史库管控平台,用户可以方便地配置迁移任务,指定规则将符合条件的非活跃数据从在线数据库迁移到成本更低的历史 OceanBase 数据库集群中。同时,历史库平台提供多维度的限速能力,以及多项目间优先级调度功能。用户通过配置限速减少迁移时对业务的影响,通过配置优先级可管理多套集群,满足多项目同时运行。待数据迁移完成后,提供数据校验、校验成功后删除在线数据配套功能,方便实用。
  经过支付宝业务的打磨,历史库平台(见下图)已经支撑支付宝内部交易、支付、账务等多个重要系统,节省了支付宝内部数据存储成本。同时,在网商银行也有广泛的使用场景。
  ![image.png](/img/solutions/storage_compression/9.png)
  从图中可见,历史库平台包含三大板块:在线数据库、历史数据库集群、历史库管控平台。 - -- 在线数据库,用于存放应用常常需要访问的数据。通常会采用更高规格配置的服务器,提供高性能的处理能力。目前已支持 OceanBase , MySQL , Oracle 作为数据源。 -- 历史数据库集群用于存放应用产生的终态数据,根据应用需求不同,即可以作为数据归档存储的集群不对应用提供访问,也可以满足应用的访问需求。采用成本更低的 SATA 盘来搭建 OceanBase 数据库集群。其中的历史库客户端用于处理用户发起的迁移、校验、删除任务。支付宝内部实现了多维度的限速,根据需求不同可以灵活地提供集群限速和表限速功能,最大程度的避免了任务对在线库应用流量的影响。 -- 历史库管控平台是用户对历史库进行各项操作的运维管理平台,提供权限管理、任务配置、任务监控等功能。 - - -### 用户收益 - -  支付宝当前已建设 20 多个历史库集群,在支付宝内部已覆盖交易、支付、充值、会员、账务等几乎所有核心业务,总数据量 95 PB ,每月增量 3 PB 。其中,最大的交易支付集群组,数据量 15 PB ,每日数据增量可达到 50 TB 。支付宝历史库的实践,带来的收益显著,主要包括以下三点: - -- 成本下降 80% 左右。 - -  由于历史库采用成本更低的 SATA 盘来搭建 OceanBase 数据库集群,单位空间磁盘成本降低到线上机器的 30% 。同时使用更高压缩比的 zstd 压缩算法,使得总体成本下降 80% 左右。如果线上是 MySQL 、 Oracle 等传统数据库,那么成本会降低更多。因为 OceanBase 本身的数据编码、压缩以及 LSM-Tree 的存储架构等,使得存储成本只有传统数据库的 1/3 。 - -- 弹性伸缩能力降低运维成本。 - -  历史库使用 OceanBase 三副本架构,每个 zone 中有多个 OBServer ,通过分区将数据分散到多个 unit 中。OceanBase 具备业务无感知的弹性伸缩能力,并且可以通过扩容节点增加容量、提升性能。这意味着历史库可以不再受限于磁盘大小,通过少数集群就可以涵盖所有业务的历史库,降低运维成本。
  目前历史数据是永久保存的,随着时间的推移,历史库的容量占用也会越来越高。依赖 OceanBase 本身的高扩展性,通过横向扩展 OBServer ,增加 unit_number 即可实现容量的扩容。 - -- 数据强一致,故障快速修复。 - -  数据迁移相当于一份数据归档及逻辑备份,如果这些数据发生了丢失,那么后续需要做审计、历史数据查询的时候,数据就对不上了。这对于很多业务尤其是金融业务而言是无法忍受的。
  OceanBase 底层使用 Paxos 一致性算法,当单台 OBServer 宕机时,可以在 30s 内快速恢复,并保证数据的强一致,降低对线上查询及归档任务的影响。 - - -## 携程 - - - -### 业务挑战 - -- 随着订单业务量的增加,业务数据迅猛增长,传统数据库的存储瓶颈以及性能不佳问题越来越明显。 -- 不仅运维成本和复杂度有所增加,同时需要不断对应用进行改造和适配以解决不断分库分表带来的问题。 - - -### 解决方案 - -- 相比传统的集中式数据库 MySQL,OceanBase 在存储层面极致的压缩能力,有效降低企业使用数据库的硬件成本。 -- OceanBase 具备灵活的资源扩展能力,根据业务实际发展情况可以动态的进行计算和存储能力的线性扩展,支撑海量数据的存储和计算,同时很好地应对未来的业务增长要求。 -- 在数据迁移方面,因 OceanBase 兼容 MySQL 协议与语法,因此 OMS 可以做到平滑迁移,可大幅降低业务迁移和改造成本。OMS 通过全量迁移、增量迁移、反向迁移,保障数据迁过程中的强一致,并提供数据同步到 kafka 等消息队列中的能力。 - -![image.png](/img/solutions/storage_compression/10.png) - - -### 用户收益 - -- 运维更加高效与便捷:单集群替换数十套 MySQL 环境,运维管理成本大大降低,同时管理更加方便。使用普通的 PC 服务器即可构建超高吞吐的 OceanBase 集群,无需分库分表,快速按需扩展,为携程历史库在水平扩展过程中提供了平滑的成本增长曲线。 -- 低成本:支撑上百 TB 数据存储场景且性能和稳定性有保证,同时相比较之前的方案,OceanBase 方案的存储成本降低 85%,降本效果明显。 -- 数据同步性能提升:数据迁移对业务透明,OMS 支持全量数据迁移、增量数据同步,支持主流数据库的一站式数据迁移。数据从上游写入到下游 OceanBase 响应延迟更小,数据同步速度更快,同步延迟时间减少 3/4。 -- 数据写入性能优秀:OceanBase 的无共享架构、分区级主副本打散,以及并行执行框架提供的 ParallelDML 能力,真正实现了高效的多节点写入。利用该特性,数据写入性能提升数倍,能够从容应对携程历史库的超高并发数据写入需求。 diff --git a/docs/user_manual/user_best_practices/trouble_shooting/_index.md b/docs/user_manual/user_best_practices/trouble_shooting/_index.md deleted file mode 100644 index 4253980f6..000000000 --- a/docs/user_manual/user_best_practices/trouble_shooting/_index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 常见问题排查 -bookCollapseSection: false -weight: 7 ---- diff --git a/docs/user_manual/user_best_practices/trouble_shooting/collection_stack.md b/docs/user_manual/user_best_practices/trouble_shooting/collection_stack.md deleted file mode 100644 index c346ec5f7..000000000 --- a/docs/user_manual/user_best_practices/trouble_shooting/collection_stack.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -title: OBServer 节点 core 掉后如何收集堆栈 -weight: 2 ---- -# **OBServer 节点 core 掉后如何收集堆栈** - -本文介绍 OBServer 节点 core 掉后如何排查确认,并收集堆栈。 - -## **问题现象** - -正常运行的 OBServer 突然异常退出。 - -## **问题排查** - -可以通过如下几种方法排查 OBServer 是否 core 掉。 - -- 执行如下命令查看 OBServer 进程是否存在,若输出为 0 表示进程不存在。 - - ```shell - [root@x.x.x.x ~]$ ps -ef | grep 3.1.5 | grep -v grep | wc -l - 0 - ``` - -- 查看对应的 OBServer 日志里是否输出 `CRASH ERROR` 关键字,如下所示。 - - ```shell - [root@x.x.x.x ~]$ grep "CRASH ERROR" /home/admin/ob3.1.5/log/observer.log - CRASH ERROR!!! sig=11, sig_code=0, sig_addr=59ef, timestamp=1683513011732829, tid=27670, tname=observer, trace_id=0-0, extra_info=((null)), lbt=0x9bac9c8 0x9b9d248 0x2ac7a28ac62f 0x2ac7a2f8a9fd 0x2ac7a2f8a893 0x9461d97 0x22ed172 0x2ac7a2ee7554 0x22ebe28 - ``` - -- 查看对应的 OBServer 机器 `/etc/sysctl.conf` 文件中指定的 kernel.core_pattern 位置是否生成对应的 core 文件,如下所示。 - - ```shell - [root@x.x.x.x ~]$ grep "kernel.core_pattern" /etc/sysctl.conf - kernel.core_pattern = /home/admin/core_test/core-%e-%p-%t - - [root@x.x.x.x ~]$ ls -l /home/admin/core_test/core-observer-27670-1683513011 - -rw------- 1 admin admin 8723914752 5月 8 10:30 /home/admin/core_test/core-observer-27670-1683513011 - ``` - - > **说明** - > - > - 如果没有指定 kernel.core_pattern,默认会在 OceanBase 数据库的 `home_path` 路径下生成 `core.${ob_pid}` 的文件。 - > - > - 可以通过 ulimit -a 或者 ulimit -c 查看当前资源的限制,如果设置为 0 或者很小则会在发生节点 core 时无法生产 core 文件的情况。 - -## **收集堆栈** - -### **安装 debuginfo** - -在收集堆栈之前,需要先安装一下 oceanbase-ce-debuginfo 包。 - -> **注意** -> -> 需安装对应 OceanBase 数据库版本的 oceanbase-ce-debuginfo 包,比如当前使用的 OceanBase 数据库版本为:`Server version: 5.7.25 OceanBase 3.1.5 (r100000252023041721-6d73a6764190f46bbc0805dc27eea5ab08e1920c) (Built Apr 17 2023 21:10:15)`,则下载对应的 V3.1.5 版本 oceanbase-ce-debuginfo 包。 - -1. 下载的 oceanbase-ce-debuginfo 包 - - ```shell - wget https://mirrors.aliyun.com/oceanbase/community/stable/el/7/x86_64/oceanbase-ce-debuginfo-3.1.5-100000252023041721.el7.x86_64.rpm - ``` - -2. 安装 debuginfo - - ```shell - yum install oceanbase-ce-debuginfo-3.1.5-100000252023041721.el7.x86_64.rpm -y - ``` - - 如果执行安装命令时提示如下内容,您可以使用 rpm --force 方式安装,示例入下。 - - 提示信息: - - ```shell - file /usr/lib/debug from install of oceanbase-ce-debuginfo-3.1.5-100000252023041721.el7.x86_64 conflicts with file from package filesystem-3.2-25.el7.x86_64 - ``` - - rpm 命令安装: - - ```shell - rpm -ivh --force oceanbase-ce-debuginfo-3.1.5-100000252023041721.el7.x86_64.rpm - ``` - -3. 将安装后的 observer.debug 文件拷贝到跟 OBServer 可执行文件同一级目录下 - - ```shell - [root@x.x.x.x opt]$ rpm -ql oceanbase-ce-debuginfo | grep observer.debug - /usr/lib/debug/home/admin/oceanbase/bin/observer.debug - [root@x.x.x.x opt]$ cp /usr/lib/debug/home/admin/oceanbase/bin/observer.debug /home/admin/ob3.1.5/bin/ - ``` - -### **收集堆栈** - -我们需要借助 addr2line 或者 gdb 来收集发生 core 时刻的堆栈: - -1. 使用 addr2line 收集堆栈 - - ```shell - # 如果没有 addr2line 命令,需要先安装 - yum install -y binutils - - addr2line -pCfe /home/admin/ob3.1.5/bin/observer 0x9bac9c8 0x9b9d248 0x2b910122b62f 0x2b91019098ed 0x2b9101909783 0x946be28 0x22ed172 0x2b9101866554 0x22e - safe_backtrace at ??:? - oceanbase::common::coredump_cb(int, siginfo_t*) at ??:? - ?? ??:0 - ?? ??:0 - ?? ??:0 - oceanbase::observer::ObServer::wait() at ??:? - main at ??:? - ?? ??:0 - _start at ??:? - ``` - -2. 使用 gdb 命令收集 - -## **堆栈分析** - -可以将堆栈信息发送到社区问答或者给到对应的社区支持人员,社区同学会帮忙分析处理。 diff --git a/docs/user_manual/user_best_practices/trouble_shooting/observer_high_memory.md b/docs/user_manual/user_best_practices/trouble_shooting/observer_high_memory.md deleted file mode 100644 index afa284605..000000000 --- a/docs/user_manual/user_best_practices/trouble_shooting/observer_high_memory.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: OBServer 内存占用高 -weight: 3 ---- - -# **OBServer 内存占用高** - -## **场景:内存分配器缓存占用内存高** - -**现象** - -部署时配置 memory_limit 为48G, 内存一直用到46G, 并且不会下降,持续上升,虽然没有超过48G,但是因为 ocp 主机一共 64G 内存,很容易就触发内存告警阀值,扰乱真正有问题的告警消息 - -**查看当前内存配置** - -```sql -select zone,svr_ip,svr_port,name,value from __all_virtual_sys_parameter_stat where name in ('memory_limit','memory_limit_percentage','system_memory') order by svr_ip,svr_port; -``` - -**排查方法** - -登录 OBServer 服务器,进入observer日志目录, 搜索observer.log中"CHUNK_MGR"关键字 -```bash -grep 'CHUNK_MGR' observer.log -``` - -结果如下,其中 freelist_hold 表示缓存内存,可以看到有三十多G内存是被缓存的没有释放 - -```bash -[2023-03-24 16:44:10.771913] INFO [COMMON] ob_tenant_mgr.cpp:568 [3720][2][Y0-0000000000000000] [lt=4] [dc=0] [CHUNK_MGR] free=15982 pushes=13471449 pops=13455467 limit= 51,539,607,552 hold= 49,956,257,792 used= 16,439,574,528 freelist_hold= 33,516,683,264 maps= 258,350 unmaps= 250,521 large_maps= 255,745 large_unmaps= 250,459 memalign=0 virtual_memory_used= 55,661,019,136 -``` - -**处理方法** - -设置memory_chunk_cache_size参数为10G,也就是最多缓存10G。该参数可根据实际场景动态调整。 - -```sql -alter system set memory_chunk_cache_size="10G"; -show parameters like "memory_chunk_cache_size"; -``` - -处理完成后,内存马上就下来了,使用率恢复正常。 - -**注意** - -缓存值是由 memory_chunk_cache_size 参数来控制的,默认为 0 不需要配置。 diff --git a/docs/user_manual/user_best_practices/trouble_shooting/record_lock_troubleshoot.md b/docs/user_manual/user_best_practices/trouble_shooting/record_lock_troubleshoot.md deleted file mode 100644 index 14332304a..000000000 --- a/docs/user_manual/user_best_practices/trouble_shooting/record_lock_troubleshoot.md +++ /dev/null @@ -1,300 +0,0 @@ ---- -title: 行锁问题排查 -weight: 1 ---- - -# **行锁问题排查** - -本文介绍行锁问题的排查思路,并提供部分排查案例。 - -## **适用版本** - -OceanBase 4.x - -> **说明** -> -> OceanBase 3.x 版本行锁问题排查可以参考:https://www.oceanbase.com/knowledge-base/oceanbase-database-20000000016?back=kb. - -## **锁冲突问题的排查思路** - -在业务环境中,可以将锁抽象为两个与行锁有密切关系的对象,即行锁的持有者与等待者。而行锁的持有者与等待者都是事务的一部分,因此在监控活跃事务中监控锁的持有者与等待者会对锁冲突的问题排查提供很大的帮助。 - -OceanBase 数据库提供了以下虚拟表,分别用于监控活跃事务、行锁持有者与行锁等待者。 - -**\_\_all_virtual_trans_stat:用于监控活跃事务。** - -各主要表列的说明如下表所示。 - -| **列名** | **说明** | -| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| svr_ip | 表示创建该事务上下文的 OBServer 节点的 IP 地址。 | -| session_id | 表示该事务上下文所属会话的唯一 ID。 | -| trans_id | 表示该事务的唯一 ID。 | -| participants | 表示当前事务的参与者列表。 | -| ctx_create_time | 表示事务上下文的创建时间。 | -| ref_cnt | 表示事务上下文当前的引用计数。 | -| state | 表示事务上下文当前的状态。 | -| part_trans_action | 表示当前语句处于的执行阶段,可选值为 1、2、3、4,分别代表如下含义。
1:说明语句的 task 正在执行。
2:说明语句 task 执行完成。
3:说明进入了事务提交阶段。
4:说明事务正在进入回滚。 | -| is_exiting | 表示当前事务上下文是否正在退出。 | -| pending_log_size | 表示事务内存中有多少数据量需要去写 clog。 | - -**\_\_all_virtual_trans_lock_stat:记录了当前集群中所有活跃事务持有行锁的相关信息。** - -各主要表列的说明如下表所示 - -| **列名** | **说明** | -| ---------------- | ----------------------------------------------------------------------- | -| rowkey | 表示持有锁的行的 rowkey。 | -| session_id | 表示持有锁的事务所属的会话唯一 ID。 | -| proxy_session_id | 表示持有锁的事务所属客户端 OBProxy/Java Client 对应的 IP 地址与端口号。 | -| trans_id | 表示持有锁的事务的唯一 ID。 | - -**\_\_all_virtual_lock_wait_stat:统计了当前集群中所有等待行锁的请求或语句的相关信息。** - -各主要表列的说明如下表所示 - -| **列名** | **说明** | -| ---------------- | --------------------------------------- | -| session_id | 等待锁的事务所属的会话唯一 ID。 | -| tablet_id | 表示底层分片的 tablet_id。 | -| lock_ts | 表示该请求开始等待锁的时间点。 | -| abs_timeout | 表示等待锁的语句的绝对超时时间。 | -| try_lock_times | 表示等待锁的语句重试加锁的次数。 | -| block_session_id | 表示在该行第一个等待事务的 session_id。 | - -> **说明:** -> -> - 等待锁分为两种情况,即写写冲突与读写冲突。对于读写冲突的情况,是由于事务中读语句的 read_snapshot_version 大于写语句的 prepare_version,但 commit_version 不能确定,因此需要等待写事务完成后才能读到写语句的修改 -> -> - \_all_virtual_lock_wait_stat 表中主要用于展示写写冲突的情况。 - -## **锁冲突问题的排查案例** - -### **场景一** - -业务使用了较大的超时事件,且存在一个会话中的未知长事务持有锁,阻塞了其他事务的执行,需要找到并停止该长事务。 - -#### **方案一:通过无法加锁的事务查询持有锁的事务** - -1. 通过 show full processlist; 获取无法加锁的事务的 session_id,找到等待锁的行的 rowkey。 - - ```sql - MySQL [oceanbase]> select * from __all_virtual_lock_wait_stat where session_id=3222247256 \G - *************************** 1. row *************************** - svr_ip: x.x.x.x - svr_port: 7801 - tenant_id: 1 - tablet_id: 200002 - rowkey: {"INT":1} - addr: 47882047755232 - need_wait: 1 - recv_ts: 1684727706118418 - lock_ts: 1684727706119706 - abs_timeout: 1684727716018418 - try_lock_times: 1 - time_after_recv: 3750212 - session_id: 3222247256 - block_session_id: 3222247256 - type: 0 - lock_mode: 0 - last_compact_cnt: 0 - total_update_cnt: 2 - 1 row in set (0.00 sec) - ``` - -2. 可以发现等待锁的事务在等待主键为 pk 的行。 - - ```sql - MySQL [oceanbase]> select * from __all_virtual_trans_lock_stat where tablet_id=200002 and rowkey like '%{"INT":1} %'\G - *************************** 1. row *************************** - tenant_id: 1 - trans_id: {txid:4008048} - svr_ip: x.x.x.x - svr_port: 7801 - ls_id: 1 - table_id: 0 - tablet_id: 200002 - rowkey: rowkey_object=[{"INT":1}] - session_id: 3222015200 - proxy_session_id: NULL - ctx_create_time: 2023-05-22 11:45:35.025896 - expired_time: 2023-05-23 11:45:35.024835 - row_lock_addr: 0 - 1 row in set (0.01 sec) - ``` - -3. 根据查到的 session_id 停止该事务的会话 - - ```sql - MySQL [oceanbase]> kill 3222015200; - Query OK, 0 rows affected (0.00 sec) - ``` - -**方案二:通过查询长事务** - -1. 根据事务执行时间,找到执行时间最长且未结束事务的 trans_id。 - - ```sql - MySQL [oceanbase]> select * from __all_virtual_trans_stat where session_id!=0 order by ctx_create_time desc limit 5\G - *************************** 1. row *************************** - tenant_id: 1 - svr_ip: x.x.x.x - svr_port: 7801 - trans_type: 0 - trans_id: 4210669 - session_id: 3222110265 - scheduler_addr: "x.x.x.x:7801" - is_decided: 0 - ls_id: 1 - participants: NULL - ctx_create_time: 2023-05-22 16:40:44.737797 - expired_time: 2023-05-23 16:40:44.737797 - ref_cnt: 2 - last_op_sn: 7 - pending_write: 0 - state: 10 - part_trans_action: 2 - trans_ctx_addr: 0x2b8c278137d0 - mem_ctx_id: -1 - pending_log_size: 131 - flushed_log_size: 0 - role: 0 - is_exiting: 0 - coordinator: -1 - last_request_time: 2023-05-22 16:41:49.754470 - gtrid: NULL - bqual: NULL - format_id: -1 - ``` - -2. 通过事务的 trans_id 找到其所持有的所有锁,并根据 rowkey 明确哪一个是需要停止的服务,相同的 trans_id 说明是同一个事务内的不同行锁信息。 - - ```sql - MySQL [oceanbase]> select * from __all_virtual_trans_lock_stat where trans_id like '%4210669%'\G - *************************** 1. row *************************** - tenant_id: 1 - trans_id: {txid:4210669} - svr_ip: x.x.x.x - svr_port: 7801 - ls_id: 1 - table_id: 0 - tablet_id: 200002 - rowkey: rowkey_object=[{"INT":1}] - session_id: 3222110265 - proxy_session_id: NULL - ctx_create_time: 2023-05-22 16:40:44.737797 - expired_time: 2023-05-23 16:40:44.737797 - row_lock_addr: 0 - *************************** 2. row *************************** - tenant_id: 1 - trans_id: {txid:4210669} - svr_ip: x.x.x.x - svr_port: 7801 - ls_id: 1 - table_id: 0 - tablet_id: 200002 - rowkey: rowkey_object=[{"INT":3}] - session_id: 3222110265 - proxy_session_id: NULL - ctx_create_time: 2023-05-22 16:40:44.737797 - expired_time: 2023-05-23 16:40:44.737797 - row_lock_addr: 0 - *************************** 3. row *************************** - tenant_id: 1 - trans_id: {txid:4210669} - svr_ip: x.x.x.x - svr_port: 7801 - ls_id: 1 - table_id: 0 - tablet_id: 200002 - rowkey: rowkey_object=[{"INT":2}] - session_id: 3222110265 - proxy_session_id: NULL - ctx_create_time: 2023-05-22 16:40:44.737797 - expired_time: 2023-05-23 16:40:44.737797 - row_lock_addr: 0 - 3 rows in set (0.00 sec) - ``` - -3. 确认要停止的事务的 session_id 后,停止对应事务的会话。 - - ```bash - MySQL [oceanbase]> kill 3222110265; - Query OK, 0 rows affected (0.00 sec) - ``` - -### **场景二** - -业务反馈某张表上执行的事务总超时,已知该表的表名以及库名,本场景以库名为 test,表名为 t0522 为例。 - -1. 根据库表信息找到对应的持锁事务,如果数据比较大,可以拆分 SQL。 - - ```sql - MySQL [oceanbase]> select - avtls.trans_id, - avtls.session_id, - avtls.proxy_session_id - from - __all_tablet_to_ls attl, - __all_table at, - __all_database ad, - __all_virtual_trans_lock_stat avtls - where - ad.database_name='test' - and ad.database_id = at.database_id - and at.table_name='t0522' - and at.table_id=attl.table_id - and attl.tablet_id=avtls.tablet_id - and attl.ls_id=avtls.ls_id - order by - avtls.ctx_create_time desc - limit 5; - *************************** 1. row *************************** - trans_id: {txid:3966830} - session_id: 3221618369 - proxy_session_id: NULL - 1 row in set (0.01 sec) - ``` - -2. 发现持有行锁的事务的 session_id 为 3221618369,可以根据 trans_id 查看事务详情 - - ```sql - MySQL [oceanbase]> select * from __all_virtual_trans_stat where trans_id='3966830'\G - *************************** 1. row *************************** - tenant_id: 1 - svr_ip: x.x.x.x - svr_port: 7801 - trans_type: 0 - trans_id: 3966830 - session_id: 3221618369 - scheduler_addr: "x.x.x.x:7801" - is_decided: 0 - ls_id: 1 - participants: NULL - ctx_create_time: 2023-05-22 16:12:01.161296 - expired_time: 2023-05-23 16:11:55.393787 - ref_cnt: 2 - last_op_sn: 3 - pending_write: 0 - state: 10 - part_trans_action: 2 - trans_ctx_addr: 0x2b8c278137d0 - mem_ctx_id: -1 - pending_log_size: 43 - flushed_log_size: 0 - role: 0 - is_exiting: 0 - coordinator: -1 - last_request_time: 2023-05-22 16:12:01.161296 - gtrid: NULL - bqual: NULL - format_id: -1 - 1 row in set (0.01 sec) - ``` - -3. kill 连接 - - ```sql - MySQL [oceanbase]> kill 3221618369; - Query OK, 0 rows affected (0.00 sec) - ``` diff --git a/docs/user_manual/user_best_practices/tutorial/_index.md b/docs/user_manual/user_best_practices/tutorial/_index.md deleted file mode 100644 index 82f9550b7..000000000 --- a/docs/user_manual/user_best_practices/tutorial/_index.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: Quick Start -bookCollapseSection: false -weight: 1 ---- - -# Overview - -You can deploy OceanBase Community Edition in the following ways: - -* Quick deployment: If you want to quickly deploy a standalone edition to experience its features, deploy OceanBase Community Edition by referring to the quick deployment solution. -* Standard deployment: If you want to perform standard deployment in a production environment, deploy OceanBase Community Edition by referring to the standard deployment solution. - - -## Quick deployment - -For non-natively supported operating systems such as Mac OS and Windows, we recommend that you deploy the database by using the Docker image. For more information, see [Quick experience](./quickstart). -For natively supported operating systems such as Linux, we recommend that you deploy the database by using OceanBase Deployer (OBD) in a one-click manner. For more information, see [Quick experience](./deploy_in_production). - -## Standard deployment - -In an offline environment, we recommend that you use OBD for standard deployment. For more information, see [Deploy OceanBase Database offline](200.local-deployment/500.deploy-oceanbase-database-in-the-production-environment.md). diff --git a/docs/user_manual/user_best_practices/tutorial/deploy_in_production.md b/docs/user_manual/user_best_practices/tutorial/deploy_in_production.md deleted file mode 100644 index 70315bfe1..000000000 --- a/docs/user_manual/user_best_practices/tutorial/deploy_in_production.md +++ /dev/null @@ -1,3 +0,0 @@ - - -To Be done \ No newline at end of file diff --git a/docs/user_manual/user_best_practices/tutorial/quickstart.md b/docs/user_manual/user_best_practices/tutorial/quickstart.md deleted file mode 100644 index c3d2e2b0f..000000000 --- a/docs/user_manual/user_best_practices/tutorial/quickstart.md +++ /dev/null @@ -1,162 +0,0 @@ -# Quick experience - -This topic describes how to deploy OceanBase Database for quick experience. - -## Prerequisites - -Your server is connected to the Internet, and your hardware and software meet the following requirements. - -| Item | Description | -| --- | --- | -|OS|
  • Anolis OS 8.X (Linux kernel V3.10.0 or later)
  • Red Hat Enterprise Linux Server 7.X (Linux kernel V3.10.0 or later)
  • CentOS Linux 7.X (Linux kernel V3.10.0 or later)
| -|CPU|At least two cores or preferably four cores or more| -|Memory|At least 10 GB or preferably 16 GB or more| -|Disk type|Preferably SSD| -|Disk space|Four times the memory size or more| -|File system|EXT4 or XFS. Choose XFS when the data volume exceeds 16 TB.| - -> **Note** -> -> At least 10 GB of memory is required if you use Docker to deploy OceanBase Database. - -### Use OBD to deploy OceanBase Database - -> **Note** -> -> The following describes the deployment of OceanBase Database on an x86-based CentOS Linux 7.9 system. The procedure may be different on other OSs. - -#### Step 1: Download and install OBD - -OceanBase Deployer (OBD) is the most efficient deployment tool to make OceanBase Database ready to work. We recommend that you use OBD when you deploy OceanBase Database for experience. Download and install OBD by performing the following steps. -If the server has access to the Internet and allows you to add a third-party YUM repository as the software source, you can run the following command to install OBD from the official software source of OceanBase: - -```test -sudo yum install -y yum-utils -sudo yum-config-manager --add-repo https://mirrors.aliyun.com/oceanbase/OceanBase.repo -sudo yum install -y ob-deploy -``` - -#### Step 2: Use the obd demo command for quick deployment - -With the `obd demo` command, you can directly deploy and start a specified component on your local computer without loading the configuration file. The fixed name of the cluster deployed is `demo`. After the deployment, you can run the `obd cluster list` command to view the cluster in the cluster list. You can also run other cluster commands to manage the cluster, such as `obd cluster display demo`. - -```bash -obd demo [-c/--components] -``` - -The following table describes the parameters. - -| Parameter | Required | Data type | Default value | Description | -|------------------|---------|------------|----------|--------------------------------------------------------------------| -| -c or --components | No | String | oceanbase-ce,obagent,prometheus,grafana | The list of components that are separated with commas (`,`). You can use this parameter to specify the components to be deployed. | - -By default, this command deploys the minimum specifications in the home directory of the current user, and the latest versions are deployed by default. You can use this command to deploy OceanBase Community Edition, OBProxy Community Edition, OBAgent, Grafana, and Prometheus. - -You can select the version and specify the configurations of a component to be deployed. - -```bash -# Deploy components of the specified version. -obd demo -c oceanbase-ce,obproxy-ce --oceanbase-ce.version=4.2.1 -# Specify the components to be deployed and the package hash of OceanBase Community Edition. -obd demo -c oceanbase-ce,obproxy-ce --oceanbase-ce.package_hash=f38723204d49057d3e062ffad778edc1552a7c114622bf2a86fea769fbd202ea -# Specify the installation path for all components to be deployed. -## Deploy OceanBase Community Edition and OBProxy Community Edition in the /data/demo directory and create corresponding working directories for them. -obd demo -c oceanbase-ce,obproxy-ce --home_path=/data/demo -# Specify the installation path for all components to be deployed. -obd demo --home_path=/path -# Specify the installation path for a specific component to be deployed. -## Deploy OceanBase Community Edition in the home directory and create a working directory for it, and deploy OBProxy in the /data/playground/obproxy-ce directory. -obd demo -c oceanbase-ce,obproxy-ce --obproxy-ce.home_path=/data/demo/ -# Specify the configurations of a component to be deployed. -## Specify the mysql_port parameter of OceanBase Community Edition. -obd demo --oceanbase-ce.mysql_port=3881 -``` - -> **Notice** -> -> This command supports only level-1 configurations under global that are specified by using options. - -#### Step 3: Connect to OceanBase Database - -Perform the following steps to connect to OceanBase Database: - -1. Install the OceanBase Database client (OBClient): - - If you have added the official YUM repository of OceanBase as the software source, run the following command to install the OBClient: - - ```test - sudo yum install -y obclient - ``` - -2. Run the following command to connect to OceanBase Database by using the OBClient: - - ```test - obclient -h127.0.0.1 -P2881 -uroot@sys - ``` - -### Use Docker to deploy OceanBase Database - -> **Notice** -> -> Before you deploy the [oceanbase-ce](https://hub.docker.com/r/oceanbase/oceanbase-ce) image, make sure that the following prerequisites are met: -> -> * The resources on your server are sufficient to support a Docker container with at least 2 CPU cores and 8 GB of memory. -> -> * You have installed the latest version of Docker on your server. For more information, see the [Docker Documentation](https://docs.docker.com/get-docker/). -> * You have started the Docker service on your server. - -#### Step 1: Start an OceanBase Database instance - -Run the following command to start an OceanBase Database instance: - -```test -# Deploy an instance with the maximum specifications supported by the container. -docker run -p 2881:2881 --name obstandalone -d oceanbase/oceanbase-ce -## Deploy a mini standalone instance. -docker run -p 2881:2881 --name obstandalone -e MINI_MODE=1 -d oceanbase/oceanbase-ce -``` - -> **Note** -> -> By default, the preceding command pulls the latest version of Docker image. You can select a desired docker image from [Docker images](https://hub.docker.com/r/oceanbase/oceanbase-ce/tags) as needed. - -The process is expected to take two to five minutes. Run the following command. If "boot success!" is returned, the instance is started. - -```test -$ docker logs obstandalone | tail -1 -boot success! -``` - -#### Step 2: Connect to the OceanBase Database instance - -The oceanbase-ce image is integrated with the OBClient and the default connection script `ob-mysql`. - -```test -docker exec -it obstandalone ob-mysql sys # Connect to the root user of the sys tenant. -docker exec -it obstandalone ob-mysql root # Connect to the root user of the test tenant. -docker exec -it obstandalone ob-mysql test # Connect to the test user of the test tenant. -``` - -You can also run the following command to connect to the instance by using the OBClient or MySQL client on your local server. - -```test -mysql -uroot -h127.1 -P2881 -``` - -After the connection is established, the following information is displayed: - -```test -$ docker exec -it obstandalone ob-mysql sys - -login as root@sys -Command is: obclient -h127.1 -uroot@sys -A -Doceanbase -P2881 -Welcome to the OceanBase. Commands end with ; or \g. -Your OceanBase connection id is 3221487638 -Server version: 5.7.25 OceanBase 4.0.0 (r10100032022041510-a09d3134c10665f03fd56d7f8bdd413b2b771977) (Built Oct 15 2022 02:16:22) - -Copyright (c) 2000, 2022, OceanBase and/or its affiliates. All rights reserved. - -Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. - -MySQL [oceanbase]> -``` \ No newline at end of file diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 0eb3fcaf2..1c8f0f7cd 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -31,11 +31,7 @@ const user_manual = [ { label: "Operation And Maintenance Manual (in Chinese)", to: '/docs/user_manual/operation_and_maintenance/about_this_manual/overview' - }, - { - label: "User Best Practices (in Chinese)", - to: '/docs/user_manual/user_best_practices/about_oceanbase/overview' - }, + } ] const dev_manual = [ @@ -57,7 +53,7 @@ const docs = [ }, { label: 'User Manual', - to: '/docs/user_manual/user_best_practices/about_oceanbase/overview', + href: "https://oceanbase.github.io", dropdownItems: user_manual, }, { diff --git a/sidebars.ts b/sidebars.ts index b60c29d97..25eb5b177 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -193,98 +193,7 @@ const sidebars: SidebarsConfig = { }] }, ], - tutorialSidebar: [{ type: 'autogenerated', dirName: 'user_manual/user_best_practices/tutorial' }], blogsSidebar: [{ type: 'autogenerated', dirName: 'blogs' }], - legacySidebar: [ - { - type: 'category', - label: 'About OceanBase', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/about_oceanbase' - }], - }, - { - type: 'category', - label: 'Deploy Oceanbase', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/deploy_oceanbase', - }] - }, - { - type: 'category', - label: 'Deploy Tools', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/deploy_tools', - }] - }, - { - type: 'category', - label: 'Data Migration', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/data_migration', - }] - }, - { - type: 'category', - label: 'Operation Maintenance', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/operation_maintenance', - }] - }, - { - type: 'category', - label: 'Performance Tuning', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/performance_tuning', - }] - }, - { - type: 'category', - label: 'Trouble Shooting', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/trouble_shooting', - }] - }, - { - type: 'category', - label: 'Development Practice', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/development_practice', - }] - }, - { - type: 'category', - label: 'Solutions', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/solutions', - }] - }, - { - type: 'category', - label: 'FAQ', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/FAQ', - }] - }, - { - type: 'category', - label: 'Appendix', - items: [{ - type: 'autogenerated', - dirName: 'user_manual/user_best_practices/appendix', - }] - }, - ] } export default sidebars diff --git a/src/pages/index.tsx b/src/pages/index.tsx index c638b5af2..481f7c59b 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -26,7 +26,7 @@ function HomepageHeader() {
Get Started