OceanBase 4.X-2F1A 仲裁高可用方案初探

作者:郑增权,爱可生 DBA 团队成员,OceanBase 和 MySQL 数据库技术爱好者。

爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

本文约 1500 字,预计阅读需要 5 分钟。

背景

对于分布式数据库来说,当多个数据副本间发生半数异常时(一半副本故障或与另一半网络隔离),可以通过集群之外的仲裁服务来参与变更决策(选主/成员组变更),进而恢复服务。OceanBase 数据库 V4.1.0 版本开始支持 仲裁服务(Arbitratrion Service)。

某客户基于节约成本的想法,欲使用 OceanBase 仲裁服务功能,架构副本类型如下:

  • 集群架构:1-1-1
  • 副本类型为:2F1A(2 个全能型副本 + 1 个仲裁服务节点)

存在的疑虑:

  • 1 个全能型副本(Leader)发生故障后租户能否正常读写?
  • 2 个全能型副本发生可恢复的故障,均触发永久下线,后续启动 OBServer 后集群能否恢复正常?

本文基于如上 2 个问题展开实验,记录相关过程和结果,为类似需求评估提供参考。

相关术语

OceanBase 数据库仲裁服务

OceanBase 数据库仲裁服务(Arbitratrion Service)是一种基于 Paxos 多副本容灾方案提出的新型高可用方案

仲裁服务独立于 OceanBase 集群部署,是一个以特殊模式启动的轻量级 observer 进程,它承载着日志流级别的仲裁成员。

仲裁成员具备如下特征:

  • 仅参与选举、成员变更投票,不参与日志同步投票。
  • 不存储日志,无 MemTable 和 SStable,资源开销极小。
  • 不能当选为主提供服务。

当半数全功能副本故障导致日志同步失败并且达到日志流降级控制时间时,仲裁服务会自动执行日志流降级流程,将故障副本从成员列表中剔除,从而恢复服务,且能做到 RPO = 0。待故障的全功能副本恢复时,仲裁服务又会执行日志流升级流程,将被降级的副本重新加入成员列表,提供更高的可用性保证。

日志流

OceanBase 数据库参考传统数据库分区表的概念,把一张表格的数据划分成不同的分区(Partition)。在 V4.0.0 版本,分区是用户创建的逻辑对象,是划分和管理表数据的一种机制。

每个分区都有其对应的数据存储对象,称之为分片(Tablet),它具备存储数据的能力,支持在机器之间迁移(transfer),是数据均衡的最小单位。

日志流是由 OceanBase 数据库自动创建和管理的实体,它代表了一批数据的集合,包括若干 Tablet 和有序的 Redo 日志流。它通过 Paxos 协议实现了多副本日志同步,保证副本间数据的一致性,实现了数据的高可用。

日志流降级

日志流 Leader 上运行的仲裁服务发现一些副本在 arbitration_timeout(默认值为 5s)内没有向 Leader 回复日志确认消息,仲裁服务将执行进一步检查,准备对日志不同步的副本进行日志流降级操作。

仲裁服务通过周期性探测,检查日志不同步的副本是否存在降级策略中所列的异常,如果存在异常,则执行日志流降级操作。

注意: 仅当故障副本(包括已降级的副本)总数等于全功能副本总数的一半时,仲裁服务才会执行日志流降级操作。例如,4F1A 部署场景下:

  • 如果只有 1 个 F 副本出现异常,仲裁服务不会执行日志流降级,因为此时 4F 的多数派 3F 依然存活,可以正常同步日志。
  • 如果有 2 个 F 副本出现异常(例如:2 个 F 副本网络中断,或者一个 F 副本 Rebuild,一个 F 副本所在 Server 宕机),仲裁服务会执行日志流降级。
  • 如果有 3 个及以上 F 副本出现异常,此时仅存活一个 F 副本和仲裁服务,不满足多数派,日志流将会无主,无法执行日志流降级。

环境信息

  • CentOS Linux release 7.5.1804 (Core)
  • OCP 云平台:4.2.0
  • OceanBase:4.2.1.4
  • 测试租户:mysql_ob
  • 租户模式:MySQL
  • 租户规格:1.5C6G

实验过程

