mysql gtid 还是pxc_记一次 PXC 集群拆分引发的思考

原标题:记一次 PXC 集群拆分引发的思考

40960459b1ceef3100917826b66cb21b.png

作者简介

冷正磊

2018年2月加入去哪儿网 DBA 团队,主要负责机票业务的 MySQL 和 Redis 数据库的运维管理工作,以及数据库自动化运维平台部分功能的开发工作,对数据库技术具有浓厚兴趣,具有多年 MySQL 和 Redis 运维管理和性能优化经验。

1. 内容摘要

众所周知,MySQL 基于 GTID 复制功能的出现,极大地简化了 MySQL 复制拓扑初始化配置和变更以及高可用的切换。在去哪儿网,我们大量使用 PXC(Percona XtraDB Cluster)集群,然而 PXC 中用于记录事务的 Galera GTID 与普通的 MySQL GTID 还是有一点差异,运维过程中如果不加注意,可能会引发一些问题。本文通过记录一次 PXC 集群拆分的过程中由于未深刻理解这两者的差别而导致的问题与原因分析,总结了 Galera GTID 与 MySQL GTID 的异同点以及运维过程中应该注意的事项。

2. 背景

Qunar 机票核心业务某个 PXC 集群 C1 由于运行时间比较久,随着业务的持续发展,集群单个节点的实例数据大小已达到 5T 以上,对数据量如此大的 MySQL 集群进行日常维护(备份、集群节点水平扩容、实例迁移等)以及实例故障恢复都是一项比较耗时费力的工作。经过与研发讨论后决定将集群 C1 中比较大的两个库 DB1 和 DB2 拆分出来,组成一个新的 PXC 集群 C2。

集群拆分前后示意图如下(正常每个集群有三个节点,为简单起见,每个集群只画了一个节点):

e460af675f68779ef6f5e2e48ecce339.png

3. 方案简要说明

PXC 集群进行库的拆分,大致流程是使用当前集群 C1 的任意一个节点做一个全量副本,利用这个副本再做2个节点的数据,组建一个三节点的新集群 C2。同时为了保持数据的一致性,新集群 C2 的写节点作为原有集群 C1 某个节点的从库,不断同步集群 C1 的数据更新。

0977704410b3d6768464c42938e12392.png

原计划是第一步先迁移 DB1,主要流程为:

业务方下线集群 C1 上与 DB1 相关的应用服务,停止对该库中所有表的写入。

为了防止遗漏的应用服务对 DB1 进行写入,DBA 将该库里面所有的表进行改名,即加一个统一的后缀(需提前准备好脚本)。

DBA 确认两个集群直接主从同步无延迟后,在新集群 C2 上恢复 DB1 所有表的名称,即去掉第2步中添加的后缀(需提前准备好脚本)。

业务方发布新的应用服务(业务方已提前修改好代码中的数据源配置),开始访问集群 C2 中的 DB1,各系统验证业务是否正常。

第二步是在完成第一步之后,仍然保持两个集群之间主从同步关系,等使用 C2 中 DB1 相关的业务确认无问题后以同样的方式迁移 DB2。

全部迁移完后观察一段时间,确认各业务流程正常,最后删除两个集群中不需要的 DB。

其中第一步操作过程如下:

cb1a3deb7df0bef2827b9560bb08d6be.png

不过在顺利完成第一步后出现了意外,原本应该正常同步数据的两个集群出现了复制中断,根据报错信息发现大量的数据(除 DB1 之外的库)在集群 C2 上找不到对应的记录,由于两个集群中 DB2 的数据没法保证一致性,导致不得不中止后续的迁移计划,以至于集群 C2 上只完成了库 DB1 的迁移。

4. 问题分析与复现4.1 问题分析

正常来说,集群 C1 已经彻底停止(表名已改)了对 DB1 中表的写入,而集群 C2 上只会对 DB1 中表进行写入,其他库的写入不受影响,应该正常复制才对。

既然复制出现了问题,那么原有的“理所当然”的想法肯定存在不合理的地方。经过排查,我们发现了一个令人匪夷所思的问题,两个集群用作复制的两个节点,从库和主库的 GTID 的 部分竟然是一样的,导致从库在对 DB1 进行写入后,生成的 GTID 的 值比主库上大,当接收主库推送过来的 binlog 数据时,发现主库事务的 GTID 的 值比自己的小,于是从库直接选择了跳过该事务,并没有重放这部分 binlog,从而出现了主从数据不一样的情况。

