OceanBase数据库实践入门——性能测试建议

概述

本文主要分享针对想压测OceanBase时需要了解的一些技术原理。这些建议可以帮助用户对OceanBase做一些调优,再结合测试程序快速找到适合业务的最佳性能。由于OceanBase自身参数很多、部署形态也比较灵活,这里并没有给出具体步骤。

数据库读写特点

压测的本质就是对一个会话的逻辑设计很高的并发。首先需要了解单个会话在数据库内部的读写逻辑。比如说,业务会话1对数据库发起一个DML SQL,第一次修改某笔记录,数据库会怎么做呢?

为了便于理解OB的行为,我们先看看ORACLE是怎么做的。后面有对比才可以加深理解。

ORACLE 读写特点

ORACLE会话第一次修改一行记录,如果该记录所在块(8K大小)不在内存(Buffer Cache)里时会先从磁盘文件里读入到内存里。这个称为一次物理读,为了性能考虑,ORACLE一次会连续读取相邻的多个块。然后就直接在该块上修改,修改之前会先记录REDO和UNDO(包括UNDO的REDO)。然后这个数据块就是脏块(Dirty Block)。假设事务没有提交,其他会话又来读取这个记录,由于隔离级别是读已提交(READ COMMITTED),ORACLE会在内存里克隆当前数据块到新的位置,新块包含了最新的未提交数据。然后ORACLE在新块上逆向应用UNDO链表中的记录,将数据块回滚到读需要的那个版本(SCN),然后才能读。这个也称为一次一致性读(Consistency Read),这个新块也称为CR块。

即使是修改一条记录一个字段的几个字节,整个块(8K大小)都会是脏块。随着业务持续写入,大量脏块会消耗数据库内存。所以ORACLE会有多重机制刷脏块到磁盘数据文件上。在事务日志切换的时候也会触发刷脏块操作。如果业务压力测试ORACLE,大量的写导致事务日志切换很频繁,对应的刷脏操作可能相对慢了,就会阻塞日志切换,也就阻塞了业务写入。这就是ORACLE的特点。解决办法就是加大事务日志文件,增加事务日志成员或者用更快的磁盘存放事务日志和数据文件。

ORACLE里一个表就是一个Segment(如果有大对象列还会有独立的Segment,这个先忽略),Segment由多个不一定连续的extent组成,extent由连续的Block(每个大小默认8K)组成,extent缺点是可能会在后期由于频繁删除和插入产生空间碎片。

OceanBase 读写特点

OceanBase会话第一次修改一行记录,如果该记录所在块(64K大小)不在内存(Block Cache)里时也会先从磁盘文件里读入到内存里。这个称为一次物理读。然后要修改时跟ORACLE做法不同的是,OceanBase会新申请一小块内存用于存放修改的内容,并且链接到前面Block Cache里该行记录所在块的那笔记录下。如果修改多次,每次修改都跟前面修改以链表形式关联。同样在修改之前也要先在内存里记录REDO。每次修改都会记录一个内部版本号,记录的每个版本就是一个增量。其他会话读取的时候会先从Block Cache中该记录最早读入的那个版本(称为基线版本)开始读,然后叠加应用后面的增量版本直到合适的版本(类似ORACLE中SCN概念)。(随着版本演进,这里细节逻辑可能会有变化。)

OB的这个读方式简单说就是从最早的版本读起,逐步应用增量(类似REDO,但跟REDO日志无关)。而ORACLE一致性读是从最新的版本读起,逐步回滚(应用UNDO)。在OB里,没有UNDO。当版本链路很长时,OB的读性能会略下降,所以OB也有个checkpoint线程定时将记录的多个版本合并为少数几个版本。这个合并称为小合并(minor compaction)。此外,OB在内存里针对行记录还有缓存,

从上面过程还可以看出,每次修改几个字节,在内存里的变脏的块只有增量版本所在的块(默认写满才会重新申请内存),基线数据块是一直不变化。所以OB里脏块产生的速度非常小,脏块就可以在内存里保存更久的时间。实际上OB的设计就是脏块默认不刷盘。那如果机器挂了,会不会丢数据呢?