故障前

  1. 查看 mysql_ob 租户日志流,可以看到有 3 个日志流。
select tenant_id,ls_id from oceanbase.CDB_OB_TABLET_TO_LS where tenant_id = 1002 group by LS_ID;

  1. 查看集群普通副本节点状态。

    SELECT * FROM oceanbase.DBA_OB_SERVERS;

  2. 查看业务租户 mysql_obleader/follower 角色信息。

  • 161 节点为 leader
  • 163 节点为 follower
    select b.tenant_name,a.tenant_id,a.ls_id,a.zone,a.svr_ip,a.role from cdb_ob_table_locations  a join __all_tenant b on a.tenant_id = b.tenant_id where a.tenant_id = 1002 group by role;

  1. 查看仲裁服务节点
  • 164 节点为仲裁服务节点
    select * from DBA_OB_ARBITRATION_SERVICE;
  1. 租户 mysql_ob 为 2F1A 架构(2F:10.186.64.161/163 , 1A:10.186.64.164)。
  1. 查看仲裁节点与其他节点联通性。
  • ACTIVE: 表示仲裁服务与该节点通信正常。所有节点均通信正常才可以为租户开启仲裁服务。
  • INACTIVE: 表示仲裁服务与该节点无法通信。无法为租户开启仲裁服务,需要排查节点与仲裁服务之间的网络通信情况。
SELECT * FROM oceanbase.GV$OB_ARBITRATION_SERVICE_STATUS;

  1. 查看租户的仲裁服务状态
  • ENABLED: 表示租户已开启仲裁服务。
  • DISABLED: 表示租户已关闭仲裁服务。
  • ENABLING: 表示租户正在开启仲裁服务。
  • DISABLING: 表示租户正在关闭仲裁服务。
select TENANT_ID,TENANT_NAME,PRIMARY_ZONE,STATUS,TENANT_ROLE,SWITCHOVER_STATUS,ARBITRATION_SERVICE_STATUS from DBA_OB_TENANTS;

  1. 查看永久下线时间参数。

    show parameters like 'server_permanent_offline_time';

  2. 永久下线时间调整为 60s。

    ALTER SYSTEM SET server_permanent_offline_time='60s';
    show parameters like 'server_permanent_offline_time';

  1. 启动脚本,持续往 evan.time_table 表写入数据。

  1. 启动另一个脚本,持续 select evan.time_table 表的最新数据。

4.2 施加故障

模拟故障:kill 掉第 1 个全能型副本

业务租户 leader 节点执行。

ps -ef | grep observer | grep -v "grep"
date && kill -9 $(ps aux | grep "observer" | grep -v "grep" | awk '{print $2}') && ps -ef | grep observer | grep -v grep && date

4.3 故障后观察

  1. 查看脚本写入状态是否正常。

  1. 查看脚本 select 是否正常。

  1. 查看业务租户 leader/follower 角色信息。

  1. 确认触发日志流降级。

当故障副本(包括已降级的副本)总数等于全功能副本总数的一半时,仲裁服务才会执行日志流降级操作。

SELECT * FROM oceanbase.DBA_OB_SERVER_EVENT_HISTORY WHERE EVENT LIKE "%DEGRADE%" AND VALUE1 = 1002 AND TIMESTAMP >= '2024-05-06 15:53%' ORDER BY 1 ;

  1. 确认节点状态变成 INACTIVE

    SELECT * FROM oceanbase.DBA_OB_SERVERS;

  2. 查看仲裁节点与其他节点联通性。

无法与 161 节点联通。

SELECT * FROM oceanbase.GV$OB_ARBITRATION_SERVICE_STATUS;

  1. 确认旧 leader 161 节点触发永久下线(预期 60s 后永久下线)。
select * from __all_rootservice_event_history where event='permanent_offline' and gmt_create like '2024-05-06%' \G

  1. 查询 kill 掉第 1 个 observer 时间点前后,业务表 time_table 实际是否有数据插入失败。
select * from evan.time_table where time >= '2024-05-06 15:53:37' order by time limit 20;

施加故障

故障模拟:kill 掉第 2 个全能型副本。

  1. 业务租户 follower 节点执行。
