精华内容
下载资源
问答
  • 现在的U盘容量已经非常大了,一般都有16G以上,为了能放单文件大于4G的数据大多数时候我们都是把U盘格式化为ntfs格式的,所以会出现不管是大文件还是小文件,当你往U盘里复制文件或者使用发送U盘功能的时候会出现...

    现在的U盘容量已经非常大了,一般都有16G以上,为了能放单文件大于4G的数据大多数时候我们都是把U盘格式化为ntfs格式的,所以会出现不管是大文件还是小文件,当你往U盘里复制文件或者使用发送到U盘功能的时候会出现一个怪现象,那就是不管U盘速度如何,都会在前部分拷贝的时候速度非常快,基本上达到U盘的正常速度,但当到达100%的时候就会卡住好久,短的5秒,慢的几分钟才会显示复制完成。详细描述就是比如开始写能30M/s,然后慢慢降下来,最后稳定8M左右。最后到100%的时候,进度条还会读个几秒才消失,大文件的话比如电影的话进度条起码要10多秒才会消失。

    小文件拷贝都最后卡很久截图
    大文件电影拷贝都最后卡很久示意图

       而且这个问题不止体现在U盘上,移动硬盘也会出现这种问题,这里主要说的win7以上的操作系统,而且不管是usb2.0的还是usb3.0的都一样,这究竟是怎么回事呢,怎么解决呢?

       【问题原因】:

      其实这是NTFS格式的原因,具体解释不清楚,猜测是因为使用这个格式后,系统默认会先把数据放内存缓存,然后再拷贝到U盘里,所以前半部分速度特别快,后面迟迟不能结束的几秒那才是真正写入U盘或移动硬盘的速度。

       【解决方案】:

      知道是ntfs的原因就好办了,我们只需要把U盘格式化为FAT32格式就好了,但这样就没办法拷贝大于4G的单个大文件了,没关系,我们可以把U盘格式化为exfat即可,这种格式不但对U盘的寿命无影响,也支持单4G大文件的写入,所以是比较推荐的格式了,大家快试试吧,效果显著哦,另外不要用剪切文件到U盘,用复制会更快。

    格式化为exfat格式,分配单元选32KB。

    转载于:https://www.cnblogs.com/waw/p/6856304.html

    展开全文
  • 需求:完成一个08:00-22:00的个...用了layui示例中的代码为:https://www.layui.com/demo/laydate.html,全文复制代码,最后删减一部分。 首先上前端html代码:个时间选择器,一个#test-limit3,一个#test-li...

    需求:完成一个08:00-22:00的两个时间选择器,第一个时间选择器为开始时间,第二个选择器为结束时间,第一个的时间必须早于第二个时间;

    页面效果如下:

    用了layui示例中的代码为:https://www.layui.com/demo/laydate.html ,全文复制代码,最后删减一部分。

    首先上前端html代码:两个时间选择器,一个#test-limit3,一个#test-limit4

    <div class="layui-form">
    			<div class="layui-form-item">
    				<div class="layui-inline">
    					<label class="layui-form-label">完成时间:</label>
    					<div class="layui-input-inline">
    						<input type="text" class="layui-input" id="test-limit3" placeholder="HH:mm">--<input type="text" class="layui-input" id="test-limit4" placeholder="HH:mm:">
    					</div>
    					<div class="layui-form-mid layui-word-aux">
    						这里以控制在8:00-22:00为例
    					</div>
    				</div>
    			</div>
    		</div>

    需要引入两个js,一个是jquery,一个是layui.js

    然后js部分:

    使用format自定义需要的时间格式,

    定义min\max时间最小\最大值,

    ready,空间打开的初始回调函数,

    done,选择完毕的回调函数,

    重要的点是修改最大最小值,#test-limit3是修改最大值,#test-limit4修改最小值,

    layui.use('laydate', function() {
    				var laydate = layui.laydate;
    				var startDate = laydate.render({
    					elem: '#test-limit3',
    					min: "08:00:00",
    					max: "22:00:00",
    					type: 'time',
    					trigger:'click',
    					ready:formatminutes,
    					format:"HH:mm",
    					done: function(value, date) {
    						endDate.config.min = {
    							date: date.date,
    							year: date.year,
    							month: date.month - 1, //关键
    							minutes: date.minutes+1,
    							seconds: date.seconds ,
    							hours: date.hours
    						};
    					}
    				});
    				var endDate = laydate.render({
    					elem: '#test-limit4',
    					min:'08:00:00',
    					max: "22:00:00",
    					type: 'time',
    					format:"HH:mm",
    					ready:formatminutes,
    					done: function(value,date) {
    						startDate.config.max = {
    							date: date.date,
    							year: date.year,
    							month: date.month - 1, //关键
    							minutes: date.minutes-1,
    							seconds: date.seconds,
    							hours: date.hours
    						}
    					}
    				});
    				
    				function formatminutes(date){
    					console.log(date)
    					$($('.laydate-time-list li ol')[2]).find('li').remove();
    				}
    			})

    上面代码就删除掉秒和动态变化起始时间和结束时间了。

    最后添加一点css:

    .laydate-time-list li:last-child{
    				display: none;
    				width: 0;
    			}
    			.laydate-time-list>li:not(:last-child){
    				width: 50%;
    			}
    			.layui-laydate-main{
    				width: 220px;
    			}

    以上就全部完成了。

    展开全文
  • MySQL 8 复制(一)——异步复制

    千次阅读 多人点赞 2019-05-10 18:25:10
    目录 一、MySQL异步复制介绍 1. 复制的用途 2. 复制如何工作 ... 简单说,复制就是将来自一个MySQL数据库服务器(主库)的数据复制到一个或多个MySQL数据库服务器(从库)。传统的MySQL复制提供了...

    目录

    一、MySQL异步复制介绍

    1. 复制的用途

    2. 复制如何工作

    3. 两阶段提交

    二、复制实验环境

    三、安装mysql-8.0.16

    四、配置异步复制

    1. 空库

    2. 脱机

    3. 联机


    一、MySQL异步复制介绍

            简单说,复制就是将来自一个MySQL数据库服务器(主库)的数据复制到一个或多个MySQL数据库服务器(从库)。传统的MySQL复制提供了一种简单的Primary-Secondary复制方法,默认情况下,复制是单向异步的。MySQL支持两种复制方式:基于行的复制和基于语句的复制。这两种方式都是通过在主库上记录二进制日志(binlog)、在从库重放中继日志(relylog)的方式来实现异步的数据复制。二进制日志或中继日志中的记录被称为事件。所谓异步包含两层含义,一是主库的二进制日志写入与将其发送到从库是异步进行的,二是从库获取与重放日志事件是异步进行的。这意味着,在同一时间点从库上的数据更新可能落后于主库,并且无法保证主从之间的延迟间隔。

            复制给主库增加的开销主要体现在启用二进制日志带来的I/O,但是开销并不大,MySQL官方文档中称开启二进制日志会产生1%的性能损耗。出于对历史事务备份以及从介质失败中恢复的目的,这点开销是非常必要的。除此之外,每个从库也会对主库产生一些负载,例如网络和I/O开销。当从库读取主库的二进制日志时,可能会造成一定的I/O开销。如果从一个主库上复制到多个从库,唤醒多个复制线程发送二进制日志内容的开销将会累加。但所有这些复制带来的额外开销相对于应用对MySQL服务器造成的高负载来说是很小的。

    1. 复制的用途

    (1)横向扩展
            通过复制可以将读操作指向从库来获得更好的读扩展。所有写入和更新都在主库上进行,但读取可能发生在一个或多个从库上。在这种读写分离模型中,主库专用于更新,显然比同时进行读写操作会有更好的写性能。需要注意的是,对于写操作并不适合通过复制来扩展。在一主多从架构中,写操作会被执行多次,这时整个系统的写性能取决于写入最慢的那部分。

    (2)负载均衡
            通过MySQL复制可以将读操作分不到多个服务器上,实现对读密集型应用的优化。对于小规模的应用,可以简单地对机器名做硬编码或者使用DNS轮询(将一个机器名指向多个IP地址)。当然也可以使用复杂的方法,例如使用LVS网络负载均衡器等,能够很好地将负载分配到不同的MySQL服务器上。

    (3)提高数据安全性
            提高数据安全性可以从两方面来理解。其一,因为数据被复制到从库,并且从库可以暂停复制过程,所以可以在从库上运行备份服务而不会影响相应的主库。其二,当主库出现问题,还有从库的数据可以被访问。但是,对备份来说,复制仅是一项有意义的技术补充,它既不是备份也不能够取代备份。例如,当用户误删除一个表,而且此操作已经在从库上被复制执行,这种情况下只能用备份来恢复。

    (4)提高高可用性
            复制能够帮助应用程序避免MySQL单点失败,一个包含复制的设计良好的故障切换系统能够显著缩短宕机时间。

    (5)滚动升级
            比较普遍的做法是,使用一个高版本MySQL作为从库,保证在升级全部实例前,查询能够在从库上按照预期执行。测试没有问题后,将高版本的MySQL切换为主库,并将应用连接至该主库,然后重新搭建高版本的从库。

    2. 复制如何工作

            如前所述,MySQL复制依赖二进制日志,所以要理解复制如何工作,先要了解MySQL的二进制日志。
    (1)二进制日志
            二进制日志包含描述数据库更改的事件,如建表操作或对表数据的更改等。开启二进制日志有两个重要目的:

    • 用于复制。主库上的二进制日志提供要发送到从库的数据更改记录。主库将其二进制日志中包含的事件发送到从库,从库执行这些事件以对主服务器上的数据进行相同的更改。
    • 用于恢复。当出现介质错误(如磁盘故障)时,数据恢复操作需要使用二进制日志。还原备份后,将重新执行备份后记录的二进制日志中的事件。

            不难看出,MySQL二进制日志所起的作用与Oracle的归档日志类似。二进制日志只记录更新数据的事件,不用于SELECT或SHOW等语句。通过设置log-bin系统变量开启二进制日志,MySQL 8中缺省是开启的。

            二进制日志有STATEMENT、ROW、MIXED三种格式,通过binlog-format系统变量设置:

    • STATMENT格式,基于SQL语句的复制(statement-based replication,SBR)。每一条会修改数据的SQL语句会记录到binlog中。这种格式的优点是不需要记录每行的数据变化,这样二进制日志会比较少,减少磁盘I/O,提高性能。缺点是在某些情况下会导致主库与从库中的数据不一致,例如last_insert_id()、now()等非确定性函数,以及用户自定义函数(user-defined functions,udf)等易出现问题。
    • ROW格式,基于行的复制(row-based replication,RBR)。不记录每一条SQL语句的上下文信息,仅需记录哪条数据被修改了,修改成了什么样子,能清楚记录每一行数据的修改细节。其优点是不会出现某些特定情况下的存储过程、函数或触发器的调用和触发无法被正确复制的问题。缺点是通常会产生大量的日志,尤其像大表上执行alter table操作时候会让日志暴涨。
    • MIXED格式,混合复制(mixed-based replication,MBR)。是语句和行两种格式的混合使用,默认使用STATEMENT模式保存二进制日志,对于STATEMENT模式无法正确复制的操作,会自动切换到基于行的格式,MySQL会根据执行的SQL语句选择日志保存方式。

            MySQL 8缺省使用ROW格式。二进制日志的存放位置最好设置到与MySQL数据目录不同的磁盘分区,以降低磁盘I/O的竞争,提升性能,并且在数据磁盘故障的时候还可以利用备份和二进制日志恢复数据。

    (2)复制步骤
            总的来说,MySQL复制有五个步骤:

    1. 在主库上把数据更改事件记录到二进制日志中。
    2. 从库上的I/O线程向主库询问二进制日志中的事件。
    3. 主库上的binlog dump线程向I/O线程发送二进制事件。
    4. 从库上的I/O线程将二进制日志事件复制到自己的中继日志中。
    5. 从库上的SQL线程读取中继日志中的事件,并将其重放到从库上。

            图1更详细地描述了复制的细节。

    图1 复制如何工作

     

            第一步是在主库上记录二进制日志。在每次准备提交事务完成数据更新前,主库将数据更新的事件记录到二进制日志中。MySQL会按事务提交的顺序而非每条语句的执行顺序来记录二进制日志。在记录二进制日志后,主库会告诉存储引擎可以提交事务了。

            下一步,从库将主库的二进制日志复制到其本地的中继日志中。首先,从库会启动一个工作线程,称为I/O线程,I/O线程跟主库建立一个普通的客户端连接,然后在主库上启动一个特殊的二进制日志转储(binlog dump)线程,它会读取主库上二进制日志中的事件,但不会对事件进行轮询。如果该线程追赶上了主库,它将进入睡眠状态,直到主库发送信号通知其有新的事件时才会被唤醒,从库I/O线程会将接收到的事件记录到中继日志中。

            从库的SQL线程执行最后一步,该线程从中继日志中读取事件并在从库上执行,从而实现从库数据的更新。当SQL线程追赶I/O线程时,中继日志通常已经在系统缓存中,所以重放中继日志的开销很低。SQL线程执行的事件也可以通过log_slave_updates系统变量来决定是否写入其自己的二进制日志中,这可以用于级联复制的场景。

            这种复制架构实现了获取事件和重放事件的解耦,允许这两个过程异步进行。也就是说I/O线程能够独立于SQL线程之外工作。但这种架构也限制了复制的过程,其中最重要的一点是在主库上并发更新的查询在从库上通常只能串行化执行,因为缺省只有一个SQL线程来重放中继日志中的事件。在MySQL 5.6以后已经可以通过配置slave_parallel_workers等系统变量进行并行复制,在后面讨论与复制相关的性能问题时会介绍并行复制的相关细节。

            现在我们已经了解了MySQL复制是以二进制日志为基础的,但是像Innodb这样的事务引擎有自己的事务日志,如ib_logfile,这些事务日志通常被称为重做日志(redo log)。作为背景知识,简单介绍下Innodb重做日志的作用。

            对Innodb的任何修改操作都会首先在称为缓冲池(innodb buffer pool)的内存页面上进行,然后这样的页面将被标记为脏页,并被放到专门的刷新列表上,后续将由master thread或专门的刷脏线程阶段性的将这些页面写入磁盘。这样的好处是避免每次写操作都操作磁盘导致大量的随机I/O,阶段性的刷脏可以将多次对页面的修改合并成一次I/O操作,同时异步写入也降低了访问时延。然而,如果在脏页还未刷入磁盘时,服务器非正常关闭,这些修改操作将会丢失,如果写入操作正在进行,甚至会由于损坏数据文件导致数据库不可用。为了避免上述问题的发生,Innodb将所有对页面的修改操作写入一个专门的文件,并在数据库启动时从此文件进行实例恢复操作,这个文件就是重做日志文件。每当有更新操作时,在数据页变更之前将操作写入重做日志,这样当发生掉电之类的情况时系统可以在重启后继续操作。这就是所谓的预写日志(Write-ahead logging,WAL)。这样的技术推迟了缓冲区页面的刷新,从而提升了数据库的吞吐量。同时由于重做日志的写操作是顺序I/O,相对于写数据文件的随机I/O要快得多。大多数数据库系统都采用类似的技术实现。

            聪明如你可能已经有了这样的疑问,在复制中二进制日志和重做日志如何协同工作?假设Innodb写完重做日志后,服务异常关闭。主库能够根据重做日志恢复数据,但由于二进制日志没写入,会导致从库同步时少了这个事务么?或者反之,二进制日志写成功,而重做日志没有写完,是否导致从库执行事务,而主库不执行?这些情况会不会产生主从数据不一致的问题呢?解决这些问题是MySQL的核心需求,让我们从MySQL基本架构说起。图2是MySQL的逻辑结构图。

    图2 MySQL服务器逻辑架构图

     

            最上层用于处理客户端连接、授权认证、安全等等。第二层架构是MySQL服务器层。大多数MySQL的核心服务功能都在这一层,包括查询解析、分析、优化、缓存以及所有内置函数,所有跨存储引擎的功能(存储过程、触发器、视图等)都在这一层实现。如你所料,二进制日志也在这一层实现。第三层包含了存储引擎,负责MySQL中数据的存储和提取。服务器通过API与存储引擎进行通信,存储引擎只是简单地响应上层服务器的请求。显然Innodb的重做日志在这一层实现。

            由于MySQL的事务日志包含二进制日志和重做日志,当发生崩溃恢复时,MySQL主库通过重做日志进行恢复,而在主从复制的环境下,从库是依据于主节点的二进制日志进行同步数据的。这样的架构对两种日志有两个基本要求:第一,保证二进制日志里面存在的事务一定在重做日志里面存在,也就是二进制日志里不会比重做日志多事务(可以少,因为重做日志里面记录的事务可能有部分没有提交,这些事务最终可能会被回滚)。第二,两种日志中事务的顺序一致,这也是很重要的一点,假设两者记录的事务顺序不一致,那么会出现类似于主库事务执行的顺序是ta、tb、tc、td,但是二进制日志中记录的是ta、tc、tb、td,被复制到从库后导致主从数据不一致。为了达到这两点要求,MySQL使用内部XA来实现(XA是eXtended Architecture的缩写,是X/Open分布式事务定义的事务中间件与数据库之间的接口规范),其核心是两阶段提交(two phase commit,2PC)。

    3. 两阶段提交

            在两阶段提交协议中一般分为事务管理器(协调者)和若干事务执行者(参与者)两种角色。在MySQL内部实现的两阶段提交中,二进制日志充当了协调者角色,由它来通知Innodb执行准备、提交或回滚步骤。从实现角度分析,提交流程和代码框架分别如图3、图4所示。

    图3 MySQL两阶段提交流程

     

    图4 commit命令的MySQL代码框架

     

    (1)先调用binglog_hton和innobase_hton的prepare方法完成第一阶段,binlog_hton的papare方法实际上什么也没做,innodb的prepare持有prepare_commit_mutex,将重做日志刷磁盘,并将事务状态设为TRX_PREPARED。
    (2)如果事务涉及的所有存储引擎的prepare都执行成功,则调用TC_LOG_BINLOG::log_xid将事务(STATEMENT格式或ROW格式)写到二进制日志,此时,事务已经铁定要提交了。否则,调用ha_rollback_trans回滚事务,而事务实际上也不会写到二进制日志。
    (3)最后,调用引擎的commit完成事务的提交。实际上binlog_hton->commit什么也不会做(上一步已经将二进制日志写入磁盘),innobase_hton->commit则清除回滚信息,向重做日志中写入COMMIT标记,释放prepare_commit_mutex,并将事务设为TRX_NOT_STARTED状态。

            如果数据库系统发生崩溃,当重启数据库时会进行崩溃恢复操作。具体到代码层面,Innodb在恢复的时候,不同状态的事务,会进行不同的处理:

    • 对于TRX_COMMITTED_IN_MEMORY的事务,清除回滚段,然后将事务设为TRX_NOT_STARTED;
    • 对于TRX_NOT_STARTED的事务,表示事务已经提交,跳过;
    • 对于TRX_PREPARED的事务,要根据二进制日志来决定事务是否提交,暂时跳过;
    • 对于TRX_ACTIVE的事务,回滚。

            简单来讲,当发生崩溃恢复时,数据库根据重做日志进行数据恢复,逐个查看每条重做条目的事务状态,根据图3的流程,如果已进行到TRX_NOT_STARTED阶段,也就是存储引擎commit阶段,那么说明重做日志和二进制日志是一致的,正常根据重做条目进行恢复即可;事务状态为TRX_ACTIVE,没写到二进制日志中,直接回滚;如果事务状态为TRX_PREPARED,要分两种情况,先检查二进制日志是否已写入成功,如果没写入成功,那么就算是TRX_PREPARED状态,也要回滚。如果写入成功了,那么就进行最后一步,调用存储引擎commit,更改事务状态为TRX_NOT_STARTED,也就是真正提交状态,可以用作数据恢复。

            可见,MySQL是以二进制日志的写入与否作为事务提交成功与否的标志,通过这种方式让Innodb重做日志和MySQL服务器的二进制日志中的事务状态保持一致。两阶段提交很好的保持了数据一致性和事务顺序性。

            了解了所有这些技术细节后,当初的疑问自然也就有了答案。假设在阶段(1)结束之后程序异常,此时没有写入二进制日志,则从库不会同步这个事务。主库上,崩溃恢复时重做日志中这个事务没有trx_commit,因此会被回滚。逻辑上主从库都不会执行这个事务。假设在阶段(2)结束后程序异常,此时二进制日志已经写入,则从库会同步这个事务。主库上,根据重做日志能够正常恢复此事务。也就是说,若二进制日志写入完成,则主从库都会正常完成事务,反之则主从库都回滚事务,都不会出现主从不一致的问题。

            MySQL通过innodb_support_xa系统变量控制Innodb是否支持XA事务的2PC,默认是TRUE。如果关闭,则Innodb在prepare阶段就什么也不做,这可能会导致二进制日志的顺序与Innodb提交的顺序不一致,继而导致在恢复时或者从库上产生不同的数据。在MySQL 8中,innodb_support_xa系统变量已被移除,因为始终启用Innodb对XA事务中两阶段提交的支持,不再让用户来选择。

            上述的MySQL两阶段提交流程并不是天衣无缝的,主从数据是否一致还与重做日志和二进制日志的写盘方式有关。innodb_flush_log_at_trx_commit和sync_binlog系统变量分别控制两者的落盘策略。

    • innodb_flush_log_at_trx_commit:有0、1、2三个可选值。0表示每秒进行一次刷新,但是每次事务提交不进行任何操作(每秒调用fsync使数据落地到磁盘,不过这里需要注意如果底层存储有cache,比如raid cache,那么这时也不会真正落盘,但是由于一般raid卡都带有备用电源,所以一般都认为此时数据是安全的)。1代表每次事务提交都会进行刷新,这是最安全的模式。2表示每秒刷新,每次事务提交时不刷新,而是调用write将重做日志缓冲区里面的内容刷到操作系统页面缓存。从数据安全性和性能比较三种策略的优劣为:1由于每次事务提交都会是重做日志落盘,所以是最安全的,但是由于fsync的次数增多导致性能下降比较严重。0表示每秒刷新,每次事务提交不进行任何操作,所以MySQL或操作系统崩溃时最多丢失一秒的事务。2相对于0来说了多了每次事务提交时的一个write操作,此时数据虽然没有落磁,但是只要没有操作系统崩溃,即使MySQL崩溃,那么事务也是不会丢失的。
    • sync_binlog:MySQL在提交事务时调用MYSQL_LOG::write完成写二进制日志,并根据sync_binlog决定是否进行刷新。默认值是0,即不刷新,从而把控制权交给操作系统。如果设为1,则每次提交事务,就会进行一次磁盘刷新。

            这两个参数不同的值会带来不同的效果。两者都设置为1,数据最安全,能保证主从一致,这也是MySQL 8的默认设置。innodb_flush_log_at_trx_commit非1,假设在二进制日志写入完成后系统崩溃,则可能出现这样的情况:从库能够执行事务,但主库中trx_prepare的日志没有被写入到重做日志中,导致主库不执行事务,出现主从不一致的情况。同理若sync_binlog非1,可能导致二进制日志丢失(操作系统异常宕机),从而与Innodb层面的数据不一致,体现在复制上,从库可能丢失事务。在数据一致性要求很高的场景下,建议就使用缺省的全1配置。

    二、复制实验环境

    1. 主机IP
    172.16.1.125(主)
    172.16.1.126(从)
    172.16.1.127(从)

    2. 软件环境
    OS:CentOS Linux release 7.2.1511 (Core)
    MySQL:MySQL Community Server 8.0.16
    glibc:glibc-2.17-105.el7.x86_64

    3. 硬件环境
    三台虚拟机,每台基本配置为:
    . 双核双CPU,Intel(R) Xeon(R) CPU E5-2420 0 @ 1.90GHz
    . 8G物理内存,8G Swap
    . 100G物理硬盘

    三、安装mysql-8.0.16

            从https://dev.mysql.com/downloads/mysql/下载二进制安装文件mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz,相关选项如图5所示。

    图5 下载mysql-8.0.16安装包

     

    然后用root用户按顺序执行下面的命令,在三台主机上安装MySQL。

    # 进入安装目录
    cd /usr/local
    # 从tar包中把提取文件
    tar xvf /home/mysql/mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz 
    # 建立软连接
    ln -s mysql-8.0.16-linux-glibc2.12-x86_64 mysql
    # 进入mysql目录
    cd mysql
    # 建立secure_file_priv系统变量指向的目录
    mkdir mysql-files
    # 修改属主为mysql
    chown mysql:mysql mysql-files
    # 修改目录权限
    chmod 750 mysql-files
    # mysql系统初始化
    bin/mysqld --initialize --user=mysql
    # 建立SSL/RSA相关文件,如果不启用SSL连接,这步可省略
    bin/mysql_ssl_rsa_setup
    # 启动mysql服务器
    bin/mysqld_safe --user=mysql &
    # 连接mysql服务器
    bin/mysql -u root -p
    -- 修改root密码
    alter user user() identified by "123456";
    -- 创建一个新的mysql管理员账号
    create user 'wxy'@'%' identified with mysql_native_password by '123456';
    grant all on *.* to 'wxy'@'%' with grant option;

            命令说明:
    (1)mysql-files目录用作secure_file_priv系统变量的值。该变量将导入和导出操作限制到特定目录。例如由LOAD DATA和SELECT ... INTO OUTFILE语句和LOAD_FILE()函数所执行的操作。仅允许具有FILE权限的用户执行这些操作。secure_file_priv系统变量设置如下:

    • 空字符串:变量不起作用,是不安全的设置。
    • 目录名:mysql限制导入和导出操作仅用于该目录中的文件。目录必须已经存在,mysql不会创建它。
    • NULL:mysql禁用导入导出操作。

    (2)mysqld --initialize 命令创建默认数据库并退出。在过程中会创建一个超级用户,并为该用户产生一个随机密码。命令执行输出如下所示:

    [root@hdp2/usr/local/mysql]#bin/mysqld --initialize --user=mysql
    2019-05-05T06:31:58.956385Z 0 [System] [MY-013169] [Server] /usr/local/mysql-8.0.16-linux-glibc2.12-x86_64/bin/mysqld (mysqld 8.0.16) initializing of server in progress as process 10256
    2019-05-05T06:32:01.287093Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: w1SN3pgRPL*D
    2019-05-05T06:32:02.901171Z 0 [System] [MY-013170] [Server] /usr/local/mysql-8.0.16-linux-glibc2.12-x86_64/bin/mysqld (mysqld 8.0.16) initializing of server has completed
    [root@hdp2/usr/local/mysql]#

            mysql文档中说产生的临时密码会写到.err日志文件里,但在本次安装中,这步并不生成.err文件。保险的做法还是记下临时密码,这点很重要。在initialize情况下,临时密码默认标记为已过期,用户必须在第一次进入mysql后首先修改密码。

    (3)mysql_ssl_rsa_setup程序将创建SSL证书和密钥文件,使用SSL进行安全连接所需的RSA密钥对文件。如果现有的SSL文件已过期,mysql_ssl_rsa_setup也可用于创建新的SSL文件。

            mysql_ssl_rsa_setup使用openssl命令,因此主机上必须安装有OpenSSL。mysql_ssl_rsa_setup检查数据目录中的以下SSL文件:

    ca.pem
    server-cert.pem
    server-key.pem

            如果存在任何这些文件,则mysql_ssl_rsa_setup不会创建任何SSL文件。否则,它会调用openssl来创建它们,以及一些其他文件:

    ca.pem               自签名CA证书
    ca-key.pem           CA私钥
    server-cert.pem      服务器证书
    server-key.pem       服务器私钥
    client-cert.pem      客户端证书
    client-key.pem       客户端私钥

            在启用SSL安全客户端连接时需要这些文件。

            之后mysql_ssl_rsa_setup检查数据目录中的以下RSA文件:

    private_key.pem      私钥/公钥对的私有成员
    public_key.pem       私钥/公钥对的公共成员

            如果存在任何这些文件,则mysql_ssl_rsa_setup不会创建任何RSA文件。否则,它会调用openssl来创建它们。对于sha256_password或caching_sha2_password插件验证的帐户不加密连接时,通过这些文件使用RSA进行安全密码交换。

            mysql 8.0.16缺省支持SSL加密连接:

    mysql> show variables like 'have_ssl';
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | have_ssl      | YES   |
    +---------------+-------+
    1 row in set (0.00 sec)

    (4)第一次进入mysql后,执行任何命令都会报以下错误:

    ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.

            提示很明显,需要修改初始化时为用户'root'@'localhost'生成的临时密码。mysql 8缺省使用的认证插件是caching_sha2_password:

    mysql> show variables like 'default_authentication_plugin';
    +-------------------------------+-----------------------+
    | Variable_name                 | Value                 |
    +-------------------------------+-----------------------+
    | default_authentication_plugin | caching_sha2_password |
    +-------------------------------+-----------------------+
    1 row in set (0.01 sec)

            当用老版本mysql的客户端连接mysql 8服务器时,可能报以下错误:

    ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded: /home/mysql/mysql-5.6.14/lib/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory

            可以使用两个方法解决此问题。第一种方法是在配置文件中设置default_authentication_plugin='mysql_native_password',然后重启mysql服务器使之生效。default_authentication_plugin是只读系统变量,不能动态修改。第二种方法是在创建用户时,使用兼容新老版本的认证方式,例如:

    create user 'wxy'@'%' identified with mysql_native_password by '123456';

            通过以下查询可以得到用户所对应的认证插件:

    mysql> select host,user,plugin from mysql.user;
    +-----------+------------------+-----------------------+
    | host      | user             | plugin                |
    +-----------+------------------+-----------------------+
    | %         | wxy              | mysql_native_password |
    | localhost | mysql.infoschema | caching_sha2_password |
    | localhost | mysql.session    | caching_sha2_password |
    | localhost | mysql.sys        | caching_sha2_password |
    | localhost | root             | caching_sha2_password |
    +-----------+------------------+-----------------------+
    5 rows in set (0.00 sec)

    四、配置异步复制

            本实验中分别针对空库、脱机、联机三种方式,配置一主两从的mysql标准异步复制。只做整服务器级别的复制,不考虑对个别库表或使用过滤复制的情况。

    1. 空库

            初始安装后,mysql中还没有任何应用数据。此时事先配置好复制,再投入使用,是最理想也是最简单的一种情况,具体配置步骤如下。
    (1)修改server_id系统变量。

    -- 主库
    set global server_id=1125;
    
    -- 从库1
    set global server_id=1126;
    
    -- 从库2
    set global server_id=1127;

            要求复制中所有MySQL实例的server_id都不相同,这里将三个实例的server_id分别配置为1125、1126、1127。server_id系统变量可以动态修改,这样做的好处是不需要重启实例,配置即可在新连接中生效。但是为了避免MySQL重启后配置信息丢失,还需要同时在/etc/my.cnf配置文件中设置server_id参数。

    (2)查看主库二进制日志信息,

    -- 主库
    show master status;

            因为是一个静态的空库,二进制信息此时不会发生变化,可以作为复制的起始点。本例中重启过MySQL实例,输出的信息为:

    +---------------+----------+--------------+------------------+-------------------+
    | File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +---------------+----------+--------------+------------------+-------------------+
    | binlog.000003 |      155 |              |                  |                   |
    +---------------+----------+--------------+------------------+-------------------+

    (3)在主库上建立复制专属用户

    -- 主库
    create user 'repl'@'%' identified with mysql_native_password by '123456';
    grant replication client,replication slave on *.* to 'repl'@'%';

            前面说过MySQL 8的用户认证机制,这里仍然使用mysql_native_password方式。

    (4)在从库创建主库信息。

    change master to
    master_host='172.16.1.125',
    master_port=3306,
    master_user='repl',
    master_password='123456',
    master_log_file='binlog.000003',
    master_log_pos=155;

            连接主库时,需要使用change master to提供连接到主库的连接选项,包括主机地址、端口、用户名、密码、二进制文件名、复制起始事件位置等。change master to后,在mysql.slave_master_info表中就会生成一条记录,此行为是由master_info_repository系统变量控制的。MySQL 8中,该变量的缺省值为TABLE,即将与复制相关的主库信息记录到mysql.slave_master_info表中。随着复制的进行,表中的数据会随之更新。change master to只是为I/O线程连接主库时提供连接参数,这条语句本身不会连接主库。以后启动I/O线程时,I/O线程都会自动读取这条记录来连接主库,不需要再执行change master to语句。

            类似地,MySQL 8缺省会将中继日志的重放信息存到mysql.slave_relay_log_info表中。该行为由系统变量relay_log_info_repository控制。中继日志信息在首次启动复制时生成,并随复制即时改变。SQL线程再次启动时就能从中获取到从中继日志的的哪个地方继续读取、执行。

    (5)在从库启动复制并查看复制信息

    start slave;
    show slave status\G
    select * from mysql.user where user='repl'\G

            刚才我们并没有在从库上建立repl用户,但由于create user语句是在起始位置点后执行的,因此可以正常复制到从库,查询mysql.user表即可确认。       

            start slave语句会启动I/O线程和SQL线程,并创建一个到主库的客户端连接。该命令执行后,在主库的processlist中会看到类似如下的线程,这些就是从库上I/O线程所建立的,Binlog Dump表示由I/O线程在主库上启动了Binlog Dump线程,每个连接上来的从库对应一个线程,如Id 32和56是两个从库的连接线程:

    mysql> show processlist;
    +----+-----------------+------------+------+-------------+--------+---------------------------------------------------------------+------------------+
    | Id | User            | Host       | db   | Command     | Time   | State                                                         | Info             |
    +----+-----------------+------------+------+-------------+--------+---------------------------------------------------------------+------------------+
      ...
    | 32 | repl            | hdp4:3723  | NULL | Binlog Dump | 328879 | Master has sent all binlog to slave; waiting for more updates | NULL             |
    | 56 | repl            | hdp3:57308 | NULL | Binlog Dump | 319204 | Master has sent all binlog to slave; waiting for more updates | NULL             |
      ...

            从库上的processlist中会看到类似如下的线程,Id 325和326分别对应I/O线程与SQL线程:

    mysql> show processlist;
    +--------+-----------------+-----------+-------+---------+--------+--------------------------------------------------------+------------------+
    | Id     | User            | Host      | db    | Command | Time   | State                                                  | Info             |
    +--------+-----------------+-----------+-------+---------+--------+--------------------------------------------------------+------------------+
      ...
    |    325 | system user     |           | NULL  | Connect | 320408 | Waiting for master to send event                       | NULL             |
    |    326 | system user     |           | NULL  | Query   | 320408 | Slave has read all relay log; waiting for more updates | NULL             |
      ...

            从show slave status输出中可以查看复制状态信息:

    mysql> show slave status\G
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 172.16.1.125
                      Master_User: repl
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: binlog.000011
              Read_Master_Log_Pos: 155
                   Relay_Log_File: hdp3-relay-bin.000002
                    Relay_Log_Pos: 319
            Relay_Master_Log_File: binlog.000011
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
                  Replicate_Do_DB: 
              Replicate_Ignore_DB: 
               Replicate_Do_Table: 
           Replicate_Ignore_Table: 
          Replicate_Wild_Do_Table: 
      Replicate_Wild_Ignore_Table: 
                       Last_Errno: 0
                       Last_Error: 
                     Skip_Counter: 0
              Exec_Master_Log_Pos: 155
                  Relay_Log_Space: 526
                  Until_Condition: None
                   Until_Log_File: 
                    Until_Log_Pos: 0
               Master_SSL_Allowed: No
               Master_SSL_CA_File: 
               Master_SSL_CA_Path: 
                  Master_SSL_Cert: 
                Master_SSL_Cipher: 
                   Master_SSL_Key: 
            Seconds_Behind_Master: 0
    Master_SSL_Verify_Server_Cert: No
                    Last_IO_Errno: 0
                    Last_IO_Error: 
                   Last_SQL_Errno: 0
                   Last_SQL_Error: 
      Replicate_Ignore_Server_Ids: 
                 Master_Server_Id: 1125
                      Master_UUID: 8eed0f5b-6f9b-11e9-94a9-005056a57a4e
                 Master_Info_File: mysql.slave_master_info
                        SQL_Delay: 0
              SQL_Remaining_Delay: NULL
          Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
               Master_Retry_Count: 86400
                      Master_Bind: 
          Last_IO_Error_Timestamp: 
         Last_SQL_Error_Timestamp: 
                   Master_SSL_Crl: 
               Master_SSL_Crlpath: 
               Retrieved_Gtid_Set: 
                Executed_Gtid_Set: 
                    Auto_Position: 0
             Replicate_Rewrite_DB: 
                     Channel_Name: 
               Master_TLS_Version: 
           Master_public_key_path: 
            Get_master_public_key: 0
                Network_Namespace: 
    1 row in set (0.00 sec)
    
    mysql> 

            在从库上执行show slave status可以查看从库状态,输出信息非常多,其中除了那些描述I/O线程、SQL线程状态的行,还有几个log_file和pos相关的行。理解这几行的意义至关重要,所以这里完整地描述它们:

    • Master_Log_File:I/O线程正在读取的master binlog;
    • Read_Master_Log_Pos:I/O线程已经读取到master binlog的哪个位置;
    • Relay_Log_File:SQL线程正在读取和执行的relay log;
    • Relay_Log_Pos:SQL线程已经读取和执行到relay log的哪个位置;
    • Relay_Master_Log_File:SQL线程最近执行的操作对应的是哪个master binlog;
    • Exec_Master_Log_Pos:SQL线程最近执行的操作对应的是master binlog的哪个位置。

            (Relay_Master_Log_File, Exec_Master_log_Pos)构成一个坐标,这个坐标表示从库上已经将主库上的哪些数据重放到自己的实例中,它可以用于下一次change master to时指定的二进制日志坐标。与这个坐标相对应的是从库上SQL线程的中继日志坐标(Relay_Log_File, Relay_Log_Pos)。这两个坐标位置不同,但它们对应的数据是一致的。

            还有一个延迟参数Seconds_Behind_Master需要说明一下,它的本质意义是SQL线程比I/O线程慢多少。如果主从之间的网络状况优良,那么从库的I/O线程读速度和主库写二进制日志的速度基本一致,所以这个参数也用来描述“SQL线程比主库慢多少”,也就是说从库比主库少多少数据,只不过衡量的单位是秒。需要注意的是,该参数的描述并不标准,只是在网速很好的时候做个大概估计,很多种情况下它的值都是0,即使SQL线程比I/O线程慢了很多也是如此。

    2. 脱机

            如果数据库已经存在应用数据,但允许一个可接受的脱机时间窗口做复制,这种场景下常用的做法是先直接将主库的数据目录整体拷贝到从库,再启动复制。具体步骤如下。

    (1)在主库上建立复制专属用户

    -- 主库
    create user 'repl'@'%' identified with mysql_native_password by '123456';
    grant replication client,replication slave on *.* to 'repl'@'%';

    (2)停掉复制涉及的实例

    mysqladmin -uroot -p123456 shutdown

            本例中一主两从三个实例都停止。

    (3)复制将主库的数据目录整体拷贝到从库

    scp -r /usr/local/mysql/data/ 172.16.1.126:/usr/local/mysql

    (4)保证所有参与复制实例的server-uuid和server_id都不同
            这是非常重要的一步,相同的server-uuid或server_id会造成复制错误。从MySQL 5.6开始,用 128 位的 server_uuid 代替了原本的 32 位 server_id 的大部分功能。原因很简单,server_id 依赖于 my.cnf 的手工配置,有可能产生冲突。而自动产生 128 位 uuid 的算法可以保证所有的 MySQL uuid 都不会冲突。首次启动时 MySQL 会调用 generate_server_uuid() 自动生成一个 server_uuid,并且保存到 auto.cnf 文件。这个文件目前存在的唯一目的就是保存 server_uuid。在 MySQL 再次启动时会读取 auto.cnf 文件,继续使用上次生成的 server_uuid。使用 show global variables like 'server_uuid' 命令可以查看 MySQL 实例当前使用的 server_uuid​,它是一个全局只读变量。全局唯一的 server_uuid 的一个好处是,可以解决由 server_id 配置冲突带来的 MySQL 主从复制的异常终止。在从库向主库申请二进制日志时,会首先发送自己的 server_uuid,主库用从库发送的 server_uuid 代替 server_id 作为 kill_zombie_dump_threads 的参数,终止冲突或者僵死的 BINLOG_DUMP 线程。

            删除data_dir/auto.cnf文件,实例启动时会自动生成server_uuid的值。本例中删除从库的/usr/local/mysql/data/auto.cnf文件,并编辑/etc/my.cnf文件修改server_id,保证三个实例的配置互不相同。

    (5)重启实例

    mysqld_safe --user=mysql &

            本例中一主两从三个实例都启动。

    (6)查看主库二进制日志信息,

    -- 主库
    show master status;

    (7)使用上一步的输出在从库创建主库信息。

    change master to
    master_host='172.16.1.125',
    master_port=3306,
    master_user='repl',
    master_password='123456',
    master_log_file='binlog.000004',
    master_log_pos=155;

    (5)在从库启动复制并查看复制信息

    start slave;
    show slave status\G

            此时在从库的err日志文件中可以看到复制正常启动的信息:

    [mysql@hdp3~]$tail /usr/local/mysql/data/hdp3.err 
    2019-05-10T02:54:22.226249Z mysqld_safe Logging to '/usr/local/mysql/data/hdp3.err'.
    2019-05-10T02:54:22.277620Z mysqld_safe Starting mysqld daemon with databases from /usr/local/mysql/data
    2019-05-10T02:54:22.710162Z 0 [System] [MY-010116] [Server] /usr/local/mysql/bin/mysqld (mysqld 8.0.16) starting as process 20571
    2019-05-10T02:54:23.370413Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
    2019-05-10T02:54:23.420160Z 0 [System] [MY-010931] [Server] /usr/local/mysql/bin/mysqld: ready for connections. Version: '8.0.16'  socket: '/tmp/mysql.sock'  port: 3306  MySQL Community Server - GPL.
    2019-05-10T02:54:23.547717Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: '/tmp/mysqlx.sock' bind-address: '::' port: 33060
    2019-05-10T02:56:17.328454Z 144 [Warning] [MY-010604] [Repl] Neither --relay-log nor --relay-log-index were used; so replication may break when this MySQL server acts as a slave and has his hostname changed!! Please use '--relay-log=hdp3-relay-bin' to avoid this problem.
    2019-05-10T02:56:17.333635Z 144 [System] [MY-010597] [Repl] 'CHANGE MASTER TO FOR CHANNEL '' executed'. Previous state master_host='', master_port= 3306, master_log_file='', master_log_pos= 4, master_bind=''. New state master_host='172.16.1.125', master_port= 3306, master_log_file='binlog.000004', master_log_pos= 155, master_bind=''.
    2019-05-10T02:56:25.235523Z 171 [Warning] [MY-010897] [Repl] Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
    2019-05-10T02:56:25.237851Z 171 [System] [MY-010562] [Repl] Slave I/O thread for channel '': connected to master 'repl@172.16.1.125:3306',replication started in log 'binlog.000004' at position 155

    3. 联机

            脱机建立复制的需求太过理想化,大多数情况下,复制是被要求在不影响线上业务的情况下,联机创建的,而且还要求对线上库的影响越小越好。例如,复制过程化中对主库加锁会影响对主库的访问,因此通常是不被允许的。这种场景下有两种备选的复制方案:使用mysqldump程序或使用如XtraBackup的第三方工具。这两种方案有各自的适用场合。使用mysqldump联机建立复制的过程如下。

    (1)在主库上建立复制专属用户

    -- 主库
    create user 'repl'@'%' identified with mysql_native_password by '123456';
    grant replication client,replication slave on *.* to 'repl'@'%';

    (2)在从库上创建主库信息

    change master to
    master_host='172.16.1.125',
    master_port=3306,
    master_user='repl',
    master_password='123456';

            注意在上面这条命令中并没有指定主库二进制文件的文件名和位置。

    (3)在从库用mysqldump建立复制

    mysqldump --single-transaction --all-databases --master-data=1 --host=172.16.1.125 --user=wxy --password=123456 --apply-slave-statements | mysql -uroot -p123456 -h127.0.0.1

            说明:

    • --single-transaction参数可以对Innodb表执行非锁定导出。此选项将事务隔离模式设置为REPEATABLE READ,并在转储数据之前向服务器发送START TRANSACTION SQL语句。它仅适用于Innodb等事务表,因为它会在发出START TRANSACTION时转储数据库的一致状态,而不会阻塞任何应用程序。因此这里假定:1. 所有的应用数据表都使用Innodb引擎。2. 所有系统表数据在备份过程中不会发生变化。
    • --master-data参数会导致转储输出包含类似 CHANGE MASTER TO MASTER_LOG_FILE='binlog.000004', MASTER_LOG_POS=1480; 的SQL语句,该语句指示主库的二进制日志坐标(文件名和位置)。如果选项值为2,则CHANGE MASTER TO语句将写为SQL注释,因此仅提供信息,不会执行。如果参数值为1,则该语句不会写为注释,并在重新加载转储文件时执行。如果未指定选项值,则默认值为1。
    • --apply-slave-statements参数会在CHANGE MASTER TO语句之前添加STOP SLAVE语句,并在输出结尾处添加START SLAVE语句,用来自动开启复制。
    • 通过管道操作符,导出导入一步进行,不需要中间落盘生成文件。

    (4)确认复制状态

    -- 从库
    show slave status\G

            mysqldump方式的优点是可以进行部分复制,如在配置文件中定义replicate-do-table=db1.*,则用这种方法可以只复制db1库而忽略其它复制事件。缺点是由于mysqldump会生成主库转储数据的SQL语句,实际是一种逻辑备份方式所以速度较慢,不适用于大库。

            联机建立复制的另一种可选方案是使用XtraBackup。XtraBackup是Percona公司的开源项目,用以实现类似Innodb官方的热备份工具InnoDB Hot Backup的功能,它支持在线热备份,备份时不影响数据读写。到目前为止,最新的版本为Percona XtraBackup 8.0.6,可以从https://www.percona.com/downloads/下载安装包。XtraBackup有很多功能和优点,例如支持全备、增量备份、部分备份;支持压缩备份;备份不影响数据读写、事务等,但是也有缺陷不足:例如不支持脱机备份、不支持直接备份到磁带设备、不支持Cloud Back,MyISAM的备份也会阻塞。不过这些小瑕疵不影响XtraBackup成为一款流行的MySQL备份工具。另外,注意XtraBackup只支持Linux平台,不支持Windows平台。下面演示用XtraBackup联机搭建主从复制的过程,主库已经建立了用于执行复制的用户repl。

    (1)在主、从库安装XtraBackup

    # 安装依赖包
    yum -y install libev
    
    # 安装XtraBackup
    rpm -ivh percona-xtrabackup-80-8.0.6-1.el7.x86_64.rpm

    (2)配置主库到从库的SSH免密码连接

    # 主库执行
    ssh-keygen    
    ... 一路回车 ...    
    ssh-copy-id 172.16.1.126

    (3)停止从库,并清空从库的数据目录

    # 从库执行
    mysqladmin -u root -p123456 shutdown
    
    # 清空数据目录
    rm -rf /usr/local/mysql/data/*

    (3)备份并传输

    # 主库执行
    xtrabackup -uroot -p123456 --socket=/tmp/mysql.sock --no-lock --backup --compress --stream=xbstream --parallel=4 --target-dir=./ | ssh mysql@172.16.1.126 "xbstream -x -C /usr/local/mysql/data/ --decompress"

            这条命令连接主库,进行并行压缩流式备份,同时将备份通过管道操作符传输到从库,并直接解压缩到从库的数据目录。所有操作一条命令完成,不需要中间落盘生成文件。

    (4)在从库恢复备份

    # 应用日志
    xtrabackup --prepare --target-dir=/usr/local/mysql/data/

    (5)查看二进制日志坐标

    cat /usr/local/mysql/data/xtrabackup_binlog_info

    (6)启动从库

    mysqld_safe --user=mysql &

    (7)登录从库启动复制

    -- 创建主库信息,其中的master_log_file和master_log_pos值来自第(5)步
    change master to
    master_host='172.16.1.125',
    master_port=3306,
    master_user='repl',
    master_password='123456',
    master_log_file='binlog.000011',
    master_log_pos=155;
    
    -- 启动复制
    start slave;
    
    -- 确认复制状态
    show slave status\G

            XtraBackup是物理复制,性能比mysqldump高的多,而且对主库的影响极小,非常适用于从头联机创建高负载、大数据量、全实例从库的场景。

    展开全文
  • 利用MATLAB实现图片切换动画效果详解

    万次阅读 多人点赞 2018-03-15 16:59:51
    内容摘要:本博文介绍MATLAB图片切换动画效果的制作以及GIF文件保存,并结合具体代码详细解释。...本文先逐步介绍实现思路及代码细节,最后附上完整代码。 1. 前言 MATLAB中为数据可视化提供了全面系统的功...

    内容摘要:本博文介绍MATLAB图片切换动画效果的制作以及GIF文件保存,并结合具体代码详细解释。介绍了利用MATLAB编程进行几幅图片的轮流切换,切换时实现与幻灯片切换相似的炫酷的图片切换特效。其中涉及一些MATLAB的高级图形设计,具体的函数及命令文中会详细介绍。本文先逐步介绍实现思路及代码细节,最后附上完整代码。

    1. 前言

           MATLAB中为数据可视化提供了全面系统的功能函数,据此可以绘制出许多形象生动的精美图像。同时MATLAB的句柄图形为用户提供了强有力的工具,可以很方便地对图形的每个可能方面进行控制,这在需要设计GUI的场合用处很大。刚接触时看到许多用MATLAB制作出炫丽的仿真动画不明觉厉,在大学的MATLAB编程课里也经常有编写一个图片切换动画的作业或课设。这里写一个有几幅图片轮流切换不同效果的程序,为此提供一个参考思路,希望对大家有所启发。

    2. MATLAB动画制作

           本节介绍MATLAB中动画制作的常用方式,对原理比较熟悉的读者可直接跳至第3节。众所周知动画其实是很多张图片的以一定时间间隔的逐帧切换,一个简单直接的想法当然就是下面代码中方式了。首先读取一张图片,通过for循环逐步扩大要显示图片的索引范围,达到逐步放大图片的效果。

    I=imread('image.jpg');%读图
    [x,y,z]=size(I);%尺寸
    while 1
    for i=1:100
       imshow(I(1:x/100*i,1:y/100*i,:));%逐渐放大显示
    end
    end

                                                                                                图2.1 显示效果图

           上述代码有点粗糙,运行过程中随着图片的放大窗口也在不断抖动。这其实跟imshow( )函数有关,每次调用它显示一张图片系统就会自动新创建一个窗口,窗口的大小是根据要显示图片的大小自动缩放的,上面的for循环中不断调用imshow( )而图片的大小是不断变大的,显示窗口自然也跟着变化。

           其实MATLAB为动画制作提供了三种实现方式:质点动画、电影动画、程序动画。质点动画是最简单的动画产生方式,产生一个顺着曲线轨迹运动的质点来操作;电影动画首先会保存一系列的图形数据,然后按照一定的顺序像电影一样的播放;程序动画是在图形窗口中按照一定的算法连续擦除和重绘图形对象。下面逐个简单演示一下

    2.1 质点动画

           质点动画由comet、comet3函数产生质点动画,分别对应二维和三维坐标下的质点。首先求解出质点完整的运动轨迹坐标x,y(三维时还有z),将x,y作为输入参数使用comet或comet3直接绘制动点。

     

    调用格式调用说明
    comet(y)显示质点绕着向量y的动画轨迹运动(二维)
    comet(x,y)显示质点在横轴、纵轴方向的运动随向量x,y的动画轨迹(二维)
    comet(x,y,p) 效果与上一个相同,额外定义轨迹尾巴线的长度p*length(y),p介于0,1之间,默认为0.1

    comet3函数的使用方法与comet相似。

    实例代码如下:

    clf;  
    clear;  
    grid on;
    
    vx = 100*cos(1/4*pi);  
    vy = 100*sin(1/4*pi);  
    t = 0:0.02:15;  
    dx = vx*t;  
    dy = vy*t-9.8*t.^2/2;  
    comet(dx, dy);

    效果如图2.1.1所示

     

     

                                                                                               图2.1.1 质点动画效果图

    2.2 电影动画

           电影动画和电影的制作有点相似,实际可以看出是一个先“拍”再“播”的过程,即捕捉将要构成动画帧的图像逐个存到一个大矩阵中,然后播放这个大矩阵的数据。

    基本步骤:
      1、调用moviein函数初始化内存,创建一个足够大的矩阵,用于存储构成每一帧图像的数据。
      2、利用getframe抓取当前画面(即每帧图像),返回的数据用于构成动画矩阵。
      3、调用movie函数按照指定的速度进行指定次数播放该电影动画。例如:movie(M, n)可以播放由矩阵M所定义的画面n次,默认只播放一次。

    getframe与movie函数调用格式见表2.2.1及表2.2.2:getframe函数可以捕捉动画帧,并保存到矩阵中。

    表2.2.1 getframe函数用法
    调用格式调用说明
    f=getframe从当前图形框中得到动画帧
    f = getframe(h)从图形句柄h中得到动画帧
    f=getframe(h,rect)从图形句柄h的指定区域rect中得到动画帧

    当创建了一系列动画帧后,可利用movie函数播放这些动画帧。该函数的用法有:

    表2.2.2 movie函数用法
    调用格式调用说明
    movie(M)将矩阵M中的动画帧播放一次
    movie(M, n)将矩阵M中的动画帧播放n次
    movie(M,n,fps)将矩阵M中的动画帧以每秒fps帧的速度播放n次

    实例:旋转的山峰动画

    clc; clear;  
       
    % peaks是一个函数,其中有2个变量。由平移和放缩高斯分布函数获得。  
    % 参数为30,得到的X、Y、Z为30*30的矩阵。  
    [X, Y, Z] = peaks(30);  
    % surf绘制三维曲面图  
    surf(X,Y,Z);  
       
    axis([-3,3,-3,3,-10,10]);  
    % 关闭所用坐标轴上的标记、格栅和单位标记。但保留由text和gtext设置的对象  
    axis off;  
    shading interp;   
    colormap(hot);  
    
    M = moviein(20);% 建立一个20列的大矩阵    
    for i = 1:20    
       view(-37.5+24*(i-1),30);% 改变视点     
       M(i) = getframe;% 将图形保存到M矩阵   
    end  
    
    movie(M,2);% 播放画面2次

    运行结果如图2.2.1所示

     

     

                                                                                              图2.2.1 电影动画实例

    2.3 程序动画

           在MATLAB中把用于数据可视和界面制作的基本绘图要素称为句柄图形对象,每个图形对象有相应的属性值,例如线条Line对象就有颜色、位置等属性。可以改变图形对象的属性值,重绘图形对象,从而创建程序动画。其基本思路是:首先新建一个图形窗口,再循环内逐渐改变图形对象的相应属性值,并使用drawnow函数更新当前图形,整个循环就会表现出变化的动画效果。

    实例代码如下

    clear;
    clc;
    %% 新建图形窗口并设置初始属性
    hFigure=figure('menubar','none','NumberTitle','off','position',...
         [800 800 360 360],'name','图片切换动画效果');
     movegui(hFigure,'center');%设置居中
     %设置坐标轴属性
     hAxes=axes('Visible','off','units','normalized','position',[0 0 1 1]);
     
    %% 在图形窗口中显示图片 
     Im=imread('image.jpg');
     hIm=imshow(Im);
     [x,y,z]=size(Im);
    %% 修改属性并重绘
    for i=1:100
    I=Im(1:x/100*i,1:y/100*i,:);%逐渐放大显示
    set(hIm,'CData',I);
    drawnow;
    end

            代码5-8行新建图形窗口并为窗口中的对象设置初始属性,可以理解为给后面显示图像提供一个自定义的环境。首先第5行figure函数用于Figure图形窗口的创建,括号中参数设置相应属性,其中'menubar','none'表示禁用菜单栏;‘NumberTitle’,‘off’表示图形标题中不显示图形编号;‘Position’,'[800 800 360 360]'表示设置图形窗口的位置与大小,格式为[左 底 宽 高];‘Name’,'图片切换动画效果',表示设置图形窗口的标题。

           第7行设置图形窗口在居中位置。第9行axes函数用于Axes坐标对象的创建,即在当前图形窗口中新建一个坐标轴,括号里面参数设置上与figure格式相似,即设置坐标轴不可见、计量单位为常规、绘图区域的位置和大小设为[0 0 1 1]([左 底 宽 高])。

          第12-13行为读入图片,并在上面设置的坐标中显示图片,hIm为image(图形)对象的句柄,可通过句柄对该对象进行操作。

          第16-20行是在for循环中修改image(图形)对象的CData属性,并重绘图形从而实现动画的。具体的第18行中,set就是一个用于设置一般对象属性的函数,第一个参数hIm为图形句柄用于指定对哪个对象进行操作,这里就是image对象了;后面‘CData’,‘I’是指当前要显示的图像数据设置为矩阵I。整句就是指对hIm对象设置其要显示的图像这一属性为I。第19行drawnow就是将属性改变了的图形显示出来。

    实现的效果如图2.3.1所示

                                                                                      图2.3.1 程序动画效果

    3. 图片切换效果制作

           经过前面动画制作原理的介绍,这节就正式说说开头那个效果的实现了,为了便于编程实现(电影动画方式需先存后播编程稍显繁琐)这里采用程序动画的制作方式。首先需要准备几张长方形图片,为了方便后面处理其大小尺寸应该一致,即几张图像有相同长宽,并与接下来编写的M文件放在同一文件夹下。新建一个M文件命名为imageswitch.m,我们后面的编程都在该文件下进行。

    3.1 效果一实现

           如图3.1.1所示,将长方形图片做一个简单划分(这里我的图片尺寸为1920*1200),以宽度的大小为边长在中间掏出一个正方形,就是图中两条蓝色线条分割的中间部分,那么两条线的位置就是(1920-1200)/2和1920-(1920-1200)/2也就是360,和1560,如此一来这张图片就分成了左右两个长条和中间正方形区域了。

                                                                                              图3.1.1 分割图片

           现在一步步从头开始编写程序吧,首先实现的是第二幅图片的左边长条由顶部向下移动逐渐覆盖原图片的左边长条,同时右边的长条也逐渐被覆盖不过是由底部向上移动的。在程序中这一过程其实是一个图片矩阵中一部分元素逐渐被另一矩阵中元素所替换的过程。说白了图片的存储和处理都是以矩阵的形式,图3.1那张图片就是一个1920*1200*3的三维矩阵,左边长条的移动就是横坐标范围在1-360,纵坐标范围为1-1200的所有元素逐渐由第二张图数据矩阵的相同范围上的对应元素所赋值的过程了。我们看一段代码理解一下。

    function imageswitch1()
    %需显示的图片文件名预存
    S=char('BingWallpaper-2016-09-27.jpg',...
        'BingWallpaper-2016-10-07.jpg');
    Imagename=cellstr(S);
    %% 读入图片
    I1=imread(Imagename{1});I2=imread(Imagename{2});
    %% 转换存储格式为double
    I1=im2double(I1);I2=im2double(I2);
    %% 
    [x,y,z]=size(I1);
    Im=I1;
    %% 创建图形窗口并设置图形对象初始属性
    hFigure=figure('menubar','none','NumberTitle','off','position',...
        [1000 1000 720 450],'name','图片切换动画效果');
    movegui(hFigure,'center');
    axes('Visible','off','units','normalized','position',[0 0 1 1]);
    % 显示图片
    hIm=imshow(Im);
    
    step=x/100;%渐变步长
    Length=(y-x)/2;
     % 动画效果一
        for i=step:step:x
            % 改变图像数据
            Im(1:i,1:Length,:)=I2(x-i+1:x,1:Length,:);
            Im(x-i+1:x,y-Length+1:y,:)=I2(1:i,y-Length+1:y,:);
            set(hIm,'CData',Im);%设置image对象CData属性为Im
            drawnow ;%重绘当前图形窗口
        end
    end

           代码中第1-19行是新建图形窗口并对图形对象设置属性值为后面动画制作提供一个好的“环境”,在2.3节中已经详细介绍这里就不再赘述了。21-30行就是重点了,21行是设置一个赋值的范围跨度,值越大后面动画进行的速度就越快;22行的Length就是小长条的宽,为了方便后面用到;整个动画的实现在for循环中进行,第26行Im是将要显示的矩阵而后面的I2是下一张图片的数据矩阵,在第12行Im已经被赋值为第一张图片的数据矩阵I1了,现在要做的就是随着for循环的进行i的值逐渐增大长条逐渐被赋值了。22行语句如下

    Im(1:i,1:Length,:)=I2(x-i+1:x,1:Length,:);

          可以看到第二维和第三维的范围两边矩阵是相同的,都分别是1:Length、:,即第二维索引范围是1到Length(小长条的宽),第三维索引取全部范围(第三维可认为是对色彩的设定,“:”表示取索引全部范围,就可以认为是保留色彩了,实际还得理解下RGB图像的存储方式哦)。第一维范围为1到i,随i的增大越来越多的区域被I2中x-i+1到x范围的I2覆盖。同理第23行也是逐渐覆盖原有图像右边长条,所以这是第二维的范围应该是y-Length+1到y,而右边的长条下一张图片的那一部分是向下逐渐覆盖的,故第一维两个矩阵的索引范围与左边的相反。

           第24行将image对象的CData属性设置为赋值后的Im,即显示的图像是新的Im。第25行用drawnow函数重绘图形窗口,就会显示新的一帧。最终以上代码运行的动画效果如图3.1.2所示

                                                                                               图3.1.2 长条移动演示

           将中间正方形区域平均分成上下两部分,上部分区域由下一幅图片相应部分向右移动覆盖,下部分由下一幅图片相应部分向左移动覆盖,效果一的完整效果就是图3.1.3所示的

                                                                                          图3.1.3 效果一的完整效果

           这只要在上面实现长条的代码23-24间加上以下两行代码其原理与长条移动相同,只不过这时的索引范围需要有所改变上层范围为1到x/2,即上半部分,第二维Im是从Length+1(正方形开始的地方)至Length+i(随i的增大逐渐覆盖这个正方形宽度范围)被I2中最右边部分开始的像素部分取代,而第二行正好覆盖的方向相反。

    Im(1:x/2,Length+1:Length+i,:)=I2(1:x/2,y-Length-i+1:y-Length,:);
    Im(x/2+1:x,y-Length-i+1:y-Length,:)=I2(x/2+1:x,Length+1:Length+i,:);

           效果一的完整代码如下,新建到imageswitch2.m文件中即可实现图3.1.3的效果

    function imageswitch2()
    %需显示的图片文件名预存
    S=char('BingWallpaper-2016-09-27.jpg',...
        'BingWallpaper-2016-10-07.jpg');
    Imagename=cellstr(S);
    %% 读入图片
    I1=imread(Imagename{1});I2=imread(Imagename{2});
    %% 转换存储格式为double
    I1=im2double(I1);I2=im2double(I2);
    %% 
    [x,y,z]=size(I1);
    Im=I1;
    %% 创建图形窗口并设置图形对象初始属性
    hFigure=figure('menubar','none','NumberTitle','off','position',...
        [1000 1000 720 450],'name','图片切换动画效果');
    movegui(hFigure,'center');
    axes('Visible','off','units','normalized','position',[0 0 1 1]);
    % 显示图片
    hIm=imshow(Im);
    
    step=x/10;%渐变步长
    Length=(y-x)/2;
     % 动画效果一
    
        for i=step:step:x
            % 改变图像数据
            Im(1:i,1:Length,:)=I2(x-i+1:x,1:Length,:);
            Im(x-i+1:x,y-Length+1:y,:)=I2(1:i,y-Length+1:y,:);
            Im(1:x/2,Length+1:Length+i,:)=I2(1:x/2,y-Length-i+1:y-Length,:);
            Im(x/2+1:x,y-Length-i+1:y-Length,:)=I2(x/2+1:x,Length+1:Length+i,:);
            set(hIm,'CData',Im);%设置image对象CData属性为Im
            drawnow ;%重绘当前图形窗口
        end
        Im=I1;
    
    end

             同样可以将覆盖的方向改变,如取与上面相反的覆盖方向就可以实现又一种切换效果了。

    3.2 缩放效果制作

           对于中间的正方形区域可以设计一个缩放的效果,如图3.2.1所示。

                                                                                             图3.2.1 缩放演示效果

     

    这时对正方形赋值的代码如下     

    Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I2(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);

           要实现缩小就需要正方形区域的索引范围以一定速度缩小,最终由下一张图片完全覆盖,实现图3.2.1效果的完整代码如下

    function imageswitch3()
    %需显示的图片文件名预存
    S=char('BingWallpaper-2016-10-13.jpg',...
        'BingWallpaper-2016-10-14.jpg');
    Imagename=cellstr(S);
    %% 读入图片
    I1=imread(Imagename{1});I2=imread(Imagename{2});
    %% 转换存储格式为double
    I1=im2double(I1);I2=im2double(I2);
    %% 
    [x,y,z]=size(I1);
    Im=I1;
    %% 创建图形窗口并设置图形对象初始属性
    hFigure=figure('menubar','none','NumberTitle','off','position',...
        [1000 1000 720 450],'name','图片切换动画效果');
    movegui(hFigure,'center');
    axes('Visible','off','units','normalized','position',[0 0 1 1]);
    % 显示图片
    hIm=imshow(Im);
    
    step=x/10;%渐变步长
    Length=(y-x)/2;
     % 动画效果一
    while 1
        
        for i=x:-step:step  
            Im=I1;
            Im(x-i+1:x,1:Length,:)=I2(1:i,1:Length,:);
            Im(1:i,y-Length+1:y,:)=I2(x-i+1:x,y-Length+1:y,:);
            
            Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I2(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);
            set(hIm,'CData',Im);%设置image对象CData属性为Im
            drawnow ;%重绘当前图形窗口
        end
        Im=I1;
    end
    end

           同样可以设计一个从中间放大而覆盖图像的效果,这只需要改变上面代码中for中的赋值部分就可以了,至于具体如何修改大家只要参考第4节中的完整代码就行了,限于篇幅这里就不多说了。

    3.3 将动画保存为GIF

           MATLAB 制作gif动态图的基本思想就是,将一张张的静态图组合成一张能动的gif图片,并保存到相应的位置。 那么,要想制作一张动态图,首先要有若干个静态图,并且他们的索引值是连续的。将我们制作的动画保存下来其完整代码如下

    function imageswitch4()
    %需显示的图片文件名预存
    S=char('BingWallpaper-2016-10-13.jpg',...
        'BingWallpaper-2016-10-14.jpg');
    Imagename=cellstr(S);
    %% 读入图片
    I1=imread(Imagename{1});I2=imread(Imagename{2});
    %% 转换存储格式为double
    I1=im2double(I1);I2=im2double(I2);
    %% 
    [x,y,z]=size(I1);
    Im=I1;
    %% 创建图形窗口并设置图形对象初始属性
    hFigure=figure('menubar','none','NumberTitle','off','position',...
        [1000 1000 720 450],'name','图片切换动画效果');
    movegui(hFigure,'center');
    axes('Visible','off','units','normalized','position',[0 0 1 1]);
    % 显示图片
    hIm=imshow(Im);
    
    step=x/10;%渐变步长
    Length=(y-x)/2;
     % 动画效果一
    
        jo=0;
        for i=x:-step:step  
            Im=I1;
            Im(x-i+1:x,1:Length,:)=I2(1:i,1:Length,:);
            Im(1:i,y-Length+1:y,:)=I2(x-i+1:x,y-Length+1:y,:);
            
            Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I2(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);
            set(hIm,'CData',Im);%设置image对象CData属性为Im
            drawnow ;%重绘当前图形窗口
    
            jo=jo+1;
            %依次输出图片
            print(hFigure,'-dbmp',sprintf('%d',jo))   
        end
        
    %% 依次读取生成的所有图片
    for j=1:jo
        %获取当前图片
        A=imread(sprintf('%d.bmp',j));
        [I,map]=rgb2ind(A,256);
        %生成gif,并保存
        if(j==1)
            imwrite(I,map,'movefig.gif','DelayTime',0.1,'LoopCount',Inf)
        else
            imwrite(I,map,'movefig.gif','WriteMode','append','DelayTime',0.1)    
        end
    end
    
    end

     

           前面第1-33行代码与之前的代码一致,在第37行print(hFigure,'-dbmp',sprintf('%d',jo)) 中hFigure是当前图形窗口的图形句柄,指定输出当前窗口的图像,'-dbmp'指定输出文件格式,sprintf('%d',jo)是对bmp文件命名为jo。后面第41-51行的for循环是遍历刚刚生成的图片将其组合成gif文件,生成的gif文件如图3.2.2所示。

     

     

     

                                                                                                  图3.2.2 movefig.gif

    4. 完整代码

           实现文中开头图中的效果的完整代码如下所示,在MATLAB中新建一个m文件,命名为imageswitch.m运行即可。注意将相应图片放在m文件相同文件夹下,图片的命名需与代码中一致(也可修改代码中文件名)免得出错。下面是本文所用到的图片文件,需要的可以选中复制下来用于测试程序,只是需要改下文件名与这里一致。

    %作品:图片切换动画效果
    %作者:吴限 
    %2018.3.14
    function imageswitch()
    %需显示的图片文件名预存
    S=char('BingWallpaper-2016-09-27.jpg',...
        'BingWallpaper-2016-10-07.jpg',...
        'BingWallpaper-2016-10-13.jpg',...
        'BingWallpaper-2016-10-14.jpg',...
        'BingWallpaper-2016-12-26.jpg');
    Imagename=cellstr(S);
    %% 读入图片
    I1=imread(Imagename{1});I2=imread(Imagename{2});
    I3=imread(Imagename{3});I4=imread(Imagename{4});
    I5=imread(Imagename{5});
    %% 转换存储格式为double
    I1=im2double(I1);I2=im2double(I2);I3=im2double(I3);
    I4=im2double(I4);I5=im2double(I5);
    %% 
    [x,y,z]=size(I1);
    Im=I1;
    %% 创建图形窗口并设置图形对象初始属性
    hFigure=figure('menubar','none','NumberTitle','off','position',...
        [1000 1000 720 450],'name','图片切换动画效果');
    movegui(hFigure,'center');
    axes('Visible','off','units','normalized','position',[0 0 1 1]);
    % 显示图片
    hIm=imshow(Im);
    
    step=x/10;%渐变步长
    Length=(y-x)/2;
    
    while 1
        % 动画效果一
        for i=step:step:x
            % 改变图像数据
            Im(1:i,1:Length,:)=I2(x-i+1:x,1:Length,:);
            Im(x-i+1:x,y-Length+1:y,:)=I2(1:i,y-Length+1:y,:);
    
            Im(1:x/2,Length+1:Length+i,:)=I2(1:x/2,y-Length-i+1:y-Length,:);
            Im(x/2+1:x,y-Length-i+1:y-Length,:)=I2(x/2+1:x,Length+1:Length+i,:);
            
            set(hIm,'CData',Im);%设置image对象CData属性为Im
            drawnow ;%重绘当前图形窗口
        end
       
         % 动画效果二
        Im=I2;
        for i=x:-step:step
            Im=I4;
            Im(x-i+1:x,1:Length,:)=I3(1:i,1:Length,:);
            Im(1:i,y-Length+1:y,:)=I3(x-i+1:x,y-Length+1:y,:);
            
            Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I3(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);
            set(hIm,'CData',Im);%设置image对象CData属性为Im
            drawnow ;%重绘当前图形窗口
        end
        
        
        %动画效果三
        Im=I3;
        for i=x:-step:step
            Im=I1;
            Im(1:i,1:Length,:)=I4(x-i+1:x,1:Length,:);
            Im(x-i+1:x,y-Length+1:y,:)=I4(1:i,y-Length+1:y,:);
            
            Im(1:x/2,Length+1:Length+i,:)=I4(1:x/2,y-Length-i+1:y-Length,:);
            Im(x/2+1:x,y-Length-i+1:y-Length,:)=I4(x/2+1:x,Length+1:Length+i,:);
      
            set(hIm,'CData',Im);%设置image对象CData属性为Im
            drawnow  ;%重绘当前图形窗口
        end
        
        %动画效果四
        Im=I4;
        for i=1:step:x
            Im(x-i+1:x,1:Length,:)=I5(1:i,1:Length,:);
            Im(1:i,y-Length+1:y,:)=I5(x-i+1:x,y-Length+1:y,:);
            
            Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I5(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);
            
            set(hIm,'CData',Im);%设置image对象CData属性为Im
            drawnow ;%重绘当前图形窗口
        end
        
        Im=I5;
    end
    end

    用到的图片文件:

     

     

     

    BingWallpaper-2016-09-27.jpg

    BingWallpaper-2016-10-07.jpg

    BingWallpaper-2016-10-13.jpg

    BingWallpaper-2016-10-14.jpg

    BingWallpaper-2016-12-26.jpg

          本文中所有代码的完整m文件还有图片文件已经上传,代码经调试通过

    下载链接:https://download.csdn.net/download/qq_32892383/10288793

    有需要的朋友在评论区留言,可以将文件发送至你的邮箱。

    【公众号获取】
    本人微信公众号已创建,扫描以下二维码并关注公众号“AI技术研究与分享”,后台回复“IS20180315”即可获取全部资源文件。

    5. 结束语

           这就是利用MATLAB进行图片切换动画的全部内容了,还有很多炫丽的动画效果可以创造,将动画效果用到PPT演示和GUI设计等方面定能增色不少。本博文介绍的方法在多次修改之下,使其程序更加严谨。由于编者能力有限,代码即使经过了多次校对,也难免会有疏漏之处。希望您能热心指出其中的错误,以便下次修改时能以一个更完美更严谨的样子,呈现在大家面前。同时如果有更好的实现方法也请您不吝赐教。

    展开全文
  • 1.在data中定义一个变量,存储时间 data(){ return { nowTime:'' } }, 2.给定一个div <div>... //显示当前时间(年月日时分) timeFormate(timeStamp) { let year = new Date(timeSta...
  • MySQL 8 复制(六)——拓扑与性能

    千次阅读 多人点赞 2019-07-04 16:39:59
    一、复制拓扑 1. 一主一(多)从 2. 双(多)主复制 4. 多源复制 5. Blackhole引擎与日志服务器 二、复制性能 1. 测试规划 2. sync_binlog与innodb_flush_log_at_trx_commit 3. 组提交与多线程复制 4. 基于...
  • QPS从04000请求每,谈达达后台架构演化之路

    万次阅读 多人点赞 2016-08-29 09:26:02
    QPS从04000请求每,谈达达后台架构演化之路 达达是全国领先的最后三公里物流配送平台。 达达的业务模式与滴滴以及Uber很相似,以众包的方式利用社会闲散人力资源,解决O2O最后三公里即时性配送难题...
  • 石子合并问题--动态规划;贪心

    万次阅读 多人点赞 2015-10-17 20:18:13
    求将这N堆石子合并成分析:当然这种情况是最简单的情况,合并的是任意堆,直接贪心即可,每次选择最小的堆合并。本问题实际上就是哈夫曼的变形。(2)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能...
  • 陈永庭,饿了么框架工具部高级架构师,主要负责MySQL异地双向数据复制,支撑饿了么异地多活项目。曾就职于WebEx、Cisco、腾讯等公司。 今天我主要分享饿了么多活的底层数据实施,会和大家介绍在整个多活的设计和...
  • Spring整合FreeMarker进行国际化的过程很简单,将spring-webmvc里的org/springframework/web/servlet/view/freemarker下的spring.ftl include各个要国际化的ftl文件里就行了,比如将这个spring.ftl复制在ftl文件...
  • MySQL数据库:主从复制Replication

    千次阅读 2018-12-26 14:57:56
    MySQL主从复制的原理:Slave从Master获取binlog二进制日志文件,然后再将日志文件解析成相应的SQL语句在从服务器上重新执行一遍主服务器的操作,通过这种方式来保证数据的一致性。由于主从复制的过程是异步复制的,...
  • unity3d动画操作以及动画实现

    万次阅读 2015-02-22 15:13:04
     //定义一个动画曲线 起始位置 坐标为-3.023f,经过5.0后 坐标移动2.96f AnimationCurve curve=AnimationCurve.Linear (0.0f,-3.023f,time,2.96f); //添加 curve.AddKey (2*time,-3.023f ); ...
  • MySQL主从复制的目的是读写分离,一个数据库只负责读,一个只负责写,需要在主机和从机进行配置。MySQL常用的规范包括命名规范、设计规范、SQL语句规范和行为规范,在进行数据库编程时要遵守。最后从MySQL基础、...
  • mysql半同步复制&组复制&全同步机制

    千次阅读 2018-08-13 17:02:57
    MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并关心从库是否已经接收并处理,这样就会有一个问题,主如果crash掉了,此时主上已经提交的事务可能并没有传从上,如果...
  • Navisworks动画制作总结

    万次阅读 2018-07-12 01:00:26
    所以,近半个月来都在与NVS朝夕相伴,说起来学习这个软件的时间也长,但在做的过程中,有遇到了不少的难题,也解决了一些问题,因此简单写个笔记,避免以后掉同样的坑。Navisworks中的场景动画(animator)分为...
  • 意想不到的MySQL复制延迟原因

    千次阅读 2017-05-28 16:41:33
    线上有个MySQL实例,存在严重的复制延迟问题,原因出乎意料。 线上有个MySQL 5.7版本的实例,从服务器延迟了3万多,而且延迟看起来好像还在加剧。 MySQL版本 Server version: 5.7.18-log MySQL Community ...
  • 完美解决Excel复制后粘贴空白的问题

    万次阅读 2016-05-01 01:03:38
    问题:分别双击打开份Excel,分别为E1和E2,从E1选择任意单元格拷贝,转E2,粘贴,鼠标指针忙碌状态,几后粘贴成功了(某些情况第一次粘贴也为空白),换单元格再次粘贴,粘贴结果为空白。   你认为是...
  • MySQL复制原理与配置

    千次阅读 2013-11-15 15:47:09
    1复制配置 1.1如何搭建复制 1.1.1master配置 开启二进制日志并创建唯一的Server ID:在my.cnf文件的[mysqld]节加入相应配置信息并重启Server使其生效。 [mysqld] log-bin=mysql-bin server-id=1 注意事项 复制组中每...
  • 本文首发在个人博客上(7988888.xyz),此文章中所有链接均通过博客进行访问。 在EyeLink系列眼仪中,我们在采集数据的过程中会得到格式为EDF...但是,可以直接查看数据,此时,就需要将EDF格式转换为ASC格式,A
  • 安卓手机如何更改开机 关机 动画

    千次阅读 2013-01-19 20:15:40
    开机画面,一般是分为2屏,当然也有3屏的说法,不管怎样,我这里说得就是最后的一屏,按照bootanimation的字面意思翻译,大概也就是开机动画的意思,那这就说第几屏了,直接用“开机动画”这词。 首先,开机...
  • android 动画卡顿分析工具

    千次阅读 2014-09-02 09:40:46
     上一次记录了解决过度绘制的过程,这一次,想先弄清个概念性的东西,就是如何判断顺顺畅?  这东西其实最初我自己也觉得有点废话,用起来会卡就明显是顺畅咯。  但这东西就跟我很想吐槽很多应用一样,明明...
  • Android的动画是由一系列的连续PNG图片作为帧组成的动画形式,是一个文件包,将各帧PNG图片以压缩方式保存。...一般包含一个文件和个目录:1.动画属性描述文件:desc.txt——–这是一个保存形式为ANSI格式,用于设置
  • 史上最全的MySQL高可用架构之【主从复制】【故障转移】【读写分离】【负载均衡】
  • svg动态图

    千次阅读 2018-07-17 10:28:55
    前面的三条CSS3都是可以有所担当的,最后这一条,呵呵,CSS3只能蹲在墙角画圈圈了! SVG的动画元素是和SMIL开发组合作开发的。SMIL开发组和SVG开发组合作开发了SMIL动画规范,在规范中制定了一个基本的XML动画...
  • OpenGL动态曲线的实现

    千次阅读 2015-11-22 20:17:52
    这几天在学OpenGL,觉得这个挺好的,没有网易邮箱,所以复制一下。  原文链接:http://hexiong26.blog.163.com/blog/static/131280813201088103731617/  首先,我们来理解一下如何实现动画,想必大家都知道...
  • Clickhouse集群应用、分片、复制

    万次阅读 2018-10-11 14:19:37
    通常生产环境我们会用集群代替单机,主要是解决个问题: 效率 稳定 如何提升效率?一个大大大任务,让一个人干需要一年,拆解一下让12个人同时干,可能只需要1个月。对于数据库来说,就是数据分片。 如何提升...
  • jQuery动画的hover连续触发动画bug处理

    千次阅读 2015-07-07 12:14:00
    JavaScript Code复制内容剪贴板 $("#menu").hover(  function () {  $("#menu").animate({ height: "500" }, 5000);  },  function () {  $("#menu").animate({ height: "100" }, 5000); ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 82,821
精华内容 33,128
关键字:

复制到最后两秒不动