OB跟ORACLE一样,修改数据块之前会先记录REDO,在事务提交的时候,REDO要先写到磁盘上(REDO同时还会发送往其他两个副本节点,这个先忽略)。有REDO在,就不怕丢数据。此外,增量部分每天还是会落盘一次。在落盘之前,内存中的基线数据和相关的增量数据会在内存里进行一次合并(称Merge),最终以SSTable的格式写回到磁盘。如果说内存里块内部产生碎片,在合并的那一刻,这个碎片空间基本被消弭掉了。所以说OB的数据文件空间碎片很小,不需要做碎片整理。同时OB的这个设计也极大降低了LSM的写放大问题。

当业务压测写OB时,脏块的量也会增长,最终达到增量内存限制,这时候业务就无法写入,需要OB做合并释放内存。OB的合并比较耗IO、CPU(有参数可以控制合并力度),并且也不会等到内存用尽才合并,实际会设置一个阈值。同时为了规避合并,设计了一个转储机制。当增量内存使用率超过阈值后,就开启转储。转储就是直接把增量内存写到磁盘上(不合并)。转储对性能的影响很小,可以高峰期发生,并且可以转储多次(参数配置)。

OB增量内存就类似一个水池,业务写是进水管在放水, 转储和大合并是出水管。水位就是当前增量内存使用率。当进水的速度快于出水,池子可能就会满。这时候业务写入就会报内存不足的错误。
这就是OB读写的特点,解决方法就是加大OB内存、或者允许OB自动对业务写入速度限流。

OceanBase部署建议

OB 在commit的时候redo落盘会写磁盘。读数据的时候内存未命中的时候会有物理读,转储和大合并的时候落盘会有密集型写IO。这些都依赖磁盘读写性能。所以建议磁盘都是SSD盘,并且建议日志盘和数据盘使用独立的文件系统。如果是NVME接口的闪存卡或者大容量SSD盘,那日志盘和数据盘放在一起也可以。不要使用LVM对NVME接口的大容量SSD做划分,那样瓶颈可能会在LVM自身。

OB的增量通常都在内存里,内存不足的时候会有转储,可以转储多次。尽管如此,建议测试机器的内存不要太小,防止频繁的增量转储。通常建议192G内存以上。

OB集群的节点数至少要有三个。如果是功能了解,在单机上起3个OB进程模拟三节点是可以的,但是如果是性能测试,那建议还是使用三台同等规格的物理机比较合适。机器规格不一致时,最小能力的机器可能会制约整个集群的性能。 OceanBase集群的手动部署请参考《OceanBase数据库实践入门——手动搭建OceanBase集群》。在部署好OceanBase之后,建议先简单了解一下OceanBase的使用方法,详情请参考文章《OceanBase数据库实践入门——常用操作SQL》。

如果要验证OB的弹性缩容、水平扩展能力,建议至少要6节点(部署形态2-2-2)。并且测试租户(实例)的每个Zone里的资源单元数量至少也要为2个,才可以发挥多机能力。这是因为OB是多租户设计,对资源的管理比较类似云数据库思想,所以里面设计有点精妙,详情请参见《揭秘OceanBase的弹性伸缩和负载均衡原理》。

下面是一个租户的测试租户资源初始化建议

  • 登录sys租户
create resource unit S1, max_cpu=2, max_memory='10G', min_memory='10G', max_iops=10000, min_iops=1000, max_session_num=1000000, max_disk_size=536870912;
create resource unit S2, max_cpu=4, max_memory='20G', min_memory='20G', max_iops=20000, min_iops=5000, max_session_num=1000000, max_disk_size=1073741824;
create resource unit S3, max_cpu=8, max_memory='40G', min_memory='40G', max_iops=50000, min_iops=10000, max_session_num=1000000, max_disk_size=2147483648;
select * from __all_unit_config;create resource pool pool_demo unit = 'S2', unit_num = 2;
select * from __all_resource_pool order by resource_pool_id desc ;create tenant t_obdemo resource_pool_list=('pool_demo');
alter tenant t_obdemo set variables ob_tcp_invited_nodes='%';