ps -ef | grep observer | grep -v "grep"
date && kill -9 $(ps aux | grep "observer" | grep -v "grep" | awk '{print $2}') && ps -ef | grep observer | grep -v grep && date

故障后观察

  1. 查看脚本 insert 数据是否正常。

处于异常状态。

  1. 查看脚本 select 数据是否正常。

处于异常状态。

  1. 查看业务租户 leader/follower 角色信息。

无主状态,执行 SQL 超时。

select b.tenant_name,a.tenant_id,a.ls_id,a.zone,a.svr_ip,a.role from cdb_ob_table_locations  a join __all_tenant b on a.tenant_id = b.tenant_id where a.tenant_id = 1002 group by role;

  1. 确认日志流降级。
    SELECT * FROM oceanbase.DBA_OB_SERVER_EVENT_HISTORY WHERE EVENT LIKE "%DEGRADE%" AND VALUE1 = 1002 AND TIMESTAMP >= '2024-05-06 16%' ORDER BY 1 ;

  1. 确认节点状态变成 INACTIVE
    SELECT * FROM oceanbase.DBA_OB_SERVERS;

  1. 查看仲裁节点与其他节点联通性。

161 和 163 节点均无法联通。

  1. 确认节点触发永久下线(预期 60s 后永久下线)。

161 和 163 节点均被标记永久下线。

select * from __all_rootservice_event_history where event='permanent_offline' and gmt_create like '2024-05-06%' \G

  1. 查询 kill 掉第 2 个 observer 时间点前后,业务表 time_table 实际是否有数据插入失败。

租户故障期间无法查询,恢复正常后补图。

select * from evan.time_table where time >= '2024-05-06 16:04:25' order by time limit 20;

  1. 确认租户处于无主状态。
  • 查看最近 30 分钟是否有原主租约过期的情况。

161 和 163 节点均存在租约过期记录。

SELECT * FROM __all_rootservice_event_history WHERE module = 'server' AND event = 'lease_expire' AND gmt_create > usec_to_time(time_to_usec(now())-3600*1000000) ORDER BY gmt_create;

  • 在 observer 全能型副本的主机上确认日志打印信息存在 4038 错误码。

cd /home/admin/oceanbase/log && grep "ret=-4038" observer.log

修复故障

  1. 辨别启动 OBServer 节点的先后顺序。

由于此时租户已经不满足多数派,处于故障状态,我们应该先启动故障时间较晚的 OBServer 节点,再启动故障时间较早的 OBServer 节点。(通过 observer.log 最后写入的时间点做判断)

tail -1 /home/admin/oceanbase/log/observer.log

  1. 启动 163 节点。

根据上一步骤判断 163 节点 observer.log 较新。

su - admin
cd /home/admin/oceanbase
date && ./bin/observer
ps -ef | grep observer | grep -v "grep"

  1. 确认 insert 脚本状态。

16:24:56 写入数据成功。

  1. 确认 select 脚本状态。

16:24:55 读取数据成功(脚本打印时间每隔1秒打印一次,存在细微误差属正常情况)。

  1. 确认 163 节点状态变成 ACTIVE
SELECT * FROM oceanbase.DBA_OB_SERVERS;

  1. 启动 161 节点。

根据之前的步骤判断 161 节点 observer.log 较旧,后启动。

su - admin
cd /home/admin/oceanbase
date && ./bin/observer
ps -ef | grep observer | grep -v "grep"

确认 161 节点 OBServer 状态变成 ACTIVE。

SELECT * FROM oceanbase.DBA_OB_SERVERS;

  1. 确认 insert 脚本状态。

正常写入。

  1. 确认 select 脚本状态。

正常读取。

  1. 通过 oceanbase.DBA_OB_UNIT_JOBS 视图查看数据补全进度。

如果查询结果为空,则表示 Unit 迁移完成,数据补全成功。

SELECT * FROM oceanbase.DBA_OB_UNIT_JOBS WHERE JOB_TYPE = 'MIGRATE_UNIT';

  1. 查看测试表 time_tabletenant_idls_id(日志流 ID)。

    SELECT TENANT_ID, LS_ID FROM oceanbase.CDB_OB_TABLET_TO_LS WHERE TABLET_ID = (SELECT DATA_OBJECT_ID FROM oceanbase.CDB_OBJECTS WHERE OBJECT_NAME ='time_table' AND OBJECT_TYPE='TABLE');

  2. 查看测试表 time_table 对应日志流的选举记录。