4.2 复现过程

为什么新建的从库生成的 GTID 的 会和主库一样呢?为了找到问题的原因,我们在测试环境用同样的流程对出现的问题进行复现。

首先我们复盘了下搭建主从复制的过程,大致如下:

1、使用 Xtrabackup 备份集群 C1 某个节点的全量数据,并将数据传送到目的服务器,用于新建集群 C2 的第一个节点。

2、在目的服务器 apply 备份日志后,根据生成的文件 xtrabackup_ binlog_info 中的内容找到复制信息。

# catxtrabackup_binlog_info

mysql-bin.000015997 401 cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,

c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-4,

da5e0de8-dc13-ee14-76e6-f074e061cc69:1-2

3、新建文件 grastate.dat,并根据 apply 后生成的文件 xtrabackup_galera_info中的内容填写 grastate.dat 文件信息。

# cat xtrabackup_galera_info

3faa7d16-23ee-11eb-94f9-3fbe474800d2:4

# vim grastate.dat# GALERA saved stateversion: 2.1uuid: 3faa7d16-23ee-11eb-94f9-3fbe474800d2seqno: 4safe_to_bootstrap: 1

4、 以 bootstrap-pxc 方式启动该实例,作为集群 C2 的第一个节点,并与老集群 C1 建立复制关系。

# 启动实例/etc/init.d/mysql.server -P 3311 bootstrap-pxcmysql> reset slave all;Query OK, 0 rows affected (0.00 sec)

# 建立新的复制mysql> set wsrep_on = 0;Query OK, 0 rows affected (0.00 sec)

mysql> reset master;Query OK, 0 rows affected (0.00 sec)

mysql> set wsrep_on = 1;Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL gtid_purged='401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-4,da5e0de8-dc13-ee14-76e6-f074e061cc69:1-2';Query OK, 0 rows affected (0.01 sec)

mysql> change master to master_host='10.86.41.xxx',master_port=3306,master_user='replication',master_password='xxxxxxxxxx',master_auto_position=1;Query OK, 0 rows affected, 2 warnings (0.02 sec)

mysql> start slave;Query OK, 0 rows affected (0.00 sec)

# 集群C2此时的master信息mysql> show master statusG*************************** 1. row ***************************File: mysql-bin.000002Position: 271Binlog_Do_DB:Binlog_Ignore_DB:Executed_Gtid_Set: 401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-4,da5e0de8-dc13-ee14-76e6-f074e061cc69:1-21 row in set (0.00 sec)

5、 向集群 C1 中未迁移的库 test2 中正常写入数据,观察主从 master 信息。

# 写入前集群C1的master状态mysql> show master statusG*************************** 1. row ***************************File: mysql-bin.000015Position: 997Binlog_Do_DB:Binlog_Ignore_DB:Executed_Gtid_Set: 401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-4,da5e0de8-dc13-ee14-76e6-f074e061cc69:1-21 row in set (0.00 sec# 向集群C1的库test2中写入两个事务的数据后master状态mysql> use test2;mysql> insert into t values(13);Query OK, 1 row affected (0.00 sec)

mysql> insert into t values(14);Query OK, 1 row affected (0.00 sec)

mysql> show master statusG*************************** 1. row ***************************File: mysql-bin.000015Position: 1481Binlog_Do_DB:Binlog_Ignore_DB:Executed_Gtid_Set: 401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-6, # 发生变化的GTIDda5e0de8-dc13-ee14-76e6-f074e061cc69:1-21 row in set (0.00 sec)

# 此时集群C2的master信息mysql> show master statusG*************************** 1. row ***************************File: mysql-bin.000002Position: 735Binlog_Do_DB:Binlog_Ignore_DB:Executed_Gtid_Set: 401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-6, # 发生变化的GTID,同步正常da5e0de8-dc13-ee14-76e6-f074e061cc69:1-21 row in set (0.00 sec)

此时复制没有问题,数据也是正常的。

6、向集群 C2 中已迁移的库 test1 中写入两个事务的数据,观察主从 master 信 息。

mysql> insert into t values(7);Query OK, 1 row affected (0.00 sec)mysql> insert into t values(8);Query OK, 1 row affected (0.00 sec)

mysql> show master statusG*************************** 1. row ***************************File: mysql-bin.000002Position: 1209Binlog_Do_DB:Binlog_Ignore_DB:Executed_Gtid_Set: 401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-8, # 写入后,从节点发生变化的GTIDda5e0de8-dc13-ee14-76e6-f074e061cc69:1-21 row in set (0.00 sec)