请注意上面的unit_num=2这个很关键。如果unit_num=1,OB会认为这个租户是个小租户,后面负载均衡处理时会有个默认规则。

  • 登录业务租户
create database sbtest;
grant all privileges on sbtest.* to sbuser@'%' identified by 'sbtest';

sysbench压测建议

因为测试场景跟业务有关,这里就以常见的sysbench场景举例分析

sysbench工具可以建几个结构相同的表,然后执行纯读、纯写、读写混合。其中读又分根据主键或者二级索引查询,等值查询、IN查询或范围查询几种。详细的可以查看官方介绍。

如果用sysbench压测OB,创建很多表是一种方法。另外一种方法就是创建分区表。OceanBase是分布式数据库,数据迁移和高可用的最小粒度是分区,分区是数据表的子集。分区表有多个分区,非分区表只有一个分区。分区表的拆分细节是业务可以定义的。OceanBase可以将多个分区分布到不同节点,也可能不会分布到多个节点。这个取决于OB集群和租户的规划设计。所以对sysbench创建的表(分区),在性能分析时要分析分区具体的位置。

建表准备

下面是sysbench里分区表示例,修改 oltp_common.lua:

   query = string.format([[
CREATE TABLE sbtest%d(id %s,k INTEGER DEFAULT '0' NOT NULL,c CHAR(120) DEFAULT '' NOT NULL,pad CHAR(60) DEFAULT '' NOT NULL,%s (id,k)
) partition by hash(k) partitions %s %s %s]],table_num, id_def, id_index_def, part_num, engine_def, extra_table_options)

需要注意用分区表后,主键列和唯一索引列需要包含分区键。分区表的索引有本地(LOCAL)索引和全局索引两种。本地索引的存储跟数据是在一起的,全局索引的存储是独立的。全局索引是为了应对查询条件不是分区键的场景,没有全局索引时,会扫描所有分区的本地索引。这两种索引的性能优劣没有定论,以实际业务场景测试为准。此外,传统数据库索引对修改操作会有负面影响,分布式数据库的全局索引对修改操作的影响可能更大,因为一个简单的DML语句都会因为要同步修改全局索引而产生分布式事务。而本地索引就没有分布式事务问题。所以对全局索引的使用场景要谨慎评估。这个特性不是OB特有,只要是分布数据库都会面临这个问题。

数据分布均衡

本节是说明OB数据分布背后的原理和方法。

OceanBase是分布式数据库,它通过每个机器上的observer进程将多个机器的资源能力聚合成一个大的资源池,然后再为每个业务分配不同资源规格的租户(实例)。所以每个业务租户(实例)的资源能力都只是整个集群能力的子集。业务并不一定能使用到全部机器资源。

OceanBase里的数据都有三份,细到每个分区有三个副本,角色上有1个leader副本2个follower副本。默认只有leader副本提供读写服务,leader副本所在的节点才有可能提供服务,有负载。OceanBase调整各个节点负载的方法是通过调整内部leader副本的位置实现的。这个调整可以让OceanBase自动做,也可以手动控制。

OceanBase自动负载均衡是参数enable_rebalance控制,默认值为True。可以查看确认。修改用alter system语句。

alter system set enable_rebalance=True;
show parameters like 'enable_rebalance';

查看实际表的分区leader副本位置

##查看分区分布
SELECT t5.tenant_id,t5.tenant_name,t3.database_name, t4.tablegroup_name, t2.table_name, t1.partition_id, concat(t1.svr_ip,':',t1.svr_port) observer, t1.role, t1.data_size,t1.row_count
from `gv$partition` t1 join `__all_table` t2 on (t1.tenant_id=t2.tenant_id and t1.table_id=t2.table_id)join `__all_database` t3 on (t2.tenant_id=t3.tenant_id and t2.database_id=t3.database_id)left join `__all_tablegroup` t4 on (t1.tenant_id=t4.tenant_id and t1.tablegroup_id=t4.tablegroup_id)join `__all_tenant` t5 on (t1.tenant_id=t5.tenant_id)
where t5.tenant_id = 1020 and role=1 and database_name in ('sysbenchtest')
order by t5.tenant_id,t3.database_id,t1.tablegroup_id,t1.partition_id, t1.role;