lease_time:当 Root Service 累计超过 lease_time 时间没有收到过某节点的任意心跳数据包时,Root Service 认为该 observer 进程短暂断线,Root Service 会标记该节点的心跳状为 lease_expired。

SELECT * FROM DBA_OB_SERVER_EVENT_HISTORY WHERE MODULE LIKE '%ELECTION%' and TIMESTAMP >= '2024-05-06 15:53:37' AND NAME1 = 'TENANT_ID' AND VALUE1 = '1002' AND NAME2 = 'LS_ID' AND VALUE2 =1001 ORDER BY TIMESTAMP;

  1. 永久下线时间改回原值并确认生效。
ALTER SYSTEM SET server_permanent_offline_time='3600s';
show parameters like 'server_permanent_offline_time';

时间线梳理

时间线

  1. 将永久下线时间参数 server_permanent_offline_time 调成 60s
  2. 2024-05-06 15:53:39 kill 掉 161 节点全能型副本节点(leader)
  3. 2024-05-06 15:53:39 ~ 15:53:43 约持续 5s,insert 脚本未写入数据,select 脚本未读到数据
  4. leader 节点由 161 切至 163
  5. 2024-05-06 15:53:43 161节点触发日志流降级,原因:CRASHED_OR_BROKEN_NETWORK
  6. 2024-05-06 15:54:39 161 节点被标记永久下线
  7. 2024-05-06 16:04:28 kill 掉 163 节点全能型副本节点(新 leader)
  8. 2024-05-06 16:04:28 租户开始进入读写异常状态
  9. 确认两个全能型副本状态都为:INACTIVE
  10. 仲裁服务节点与 2 个异常节点联通异常
  11. 2024-05-06 16:05:35 163 节点被标记永久下线
  12. 租户处于无主状态
  13. 2024-05-06 16:22:53 启动 163 节点全能型副本节点(后被 kill,先启动)
  14. 2024-05-06 16:24:55 租户恢复正常读写
  15. 2024-05-06 16:36:29 启动 161 节点全能型副本节点
  16. 将永久下线时间参数 server_permanent_offline_time 调回3600s

小结

  1. kill 掉 1 个全能型副本节点后(leader),租户存在约5s读写异常,后续恢复正常。
  2. kill 掉 2 个全能型副本节点后,租户进入无主状态,读写状态异常。
  3. 启动 163 节点全能型副本节点,可正常加回集群,约 117s 后租户读写恢复正常。
  4. 启动 161 节点全能型副本节点后,可正常加回集群。

结论

  1. 1 个全能型副本(leader)发生故障后租户能否正常读写?

结论:租户切主期间短暂读写异常,后续恢复正常读写。

  1. 2 个全能型副本发生可恢复的故障,均触发永久下线,后续启动OBServer后集群能否恢复正常?

结论:可恢复正常。

  1. 需注意的点:极端情况下如果第二个全能型副本也发生故障且无法恢复,之后即使第一个 全能型副本恢复了,也会有数据丢失,原因是仲裁成员不存储 Redo 日志。

  2. 综上,成本敏感或预算有限且能承受可能丢失数据的情况可以选择 OceanBase 2F1A 仲裁高可用方案,若期望数据不丢失建议选择全功能型副本高可用方案。

后记

仲裁服务两个典型的应用场景在于 “自动选主提升同城自动容灾能力” 和 “降低跨城带宽提升两地三中心稳定性”,感兴趣的读者可查看《OceanBase 助力企业应对数据库转型深水区挑战》 中关于仲裁服务章节的内容。

更多技术文章,请访问:https://opensource.actionsky.com/

关于 SQLE

SQLE 是一款全方位的 SQL 质量管理平台,覆盖开发至生产环境的 SQL 审核和管理。支持主流的开源、商业、国产数据库,为开发和运维提供流程自动化能力,提升上线效率,提高数据质量。

✨ Github:https://github.com/actiontech/sqle

📚 文档:https://actiontech.github.io/sqle-docs/

💻 官网:https://opensource.actionsky.com/sqle/