7、 此后如果集群 C1 上继续写入一个事务。

mysql> delete from t where id = 13; # 删除test2库t表中id=13的记录Query OK, 1 row affected (0.00 sec)# 集群C1的master信息mysql> show master statusG*************************** 1. row ***************************File: mysql-bin.000015Position: 1723Binlog_Do_DB:Binlog_Ignore_DB:Executed_Gtid_Set: 401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-7, # GTID的gno增加1da5e0de8-dc13-ee14-76e6-f074e061cc69:1-21 row in set (0.00 sec)

# 集群C2的master信息mysql> show master statusG*************************** 1. row ***************************File: mysql-bin.000002Position: 1209Binlog_Do_DB:Binlog_Ignore_DB:Executed_Gtid_Set: 401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-8, # GTID的gno没有发生变化,因为同步过来的事务的GTID的gno值比自己小,选择跳过da5e0de8-dc13-ee14-76e6-f074e061cc69:1-21 row in set (0.00 sec)

# 此时C1集群已经被删掉的记录,在C2集群仍然存在mysql> use test2;Database changedmysql> select * from t where id = 13;+----+| id |+----+| 13 |+----+1 row in set (0.00 sec)

从测试过程中发现,确实通过以上的方式搭建主从节点后,存在主从节点写入后 GTID 值的部分相同的情况,此时如果主从节点同时发生写入(针对不同的库),就会导致主从节点数据不一致。

4.3 问题原因

对问题复现过程进行分析后,发现整个过程有一步是多余的,即步骤3(新建 grastate.dat 文件),因为我们的目的是通过搭建从库的方式组建一个新的集群,而不是对原有集群扩增节点,所以在该 slave 节点(针对原集群而言)以 bootstrap 方式启动时,不需要指定原来的集群信息。

当以 bootstrap 方式启动 PXC 实例时,如果 grastate.dat 文件存在,那么该实例会从该文件中获取 uuid 参数的值,赋值给参数 wsrep_ cluster_state_uuid,同时该参数的值也决定了事务的 Galera GTID 中的 部分,所以导致这个实例的与原集群中节点的 相同,当作为主从的两个节点都有写入时,从库在应用 binlog 时就会出现冲突或者忽略的情况,导致主从数据不一致。

5. 如何改进

通过此次集群拆分过程中出现的问题,总结下原因以及改进措施,避免后续工作中出现类似情况:

5.1 原因

延用了前期类似经验的惯性思维。因为在此之前有过两次类似的迁移操作,只不过当时只需迁移一个库,在原集群停止该库的写入后,等从库复制无延迟后就马上断开了复制,所以没有出现后续复制的问题。

操作流程不够精细。操作过程中没有严格区分 PXC 集群新增节点和拆分集群的流程差异,细节之处欠缺考虑,也反映出个人在对 PXC 的使用和原理方面研究不够深入。5.2 改进措施

分别制定 PXC 集群新增节点和拆分集群的操作规范,在运维操作时严格按照规范执行。

优化集群拆分方案。比如可建立一个中间节点,对要迁移的数据库进行过滤复制,然后再同步到新集群中,可节省组建新集群的时间,同时可避免后续在新集群上删除多余的库。

81c7d9806c877839e4d2c51cd540fc24.png

3. 在标准规范的基础上,实现运维操作自动化,避免人为主观因素造成影响。

6. 关于 Galera GTID 与 MySQL GTID 的比较6.1 GTID 的概念

GTID 特性是 MySQL5.6加入的一个强大的特性,全称是 Global Transaction Identifier。MySQL 会为每一个 DML/DDL 操作增加一个唯一标记叫做 GTID,这个标记在整个复制环境中都是唯一的,格式为 。

GTID 相关的几个常见术语:

server_ uuid:单个 GTID 的前半部分,即 部分,是一个32字节+1字节(/0)的字符串。

gno:单个 GTID 的后半部分,即 部分,表示事务的序号,gno 的值从全局计数器 next_ free_ gno 中获取的。

GTID SET:表示一个 GTID 的集合,可以包含多个 server_ uuid,如 executed_ gtid、gtid_ purged。

GTID SET Interval:GTID SET 中某个 server_uuid 可能包含多个区间,比如 GTID 为“23d45aa2-3d1f-11e6-a16b-c81f66e1165d:1-99:110-200”的字符串中,GTID SET Interval 分别是“1-99”和“110-200”。6.2 GTID 的生成