sysbench命令参数

sysbench的机制是压测过程中如果有错误就会报错退出,所以需要针对一些常见的错误进行忽略处理,这样sysbench会话可以重试继续运行。比如说主键或者唯一键冲突、事务被杀等等。
下面的运行命令供参考

  • 初始化
./sysbench --test=./oltp_read_only.lua --mysql-host=***.***.82.173 --mysql-port=4001 --mysql-db=test --mysql-user="sbuser"  --mysql-password=sbtest --tables=16 --table_size=100000000 --threads=32 --time=300 --report-interval=5 --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016 prepare
  • 纯读
./sysbench --test=./oltp_read_only.lua --mysql-host=***.***.82.173 --mysql-port=4001 --mysql-db=test --mysql-user="sbuser"  --mysql-password=sbtest --tables=16 --table_size=100000000 --threads=96 --time=600  --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016 --secondary=on run
  • 纯写
./sysbench --test=./oltp_write_only.lua --mysql-host=***.***.82.173 --mysql-port=4001 --mysql-db=test --mysql-user="sbuser"  --mysql-password=sbtest --tables=16 --table_size=100000000 --threads=32 --time=600 --report-interval=5 --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016,1062 run
  • 读写混合
./sysbench --test=./oltp_read_write.lua --mysql-host=***.***.82.173 --mysql-port=4001 --mysql-db=test --mysql-user="sbuser"  --mysql-password=sbtest --tables=16 --table_size=100000000 --threads=32 --time=600 --report-interval=5 --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016,1062 run

测试观察

在纯写或者读写测试中,注意观察增量增量内存使用进度。如果写入速度比转储和合并速度还快,那会碰到内存不足写入失败错误。这就是OB租户资源相对不足了。观察这个内存使用进度可以通过 dooba脚本。dooba脚本默认在/home/admin/oceanbase/bin/目录下。
使用示例如下:

python dooba -h11..84.84 -uroot@sys#obdemo -P2883 -p**

OB有个内部视图gv$sql_audit可以查看执行过所有成功或失败的SQL,用来分析具体的SQL性能。用法详情参见官网(oceanbase.alipay.com) 或 文章《阿里数据库性能诊断的利器——SQL全量日志》。

select /*+ read_consistency(weak) query_timeout(1000000000) */ usec_to_time(request_time) req_time, svr_Ip, trace_id, sid, client_ip, tenant_id,tenant_name,user_name,db_name, query_sql, affected_rows,ret_code, event, state, elapsed_time, execute_time, queue_time, decode_time, get_plan_time, block_cache_hit, bloom_filter_cache_Hit, block_index_cache_hit, disk_reads,retry_cnt,table_scan, memstore_read_row_count, ssstore_read_row_count, round(request_memory_used/1024/1024) req_mem_mb
from gv$sql_audit 
where tenant_id=1012 and user_name in ('demouser') 
order by request_time desc 
limit 100;

经验总结

本节是一些测试场景的经验总结,需要提前了解一些OceanBase的原理特性介绍。

实际测试情形可能会出现由于是三节点部署,所以测试时压力都打到一台服务器上,这个建议用六节点测试。还有个办法就是禁用自动负载均衡手动调整分区位置。调整是OB给业务的手段,业务上有些表会有JOIN,为了性能(避免跨节点请求),业务需要这种干预能力。

还有一种情形是写入压力非常大,跑了一段时间后报内存不足的提示,这个就是租户内存资源相对写入速度和量不足了(OB的转储和合并对内存的回收赶不上写入对内存的消耗),此时需要扩容或者调整测试需求。OB 2.x版本还有自动对应用写入限速功能(自我保护),这个会影响测试报告里性能结果。如果数据库分析sql性能以及分布式调优都做了,那可以认为当前写入的TPS就是数据库的写入峰值了。需要注意的是不同的硬件,不同的租户规格,不同的测试场景,这个TPS能力都表现不同。