👥 微信群:请添加小助手加入 ActionOpenSource

🔗 商业支持:https://www.actionsky.com/sqle

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CS与MSF的权限互相传递/mimikatz抓取windows 2012明文密码

目录 CS和MSF的简单介绍 Metasploit Cobalt Strike 1、CS权限传递到MSF 2、MSF权限传递到CS 3、使用mimikatz抓取明文密码 通过修改注册表用户重新登录后抓取明文密码 今天的任务是两个 一个是CS与MSF的权限互相传递一个是抓取windows2012的明文密码 那就分别来完成 …

printf趣味代码,打印图案

文章目录 1.打印佛祖2.打印猫猫 (闪烁效果) 1.打印佛祖 #include <stdio.h>void budda_bless(){printf("///\n\ // _ooOoo_ //\n\ // o8888888o //\n\ // …

SpringBoot全局配置文件里的配置项

SpringBoot核心配置文件里可以配置的配置项&#xff0c;非常非常多&#xff01;Spring Boot Reference Guide靠后的附件里看到所有配置项和说明. 有关配置项&#xff0c;我们除了知道在哪里查询具体配置项以外&#xff0c;我们还应该知道一些细节知识点: ① SpringBoot给我们…

2024广东省职业技能大赛云计算赛项实战——集群部署GitLab

集群部署GitLab 前言 题目是这样的&#xff1a; 在Kubernetes集群中新建命名空间gitlab-ci&#xff0c;将GitLab部署到该命名空间下&#xff0c;Deployment和Service名称均为gitlab&#xff0c;以NodePort方式将80端口对外暴露为30880&#xff0c;设置GitLab服务root用户的密…

Python 高级编程:文件操作与错误处理

在前几篇文章中&#xff0c;我们介绍了Python的基本语法、函数和模块以及面向对象编程。这些知识对于大部分日常编程问题已经足够&#xff0c;但对于需要分析大数据的人来说&#xff0c;这些还不够。本章将介绍Python的文件操作以及错误处理与调试。 目录 文件操作读文件写文…

制造业采购堡垒机的四大必要性看这里!

制造业包括的行业广泛&#xff0c;与大家的生活息息相关&#xff0c;例如食品制造业、汽车制造业、纺织业、服装制造业等等。但大家对于制造业不是很了解&#xff0c;不知道制造业也是需要采购堡垒机的&#xff0c;今天我们就来聊聊制造业采购堡垒机的必要性。 制造业采购堡垒机…

node mySql 实现数据的导入导出,以及导入批量插入的sql语句

node 实现导出, 在导出excel中包含图片&#xff08;附件&#xff09; node 实现导出, 在导出excel中包含图片&#xff08;附件&#xff09;-CSDN博客https://blog.csdn.net/snows_l/article/details/139999392?spm1001.2014.3001.5502 一、效果 如图&#xff1a; 二、导入 …

AI商品图生成企业定制服务,广州这家公司走在了行业前头

&#x1f680; 最强AI绘图设计企业定制服务出炉 —— 触站A&#xff0c;智能艺术的革新者 &#x1f31f; &#x1f3a8; 触站AI&#xff0c;绘制未来的智能艺术 &#x1f3a8;在AI技术的浪潮中&#xff0c;触站AI以其前沿技术&#xff0c;为艺术与设计领域注入了新的活力。 &a…

【SkiaSharp绘图11】SKCanvas属性详解

文章目录 SKCanvas构造SKCanvas构造光栅 Surface构造GPU Surface构造PDF文档构造XPS文档构造SVG文档SKNoDrawCanvas 变换剪裁和状态构造函数相关属性DeviceClipBounds获取裁切边界(设备坐标系)ClipRect修改裁切区域IsClipEmpty当前裁切区域是否为空IsClipRect裁切区域是否为矩形…

C3P0数据库连接池

目录 一&#xff1a;连接池介绍 1.1连接池解决的问题 2.常用的数据库连接池 二&#xff1a;c3p0介绍 2.1C3P0介绍&#xff1a; 2.2C3P0快速入门 1.常用参数说明 2.API介绍 3.使用步骤 1.导入jar包c3p0-0.9.1.2.jar 2.编写c3p0-config.xml配置文件&#xff0c;配置对…