GTID 是在 SQL 的 commit 命令发起后,order commit 执行到 flush 阶段需要生成 GTID Event 的时候才会获取。MySQL 内部维护了一个全局的 GTID 的计数器 next_ free_gno 用于生成 gno。

可参考函数 Gtid_ state∶getautomatic_gno,部分代码如下∶

// 定义∶Gtid next_candidate={ sidno,sidno == get_server_sidno? next_free_gno: 1};// 赋值∶while( true){constGtid_set::Interval *iv= ivit.getO;// 定义IntervaL指针指向这个链表指针开头,如果在进行下次循环会获得NULLrpl_gno next_interval_start=iv != NULL? iv->start: MAX_GNO;// 正常情况下不会为NULL,因此 next_interval_start 等于第一个interval的start,当然如果初始化会为NULL,如果Interval->next =NULL 则标示设有区间了。while(next_candidate.gno < next_interval_start &&DBUG_EVALUATE_IF( "simulate_gno_exhausted", false, true))// 如果next_candidate.gno正常不会小于next_intervalL_start// 如果Interval->next =NULL或者初始化next_interval_start会被置为MAX_GNO,那么条件成立DBUG_RETURN(next_candidate.gno);// 返回了这个gno 则GTID生成{// 返回gno,GTID生成if(owned_gtids.get_ownernext_candidate)==O)DBUG_RETURN(next_candidate.gno)// 如果本GTID已经被其他线程占用,则next_candidate.gno++ 继续判断next_candidate.gno++;}......}

6.3 server_uuid 的生成

MySQL 在启动的时候会调用 init_ server_auto_ options 来读取 auto.cnf 文件。如果 auto.cnf 文件不存在,则会调用函数 generate_server_ uuid 来生成一个新的 server_uuid,这时 GTID 会发生改变。

当 auto.cnf 文件不存在时,调用函数 generate_ serve_ruid 生成 server_ uuid 的过程中可以看出,server_uuid 的生成至少和下面部分有关∶

数据库的启动时间。

线程的 LWP ID。LWP 是轻量级进程(light-weight process)的简称。

一个随机的内存地址。

下面是部分代码供参考∶

// 获取MySqL启动时间consttime_t save_server_start_time=server_start_time;// 加入LWP号运算server_start_time+=((ulonglong)current_pid << 48)+current_pid;// 一个内存指针,即线程结构体的内存地址thd->status_var.bytes_sent=(ulonglong)thd;// 具体的运算过程lex_start(thd);func_uuid= new(thd->mem_root)Item_func_uuid;func_uuid->fixed= 1;func_uid->vaL_str(&uuid);

6.4 Galera GTID

PXC 集群记录事务的 Galera GTID 中 的生成逻辑与 MySQL GTID 的不太一样,而且也不是 PXC 集群的 wsrep_ cluster_state_uuid,还是以上面的 PXC 集群 C2 为例,看下这个几个参数的值:

mysql> select @@server_uuid;+--------------------------------------+| @@server_uuid |+--------------------------------------+| 4fd32e4d-249f-11eb-8fd9-fa163e05f092 |+--------------------------------------+1row inset ( 0. 00sec)mysql> select @@global.gtid_executed;+---------------------------------------------------------------------------------------------------------------------------------+| @@global.gtid_executed |+---------------------------------------------------------------------------------------------------------------------------------+| 401cdbc9-e228-ee17-496f-5c53bc36ae5b:1-1123,c05582e9-dc11-ee14-6b06-c041b8b7ff2d:1-8,da5e0de8-dc13-ee14-76e6-f074e061cc69:1-2 |+---------------------------------------------------------------------------------------------------------------------------------+1row inset ( 0. 00sec)

mysql> show status like 'wsrep_%_uuid';+--------------------------+--------------------------------------+| Variable_name |Value |+--------------------------+--------------------------------------+|wsrep_local_state_uuid | 3faa7d16-23ee-11eb-94f9-3fbe474800d2 || wsrep_gcomm_uuid |4f807d05- 249f- 11eb-a679-ea70d2c3575a ||wsrep_cluster_state_uuid | 3faa7d16-23ee-11eb-94f9-3fbe474800d2 |+--------------------------+--------------------------------------+3rows inset ( 0. 00sec)