sysbench有个batch insert功能,当表是分区表的时候,默认这个batch insert 很可能会产生分布式事务,性能比单表写入要慢。需要靠提高客户端并发数来提升总的吞吐量。此外这个批量的大小不宜太大。由于OB支持SQL执行计划缓存,SQL文本过大且并发很高时,会在SQL解析环节面临内存不足问题。数据库内存的大部分还是主要用于存取数据。JAVA的addBatch方法也同理,建议批量大小设置为100以内。

在查询验证数据的时候,可能会碰到超时类错误。OB里超时的可能场景有多个:

  • SQL语句超时,由租户变量 ob_query_timeout控制。单位是微秒,默认是10秒。
  • 事务空闲超时,由租户变量 ob_trx_idle_timeout控制。单位是微秒,默认120秒。
  • 事务超时,由租户变量 ob_trx_timeout控制。单位是微秒,默认100秒。

以上超时时间都可以在租户里根据实际情况修改。

其他

关于使用分布式数据库测试,不同的做法可以得到不同的结果。即使是同一个OB租户(实例),不同的人设计的表结构不同,或者SQL不同都有可能取得不同的性能数据。这些不同都是可以从原理上给出解释。熟知这些原理的更容易发挥OB的分布式数据库特点。使用越深入会发现这个原理越多且更加有趣。当然环境、场景的不同,还是会遇到一些问题。如果有碰到问题,欢迎公众号留言讨论。


原文链接
本文为云栖社区原创内容,未经允许不得转载。

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

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

相关文章

Istio 在阿里云容器服务的部署及流量治理实践

目标 在阿里云容器服务 Kubernetes 集群上部署 Istio 服务网格实践灰度发布、故障注入、熔断等 Istio 流量管理特性 准备工作 安装和设置 kubectl 客户端,请参考不同的操作系统,如果已经安装请忽略: macOS curl -LO https://kubectl.oss-cn…

mysql创建操作用户

使用root用户创建一个新的数据库wiki 创建一个新的连接 由于新建的用户名下没有表,因此会抛出异常 就好了

Java-数组的使用