深入探索:大型语言模型消除幻觉的解决之道

随着人工智能技术的飞速发展&#xff0c;大型语言模型&#xff08;LLMs&#xff09;已经成为自然语言处理领域的明星。它们以其庞大的知识库和生成连贯、上下文相关文本的能力&#xff0c;极大地推动了研究、工业和社会的进步。然而&#xff0c;这些模型在生成文本时可能会产生…

27. 高级特性(下)

目录 一、为了类型安全和抽象而使用 newtype 模式二、使用类型别名创建类型同义词2.1 使用type关键赋予现有类型一个别名2.2 减少重复2.3 与Result<T, E>结合使用2.4 从不返回的 never type 三、高级函数和闭包3.1 函数指针3.2 返回闭包 四、宏4.1 宏和函数的区别4.2 mac…

2024 最新推广服务 API 推荐,助力业务腾飞

在数字化营销的浪潮中&#xff0c;API 服务正以其强大的功能和高效的特性&#xff0c;成为企业和开发者们实现精准推广、优化营销效果的得力助手。2024 年的今天&#xff0c;各种创新的 API 服务层出不穷&#xff0c;为广告投放、数据洞察等领域带来了前所未有的机遇。在接下来…

echarts隔行背景色

看了下使用说明&#xff0c;试了半天终于搞对了 参考文档&#xff1a;Documentation - Apache ECharts option {xAxis: {type: category,data: [Mon, Tue, Wed, Thu, Fri, Sat, Sun]},yAxis: {type: value},series: [{data: [120, 200, 150, 80, 70, 110, 130],type: bar,mar…

视频共享融合赋能平台LntonCVS视频监控业务平台建设安全煤矿矿井应用方案

随着我国经济的飞速增长&#xff0c;煤炭作为主要的能源之一&#xff0c;在我国的能源结构中扮演着至关重要的角色。然而&#xff0c;煤矿事故的频繁发生&#xff0c;不仅造成了巨大的人员伤亡和财产损失&#xff0c;也对社会产生了深远的负面影响。因此&#xff0c;实现煤矿的…

多家国产大模型提供OpenAI API服务替代方案,谷歌将推出明星网红AI聊天机器人

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 1、OpenAI终止对中国提供服务 6月25日凌晨&#xff0c;多个用户收到OpenAI的推送邮件&#xff0c;信中称&#xff0c;自今年7月9日起&#xff0c;将开始阻止来自非支持国家和地区的API&#xff08;应…

华为od-C卷200分题目3 - 两个字符串间的最短路径问题

华为od-C卷200分题目3 - 两个字符串间的最短路径问题 题目描述 给定两个字符串&#xff0c;分别为字符串A与字符串B。 例如A字符串为ABCABBA&#xff0c;B字符串为CBABAC可以得到下图m*n的二维数组&#xff0c;定义原点为(0, 0)&#xff0c;终点为(m, n)&#xff0c;水平与垂…

python自动化系列:自动将工作簿下的所有工作表合并到新工作表

作品介绍 作品名称&#xff1a;自动将工作簿下的所有工作表合并到新工作表 开发环境&#xff1a;PyCharm 2023.3.4 python3.7 用到的库&#xff1a;os、xlwings 作品简介&#xff1a;该实例使用xlwings库来操作Excel文件&#xff0c;其主要功能是将一个工作簿中所有工作表…

玩机进阶教程----MTK芯片使用Maui META修复基带 改写参数详细教程步骤解析

目前mtk芯片与高通芯片在主流机型 上使用比较普遍。但有时候版本更新或者误檫除分区等等原因会导致手机基带和串码丢失的故障。mtk芯片区别与高通。在早期mtk芯片中可以使用工具SN_Writer_Tool读写参数。但一些新版本机型兼容性不太好。今天使用另外一款工具来演示mtk芯片改写参…

打破数据分析壁垒:SPSS复习必备(十)

Means过程 统计学上的定义和计算公式 定义&#xff1a;Means过程是SPSS计算各种基本描述统计量的过程&#xff0c;其实就是按照用户指定条件&#xff0c;对样本进行分组计算均数和标准差&#xff0c;如按性别计算各组的均数和标准差。 用户可以指定一个或多个变量作为分组变…