可以看到,PXC 集群中实例的 server_ uuid 并不在它的 GTID SET 中,当 PXC 集群写入数据时,生成的 MySQL GTID 的,也不是服务器的 server_uuid。

实际上在 PXC 集群中这么设计合理的,因为 PXC 是一个分布式可多写的集群架构,所有节点共享相同的 ,当在不同的节点写入数据时,将产生同样的 GTID SET,看起来不同的事务像是在同一个服务器上执行的。

6.5 Galera GTID vs MySQL GTID

两种 GTID 使用的格式相同,即 。

对于 Galera 来说,在集群以 bootstrap 启动时会生成 ,且集群中的所有节点共享此 。

所以说 PXC 集群中各节点之间用作同步的 Galera GTID 和 MySQL GTID 之间并没有直接关系,在运维过程中切记不要搞混淆。

参考资料:

1、简书专栏《深入理解主从原理32讲》 作者:重庆八怪2、《MySQL 运维内参》 作者:周彦伟、王竹峰、强昌金

责任编辑:

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

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

相关文章

一个奇怪的注意事项TNS-12545 TNS-12560 TNS-00515

近来的reportDB无法从一开始就与系统收听&#xff0c;比较奇怪的现象。由于server有听众的一个实例上正常启动&#xff0c;这是不是从开始监听器的实例手动启动是正常的。所以写下来未能找到离奇写的原因。1、故障现象Starting CRON daemondone Oracle 10g auto start/stop Sta…

java_IO总结(一)

所谓IO&#xff0c;也就是Input与Output的缩写。在java中&#xff0c;IO涉及的范围比较大&#xff0c;这里主要讨论针对文件内容的读写 其他知识点将放置后续章节&#xff08;我想&#xff0c;文章太长了&#xff0c;谁都没耐心翻到最后&#xff09; 对于文件内容的操作主要分为…

mysql两张表联查更新语句_sql联合查询语句(两张表)

展开全部sql联合查询语句(两张e69da5e6ba9062616964757a686964616f31333365643662表)是&#xff1a;select A.ID,A.VALUE,A.TYPE,A.NAME,B.KEY,B.ID,B.VALUE,B.NAMEmin(VALUE),max(VALUE) from A left join B on A.ID B.IDwhere B.NAME"你输入的名字"and B.VALUE &g…

TextField对象相关的属性和方法总结

TextField对象相关的属性和方法&#xff0c;内容十分丰富&#xff0c;下面几个表格&#xff1a; 表一 TextField 对象的方法 方法 说明 TextField.addListener 加入接收触发事件如文本域内容变化或滚动变化的监听对象&#xff0c;触发事件可以参看最后一个表。 TextField.getDe…

ocsng mysql connection problem_OCSNG 介绍及其工作原理

OCSNG部署&#xff1a;http://wowking.blog.51cto.com/1638252/994441OCSNG 是什么呢&#xff1f;OCSNG就是Open Computer and Software Inventory Next Generation是一款免费软件&#xff0c;它使用户能够盘点网络工程师的IT资产。OCS-NG收集有关运行OCS客户端程序(“OCS Inve…

hdu--5135--贪心

尽量选边数大的3根木棍来组成一个三角形 一直到无法选取为止 这边计算三角形面积 还是用 海伦公式比较方便 1 #include <iostream>2 #include <algorithm>3 #include <cmath>4 #include <cstring>5 #include <iomanip>6 using namespace std;7 …

动易sitefactory 数据库 mysql msssql_动易SiteFactory配置文件(web.config)常用配置节解读...

配置声明节复制代码代码如下:这个节定义了,web.config文件中将要用到具体配置节点的名称,处理程序等.从这里可以看出,siteFactory里采用.net 企业应用程序块(enterpriseLibrary),里的数据访问块等.urlrewritingnet,是用来进行地址重写的,接着是asp.net ajax 配置节声明,最后有个…

Struts2_2_第一Struts2应用

web.xml文件的配置与1同样。1&#xff09;HelloWorld类的代码&#xff1a; public class HelloWorldAction {private String message;public String getMessage() {return message;}public void setMessage(String message) {this.message message;}public String execute() {…

centos7

一.CentOS下的JDK安装(jdk1.7.0.67) http://www.cnblogs.com/Jenny-sider/p/3898583.html 二.centos下 配置多个tomcat nginx绑定域名和ip http://www.cnblogs.com/Jenny-sider/p/3908254.html 三.centOS 7.0默认使用的是firewall作为防火墙 启动firewall systemctl start fire…