public class ArrayDemo03 {public static void main(String[] args) {int[] arrays {1,2,3,4,5};// JDK1.5, 没有下标,增强写法 // for (int array : arrays) { // System.out.println(array); // }printArray(arrays);Syste…

为何你的 SaaS 想法总是失败?没想清楚这 4 个原因可能会继续失败!

作者 | Elliot Bonneville译者 | 天道酬勤 责编 | 徐威龙封图| CSDN 下载于视觉中国最初,作者是准备进行一个”7 天 SaaS 挑战“的,但当他真正坐下来的时候就面临了第一个问题:他还没有决定要做什么。不过幸好的是,作者有一个完整…

重磅开源|AOP for Flutter开发利器——AspectD

问题背景 随着Flutter这一框架的快速发展,有越来越多的业务开始使用Flutter来重构或新建其产品。但在我们的实践过程中发现,一方面Flutter开发效率高,性能优异,跨平台表现好,另一方面Flutter也面临着插件,…

Vue-cli 3.X 构建工具零基础快速上手

文章目录一、环境准备1. 安装node2. 配置镜像二、安装Vue CLI2.1. 查看当前vuecli版本2.2. 安装最新版本2.3. 安装指定版本三、创建web项目3.1. 指定创建的项目名称3.2. 安装序列图3.3. 安装序列图简述一、环境准备 声明:命令均在在cmd窗口执行 1. 安装node 2. 配置…

分布式数据库选型——数据水平拆分方案

概述 水平拆分的概念随着分布式数据库的推广已为大部分人熟知。分库分表、异构索引、小表广播、这些功能几乎是产品功能需求标配。然而有些客户使用分布式数据库后的体验不尽如意。本文尝试从数据的角度总结分布式数据的复制(replication)和分区&#x…

从代码到 Docker、Kubernetes、Istio、Knative……,或许是时候重新思考从代码到云的编程了...

作者 | Lakmal Warusawithana译者 |弯月 责编 | 徐威龙封图| CSDN 下载于视觉中国早些时候,开发人员只需编写程序、构建,然后运行。如今,开发人员还需要考虑各种运行方式,作为可执行文件在机器上运行(很有可能是虚拟机…

容器服务Windows Kubernetes使用阿里云日志服务来收集容器日志

目前,容器服务Windows Kubernetes支持将业务容器产生的stdout输出、日志文件同步到阿里云日志服务(SLS)进行统一管理。 支撑组件安装 在Windows Kubernetes集群安装界面勾选使用日志服务,集群会安装支持日志收集的必要组件logta…

Java-Arrays类

public class ArrayDemo06 {public static void main(String[] args) {int[] a {1,4,2,6,5,8,7};System.out.println(a); //hashCode [I1b6d3586// 打印数组元素 Arrays.toStringSystem.out.println(Arrays.toString(a)); // [1, 4, 2, 6, 5, 8, 7]// 自己写一个方法去打印…

看完这一篇,你就对 Spring Security 略窥门径了 | 原力计划

作者 | BoCong-Deng来源 | CSDN 博客,责编 | 夕颜头图 | CSDN 下载自东方 IC出品 | CSDN(ID:CSDNnews)写在前面开发Web应用,对页面的安全控制通常是必须的。比如:对于没有访问权限的用户需要转到登录表单页面。要实现访…

Java-冒泡排序

public class ArrayDemo07 {public static void main(String[] args) {int[] a {3,5,1,7,8,4};int[] sort sort(a);System.out.println(Arrays.toString(sort)); // [1, 3, 4, 5, 7, 8]}/*每次将 最大 或 最小的数 后移*/public static int[] sort(int[] array){// 临时变量…

借助 Cloud Toolkit 快速创建 Dubbo 工程

Cloud Toolkit 是一个 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。在最新版的插件中,提供了快速创建 Dubbo 工程的功能,下面就来快速体验下吧。 Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没…

vue-cli-service不是内部或外部命令,也不是可运行的程序

报错信息: “不是内部或外部命令,也不是可运行的程序” 步骤一: 检查package.json 中是否有 vue-cli-server,没有则需安装 步骤二 : 执行npm install命令 npm run serve

另一种声音:容器是不是未来?

作者 | Ian Eyberg译者 | 天道酬勤 责编 | 徐威龙封图| CSDN 下载于视觉中国CSDN 云计算旨在为读者提供更多角度的声音,本文仅代表作者个人观点,不代表CSDN云计算任何立场。前几天作者看到了这则推文,可以这么说,是它促使我开始就…

Java-稀疏数组

public class ArrayDemo08 {public static void main(String[] args) {// 1. 创建一个二维数组 11*11 0: 没有棋子 1: 黑棋 2: 白棋int[][] array1 new int[11][11];array1[1][2] 1;array1[2][3] 2;// 输出原始的数组System.out.println("输出原始的数组");for…

揭秘|每秒千万级的实时数据处理是怎么实现的?

1、设计背景 闲鱼目前实际生产部署环境越来越复杂,横向依赖各种服务盘宗错节,纵向依赖的运行环境也越来越复杂。当服务出现问题的时候,能否及时在海量的数据中定位到问题根因,成为考验闲鱼服务能力的一个严峻挑战。 线上出现问题…

Vue3.x 使用ref和reactive、toRef

文章目录一、使用ref1. 引入2. 定义ref变量3. 赋值.value4. return 返回值5. 将数据渲染到页面二、使用reactive 和 toRef1. 引入2. 定义reactive变量3. 赋值变量.对象4. return 返回值5. 将数据渲染到页面三、效果图源码3.1. 效果图3.2. 源码一、使用ref 1. 引入 从vue里面引…

同学,要不要来挑战双11零点流量洪峰?

阿里妹导读:双十一的零点,整个电商系统的请求速率到达峰值。如果将这些请求流量只分配给少部分 server,这些机器接收到的请求速率会远超过处理速率,新来的任务来不及处理,就会产生请求任务堆积。 今年的中间件性能挑战…

GitHub 疑遭中间人攻击,最大暗网托管商再被黑!

整理 | 伍杏玲出品 | 程序人生(ID:coder_life)近期,在全球关注新冠肺炎疫情之际,黑客却频频动作,发动攻击:GitHub 疑遭中间人攻击,无法访问从26日下午开始,有网友表示国内…