About SOuP

http://www.soup-dev.com - SOuP的强大我不必多说。 - Maya 也可以开发出象houdini这样有趣的节点&#xff0c;这再soup之前是很难想象的。所以说soup的作者Peter的NB之处就在这里&#xff0c;他给人们指出了挖掘Maya潜力的一条路。 - MayaAPI真的是已经把maya能扩展的接口都暴…

mysql 动态sql 解析json数据_在SQL 中生成JSON数据

这段时间接手一个数据操作记录的功能&#xff0c;刚拿到手上的时候打算用EF做&#xff0c;后来经过仔细考虑最后还是觉定放弃&#xff0c;最后思考再三决定&#xff1a;1、以模块为单位分表、列固定(其实可以所有的操作记录都放到同一个表&#xff0c;但是考虑到数据量大的时候…

Microsoft Edge 浏览器开始支持webkit私有样式

微软表示新版的浏览器Edge(spartan)不会再增加新的私有属性&#xff0c;同时移除了部分-ms-属性&#xff0c;但很多标准在没有支持到之前&#xff0c;会使用webkit的api。Edge开发工程师Jacob Rossi列出了一份Edge所支持的webkit api列表。 CSS Core webkitBackgroundwebkitBac…

cemtos7上mysql8两个实例_centos7上配置mysql8的双主互写

注意:1、主库1&#xff1a;10.1.131.75&#xff0c;主库2&#xff1a;10.1.131.762、server-id必须是纯数字&#xff0c;并且主从两个server-id在局域网内要唯一。【主节点1】vi /etc/my.cnf[mysqld]log-binmysql-binserver-id2019001log_slave_updates1重启服务service mysqld…

不属于python数据类型的是_Python不支持的数据类型有( )。

(2) 完成划线处的代码填空: Dim classname(1 To 8000) As String 存储原始数据中的班级名称 Dim selectabc(1 To 8000) As String 存储原始数据选项的值 Dim xiangmu(1 To 8000) As String 存储原始数据选项的序号 Dim n As Integer Private Sub Command…

【Daily Scrum】12-08

因为TFS的一些问题&#xff0c;到现在一直都看不了Sprint 3的burndown and burn rate. 今天的scrum发现这个Sprint期间大家组里的事情都比较多&#xff0c;不过大家还是有很努力地在晚上和周末来完成ASC Master的任务&#xff0c;辛苦~ Member Today’s WorkTomorrow’s WorkFe…

java 字符串转成图片_java 转换图片为字符串,将字符串转换成图片显示

java 转换图片为字符串&#xff0c;将字符串转换成图片显示&#xff0c;该方法只适用于比较小的图片传输&#xff0c;50K以内&#xff1a;try{// 将图片转换成字符串File imgFile new File("f:\\Vista.png");FileInputStream fis new FileInputStream( imgFile );b…

图片延迟加载和滑动翻页

一&#xff1a;预加载 首先&#xff0c;我们需要引用JS&#xff0c; <script src"/Scripts/JQuery.LazyLoad.js"></script>其次&#xff0c;修改img的格式为&#xff1a; <img data-original"/images/img01.png" src"/images/grey.gif…

基本上,把switch,用设计模式代替,肯定是bug和过度设计。想想,本来修改一个文件几行代码可以解决的问题,变成修改3-6个类才能实现一样的功能。不是傻是什么?...

那些迷信设计模式的人&#xff0c;来修改一下这个方法吧。看看你最终的代码膨胀为几倍。。。 1 public virtual PasswordChangeResult ChangePassword(ChangePasswordRequest request)2 {3 if (request null)4 throw new ArgumentNullException(&qu…

inotify-tools、sersync配置及压力测试

一、Inotify介绍&#xff1a; Inotify 是一个 Linux 内核特性&#xff0c;它监控文件系统&#xff0c;并且及时向专门的应用程序发出相关的事件警告&#xff0c;比如删除、读、写和卸载操作等。您还可以跟踪活动的源头和目标等细节。使用 inotify 很简单&#xff1a;创建一个文…

java jml_JML 入门

【IT168 技术文章】面向对象分析和设计的原则之一就是应当尽可能地把过程设想往后推。我们大多数人只在实现方法之前遵守这一规则。一旦确定了类及其接口并该开始实现方法时&#xff0c;我们就转向了过程设想。那么到底有没有别的选择?和大多数语言一样&#xff0c;编写 Java …