自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

LeoHan

工作学习笔记、心得

  • 博客(251)
  • 资源 (12)
  • 收藏
  • 关注

原创 redis 主从同步、集群、持久化

redis有两种方式实现持久化:RDB和AOFAOF:类似数据库WAL 机制,但是redis是先执行命令,然后在记录AOF日志,是一种写后日志而不是咱们常说的写前日志(这样做主要是为了redis在写入日志之前不会对命令进行语法检查,只记录成功的命令,避免了出现记录错误命令的情况,并且命令执行完之后在记录不会阻塞当前的写操作,但是这样也会带来风险:数据可能丢失(执行完命令宕机)、可能阻塞其他他操作(AOF日志需要写磁盘))另外,如果运行时间较长,AOF日志比较快,在重启恢复的时候,较大的AOF会导致恢复较

2022-02-13 22:35:39 1359

原创 MySql中InnoDB锁注意事项

通过前面分析,Mysql事务级别,锁的级别分类,redo log和undo log,事务实现机制我们知道MySQL中有多中锁类型,那么这些锁是怎么作用的呢?总结下来如下(在InnoDB RR级别下):1. mysql中锁都是基于数据的,RR级别上默认就是使用next-key lock2. 在数据查找过程中访问到的对象就会加锁1. 原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭 区间。2. 原则 2:查找过程中访问到的对象才会加锁。

2022-02-10 21:56:56 774

原创 java中String.format输出%

在java中,很多时候我们都会使用String.format对一些字符串进行拼接或者格式化处理,比如:String.format(" AND %s = '%s'",colName,value);但是如果我们想生成比如类似SQL中like语句:String.format(" AND %s = '%%s%'",colName,value);如果按照上述写法,会报错,java.util.UnknownFormatConversionException,这时候需要对%转义,将要输出%的地方换成两个%即可

2022-01-27 17:29:03 2755

原创 MySql查询优化-执行计划explain详细说明

我们在MySql中一般相对查询进行优化的时候,都会借助MySql提供的查询计划explain进行分析,一般我们在我们需要执行查询的SQL前加上explain关键字,然后在此基础上进行分析,一般常见的explain输出如下:对上面这几个属性,我们一个一个俩分析说明。id在一个大的查询包含多个子查询中,每个子查询的执行顺序,id相同,从上往下执行;id不同,id值越大优先级越高,越先被执行;id为nul时表示一个结果集,不需要使用它查询,一般出现在包含union等查询中mysql> explai

2022-01-26 14:55:22 780

原创 java同步之线程池ThreadPoolExecutor实现原理

一般我们在java编程时为了提供程序的性能,很多时候会借助CPU多核优势,进行多线程处理,将一个大任务分给多个线程并发处理,加速处理速,而java默认提供了几种线程池实现:Executors.newFixedThreadPoolExecutors.newCachedThreadPoolExecutors.newSingleThreadExecutor()Executors.newScheduledThreadPool而这些线程池的实现,底层大部分都是基于ThreadPoolExecutor,我

2022-01-25 17:32:54 925

原创 java同步阻塞队列之SynchronousQueue

SynchronousQueue是一个比较独特的队列,其本身是没有容量的,比如我放一个元素到队列中去,不能立马返回,必须要等有人消费了这个元素之后才能返回。SynchronousQueue底层,提供了两种数据结构,队列和栈,实现了公平调度和非公平调度。其内部有一个接口Transferer是其实现的基础: abstract static class Transferer<E> { abstract E transfer(E e, boolean timed, long nan

2022-01-25 11:28:32 1274

原创 java同步阻塞队列之DelayQueue实现原理,PriorityQueue原理

DelayQueue是一种延迟队列,能够在指定的时间之后执行。其底层采用PriorityQueue作为底层数据结构。在讲解DelayQueue之前,我们需要先讲解一下PriorityQueue。PriorityQueue是一个优先级队列,在底层使用了一个可动态扩容的数组作为基础数据结构,实现了堆结构,默认是一个小顶堆。public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Ser

2022-01-24 16:23:23 517

原创 java同步阻塞队列之LinkedBlockingQueue实现原理,和ArrayBlockingQueue对比

上一篇我们说到ArrayBlockingQueue,底层是数组加锁机制实现同步阻塞队列,这里我们说下另外一个同步阻塞队列LinkedBlockingQueue.从名字上就可以看出LinkedBlockingQueue底层数据结构是基于链表结构的。我们看下其几个关键属性: private final int capacity; private final AtomicInteger count = new AtomicInteger();transient Node<E> head;

2022-01-24 14:53:55 2829

原创 java同步阻塞队列之ArrayBlockingQueue实现原理

在java中我们可以用同步阻塞队列实现生产者-消费者模型。ArrayBlockingQueue提供了阻塞队列功能,底层数据结构是基于数组,提供如下几个关键方法:public boolean add(E e) 向队列中添加元素底层调用的是offer方法,如果添加成功返回true,否则抛出IllegalStateException异常public boolean offer(E e),向队列中添加元素,如果队列 已经满了,返回false,否则添加元素public void put(E e) throw

2022-01-21 17:01:41 902

原创 java并发之ReentrantLock.Condition实现原理

一般我们在实际开发过程中经常会遇到一种情况需要在满足一个条件才能继续,比如我们使用生产-消费模型的时候,消费服务必须有数据消费,如果没数据则等待,当生产线程产生数据的时候,唤醒消费线程。示例代码如下:ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(()-> { try

2022-01-21 15:42:45 748

原创 数据结构之跳跃表

数据结构之跳跃表

2022-01-17 22:16:00 499

原创 java中HashMap和ConcurrentHashMap实现原理,LinkedHashMap和TreeMap有序

在java中,HashMap底层是通过 数组 + 链表实现的,如果当某个链表元素个数超过8个,则链表转化为红黑树。其底层类似上述结构,一层采用数组结构,每个数组里面都是一个链表,当我们进行put的时候,计算key的hash值,和数组的长度-1取余得到待添加元素在数组中的位置,然后看该位置是否有元素,如果没有元素,那么作为链表的头加入,如果有,加入到已有的链表中,如果链表的元素超过8个,这时候将链表转换为红黑树。 transient Node<K,V>[] table;static fi

2022-01-13 17:24:46 817 1

原创 数据结构之LSM树

LSM树(Log Structured Merge Tree) 是一种

2022-01-11 22:05:47 1141

原创 java中ReentrantLock实现,公平锁和非公平锁,AQS并发队列,

一般在java中,遇到并发的时候,我们很多时候可能会使用synchronized关键字来实现锁,但是synchronized关键字有一定的缺陷(比如无法实现类似读锁、非公平),而Lock可以实现。在java中常用的有ReentrantLock,我们看下实现,一般我们在代码中如下方式来调用锁: ReentrantLock lock = new ReentrantLock();lock.lock();xxxxxx lock.unlock();这里默认的是非公平锁,可以在构造ReentrantLock

2022-01-11 17:17:37 836

原创 Flink中实现自定义ProcessFunction实现定时器、侧输出

在Flink中,当我们需要获取到算子的Processing Time或者Water Mark以及定时器时,可以实现ProcessFunction函数。目前该函数主要有:K恶业的ProcessFuntion,ProcessFunction,CoPropcessFunction等,核心功能主要如下:可以使用状态计算,能够在算子中访问Keyed State可以设置定时器侧输出,可以将一部分数据发送到另外一个数据流中,而且输出的两个数据流数据类型可以不一样。如下自定义实现一个KeyedProcessF

2022-01-09 22:45:55 1687

原创 Flink中自定义Rich函数实现

在Flink中,我们知道map ,flatMap,reduce算子都可以自定义函数实现,比如MapFunction:public class MyMapFunction implements MapFunction<String,Integer> { @Override public Integer map(String s) throws Exception { return Integer.parseInt(s); }}同时,Flink中还提供

2022-01-09 22:27:58 1619

原创 Flink中window 窗口和时间以及watermark水印

我们都知道,Flink的核心是流式处理,但同时也支持批处理,Flink底层是一个流式引擎,在这个上面实现了流处理和批处理,而窗口则是批处理的实现。在Flink中window从大的分类上主要有三种:Time Window(根据时间)、Count Window(根据数据量)、Session Window(会话窗口)窗口类型有如下两种:Tumbling Window 滚动窗口,窗口之间的数据没有重叠Sliding Window 滑动窗口,窗口之间的数据有可能重叠Count WindowCount

2022-01-08 22:51:11 1593 1

原创 scala函数,柯里化函数

scala中函数没有重载的概念,函数名称唯一,不区分参数,scala是完全面向对象,也是完全面向函数根据函数的最后一行推断返回类型:def test():String={print(1111)"Hello world"}这时候可以省略返回类型如果函数体只有一行代码,大括号可以省略def test():String = "Hello word "如果函数的声明没有参数列表,小括号可以省略:def test = "Hello World"但是这样访问的时候不能增加小括号,只能用

2022-01-03 17:27:59 262

原创 scala中泛型,协变和逆变

<:表示泛型的上限,传递数据应该是当前类,或者当前类的子类>:表示泛型的下限,和java不太一样,什么类型都可以传递scala中为了丰富泛型的功能,提供了协变(+)和逆变(-)的功能Scala的协变(+),逆变(-),协变covariant、逆变contravariant、不可变invariant对于一个带类型参数的类型,比如 C[T],如果对A及其子类型B,满足C[B]也符合 C[A]的子类型,那么就称为covariance(协变) ,如果 C[A]是 C[B]的子类型,即与原来的父

2022-01-03 16:46:50 429

原创 scala中隐式转换

scala中默认的情况下支持数值类型的自动转换byte->short->int->longscala默认情况下支语法中的类型的自动转换child -> parent -> trait(interfact)scala中也允许开发人员自定义类型转换规则,将两个无关的类型通过编程手段让他们可以自动转换隐式转换注意事项隐式转换的函数名可以是任意的,与函数名无关,与函数签名有关隐式函数可以有多个,但是需要保证当前环境下只有一个隐式转换函数能够被识别隐式值隐式值也叫隐

2022-01-03 16:39:04 556

原创 scala中下划线

替换java等价语法导入通配符//Javaimport java.util.*;//Scalaimport java.util._类成员默认值Java中类成员可以不赋初始值,编译器会自动帮你设置一个合适的初始值:class Foo{ //String类型的默认值为null String s;}而在Scala中必须要显式指定,如果你比较懒,可以用_让编译器自动帮你设置初始值:class Foo{ //String类型的默认值为null va

2022-01-03 16:11:50 173

原创 scala中case class与一般的class的区别

(1)case class初始化的时候可以不用new,也可以加上,但是class必须加new(2)默认实现了equals、hashCode方法(3)默认是可以序列化的,实现了Serializalbe(4)自动从scala.Product中继承一些函数(5)case class构造函数参数是public的,可以直接访问(6)case class默认情况下不能修改属性值(7)case class最重要的功能,支持模式配置,这也是定义case class的重要原因case class和case ob

2022-01-03 16:07:08 1359

原创 设计模式研究,java 23种设计模式

众所周知,我们在实际编写代码时,为了更好的组织代码,都会在代码中采用的一定的设计模式。一般我们在代码的设计中会遵循如下几大原则:开闭原则(OCP open closed Proiciple),一个类应该对扩展开放,对修改关闭单一职责原则(SRP single responsibility prociple)依赖倒置原则(DIP depedence inversion principle),实际上就是要依赖于抽象,高层模块不应该依赖于底层模块,二者应该依赖于抽象;抽象不应该依赖于具体实现,具体实现应该

2021-12-29 16:40:33 785

原创 简单聊聊架构设计

这个章节简单聊聊架构设计。很多技术人员,比较热衷技术,认为技术学好了,架构设计就能手到擒来,但是我的一个观点是,脱离业务的技术都是耍流氓,不是说技术不重要,而是技术必须是服务业务的。架构,可以说是系统的蓝图,是对系统高层次的定义和描述,在一些复杂情况下,架构可以分为面向业务的业务架构和面向计算机(系统)的软件架构。业务架构主要是从业务方面描述软件系统,定义了系统能够实现的业务。在业务架构中,动态的内容包括业务流程、节点、输入输出,静态的内容包括业务域、业务模块、单据模型等。软件架构有两个主要的概念流

2021-12-27 22:12:34 654

原创 UML动态视图,活动图、状态图、时序图、协作图

UML中提供了四种动态视图:活动图、状态图、时序图、协作图活动图:描述为了一个目标需要做的活动以及活动的执行顺序,需要注意的是,活动图描述的实际上业务流程,属于面向过程的分析方法,可能会导致类的职责混乱,但是面向对象中对象越独立、封装的越好,越是难以了解对象将会干什么,而活动图解决了业务目标过程化描述,需要谨记的是活动图只是我们用来描述业务目标的完成过程并借此发现对象的工具,并不是分析目标,也不是编程的依据。我们使用活动图来描述用例场景,帮助确认问题领域,从问题领域中发现关键对象,然后就应该不关系活动图而

2021-12-26 15:16:23 4652

原创 UML用例建模,业务用例建模、概念用例建模、系统用例建模,领域建模

在面向对象软件开发的过程中,针对复杂系统,我们一般会先进行相关建模来了解现实世界问题,通过抽象方法,建立模型来表征现实世界,获得对现实事物本身的理解,然后将这些理解到的知识概念化,并将这些逻辑概念组织起来,形成对所观察事务的内部结构和工作原理便于理解的表达。在UML中通过用例驱动的方式来一步一步获取对现实世界的理解。一般我们通过如下三个用例建模步骤来获取对现实世界问题的认知,然后将其转化为计算机世界的实现,主要有如下三个步骤:业务用例建模概念用例建模系统用例建模业务用例建模业务用例建模早于需

2021-12-25 22:50:50 7732 1

原创 UML 用例、类图、对象图、包图、鲁棒图(分析类),UML关系

用例图用例图描述了一组用例、参与者以及他们之间的关系,是从用户的角度而不是开发者的角度来描述对软件产品的需求,分析产品所需的功能和动态行为,通过用例图能够知道系统将会做什么,用例图一般用来对需求建模。用例图包含了三个内容:用例(Use Case) ;参与者(Actor)参与者、用例之间的关系,泛华、包含、扩展等关系参与者图形表示如下:用例表示如下:几种关系如下:泛化,多个参与者之间的公共行为,与类的泛化关系相同,可以理解为父类和子类的关系包含,一个用例(称为基础用例)的行为包

2021-12-22 22:16:35 3972

原创 Hystrix和Sentinel对比,如何选择

我们知道,在目前微服务中,众多的微服务调用关系错综负责,为了维护系统的稳定,引入了限流、降级、熔断等概念,这其中比较出名的是Hystrix和Sentinel,来聊聊这二者的异同。

2021-12-20 22:19:06 11106

原创 java中Class<T> 和Class<?>

在java中类型Class是一个很重要的概念,用来表示一个类对应的具体类型,T bean,;Class clsT;Class<?> clasGeneric;上面T表示的是一个具体的类型,而Class 和Class<?> 则代表这个类型对应的类Class<?>这是一个类型通配泛型,表示可以表示任何类,如果要加上限定可以通过如下方式:Class<? extends T> 表示是T的一个未知子类型Class<? super T> 表示

2021-12-20 14:44:13 3040

原创 Hystrix原理和配置说明

在微服务场景下,服务间调用链路加长,有时候一个部分出现问题可能会引起整个链路的崩溃,为此微服务中提出了如下两个概念:服务熔断服务降级服务熔断熔断来源于电路中断路器的概念,服务熔断指的是 当下游服务因为某种原因不可用或响应慢时,上游服务为了保证自己整体服务的可用性,不在调用下游服务而是直接返回,快速释放资源,直到下游服务恢复才继续调用服务降级当服务器压力剧增的时候,根据当前业务情况以及流量,对一些服务和页面有策略的降级,以此缓解服务器资源的压力以保障核心任务的正常运行,同时也保证了大部分客户能

2021-12-19 22:37:21 717

原创 Sentinel dashboard持久化,持久化到nacos和数据库中(基于sentinel 1.8.2)

我们知道,Sentinel dashboard默认是是没有持久化功能的,都是保存在内存中的,对于sentinel客户端同样如此,当在sentinel dashboard配置规则的时候,dashboard会获取对应应用配置的dashboard给应用传递消息的http,将规则通过HTTP请求发送给sentinel客户端,同样,sentinel客户端也是没有持久化的都是放在内存中的。sentinel dashbord通过HTTP向sentinel客户端获取客户端的限流配置,而在dashboard更新配置之后则通

2021-12-14 22:16:04 3002 3

原创 sentinel中blockHandler、blockHandlerClass、fallback、defaultFallback、fallbackClas实现原理、配置说明

我们知道现在项目一般都会使用springboot作为基础框架,如果项目需要基于springboot实现自动导入的话,一般都会实现一个xxx-starter,我们看看spring-cloud-starter-alibaba-sentinel是怎么实现的。本篇有些内容需要基于之前一篇的分析Sentinel限流原理(基于Sentinel1.8.1),限流、熔断、热点参数限流、授权实现原理springboot自动装配原理,底层源码分析,条件注解实现机制,EnableAutoConfiguration,AutoC

2021-12-11 14:45:58 2676

原创 Sentinel限流原理(基于Sentinel1.8.1),限流、熔断、热点参数限流、授权实现原理

在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 API 显式创建;每一个 Entry 创建的时候,同时也会创建一系列功能插槽(slot chain)。这些插槽有不同的职责,例如:NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,例如该资源的 RT,

2021-12-09 23:18:46 5958

原创 springboot sentinel使用示例(基于sentinel 1.8),流控,降级,sentinel-dashboard使用,blockHandler和fallback

废话不多说,直接上代码,首先看pom依赖:<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.2.7.RELEA

2021-12-06 14:35:24 2365 1

原创 Nacos中服务注册中心AP、CP模式实现,AP、CP模式切换

本文分析Nacos基于Nacos 2.0Nacos中服务注册中心默认是AP模式,AP模式和CP模式切换可以通过请求如下接口调整curl -X PUT 'ip:port/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'同时客户端设置 spring.cloud.nacos.discovery.ephemeral=false (默认为true) ,表示是启用AP模式接下来我们看看Nacos中对于AP、CP模式是怎么实现的。首先说明

2021-12-04 16:55:46 16501 5

原创 Raft算法实现 - Sofa-JRaft,选主,数据写入,日志复制

关于raft算法相关细节,可以全看之前的文章 分布式一致性算法,两阶段提交,三阶段提交,Paxos,Raft,zookeeper的选主过程,zab协议,顺序一致性,数据写入流程,节点状态,节点的角色这里我们说下阿里开源的sofa-jraft的实现。首先说明下,在sofa-jraft有几个比较重要的角色Node 代表的就是一个服务节点Ballot 代表的是一次投票的相关信息PeerId 代表的是一个复制组里面的一个参与角色JRaft的投票有两个步骤preVote和vote,之所以要增加一个pr

2021-12-02 11:16:20 3449

原创 高性能队列Disruptor使用入门,原理和代码实现

网上有不少关于Disruptor的文章,这里聊聊自己的看法。我总结Disruptor高性能的实现在于如下几点:缓存行填充对齐无锁CAS操作异步并发消费1. 缓存行填充对齐一般我们知道在我们程序运行的时候,内存访问都是比较快速的,但是CPU并不和内存打交道,而是和寄存器,当CPU要读取数据时,从内存中将数据加载到寄存器中,CPU的每个核心都有一个专属的寄存器,但是寄存器受限于成本,一般容量比较小,后来人们又在寄存器和内存中增加了所谓的L1,L2,L3三级缓存,当CPU要访问一个数据的时候,首先

2021-11-29 21:59:41 1118

原创 Nacos基于Spring cloud客户端配置动态刷新,配置文件命名逻辑

我们知道Nacos可以作为配置中心来使用,并且Nacos官方给我们提供了基于spring cloud的自动导入模块,可以在项目中比较方便的引入,另外Nacos的spring cloud客户端配置可以动态刷新,接下来我们看下spring cloud nacos客户端怎么实现的,一般我们在spring cloud项目中使用Nacos的配置中心功能时会引入如下pom:<dependency> <groupId>com.alibaba.cloud</

2021-11-24 13:39:59 975

原创 MySQL中join连接,内连接,外连接,连接算法,优化

MySQL中连接大致分为两类:内连接:驱动表的记录在被驱动表中找不到匹配的记录,该记录不会加入到最后的结果集(inner join)外连接:驱动表的记录即使在被驱动表中找不到匹配的记录,也会加入到结果集(left [outer] join、right [outer] join)外连接又分为做外链接和右外连接:左连接:选取左侧的表为驱动表右连接:选择右侧的表为驱动表在MySQL中where条件不管内连接还是外连接都会过滤,对于on条件,内连接时on的作用和where条件是等价的,外连接的时

2021-11-23 19:11:47 1027

原创 Dubbo中Filter过滤器,拦截器的实现原理,实现自定义的Filter过滤器

我们知道Dubbo中大部分的实现类加载都是通过SPI实现,同样Dubbo也提供了Filter机制,这个部分研究下怎么实现了,是怎样的一个调用逻辑。首先我们看下Dubbo中Filter的定义: * Filter Chain in 3.x * * -> Filter -> Invoker * * Proxy -> ClusterFilter -> ClusterInvoker -> Fil

2021-11-21 22:45:36 4702

conntrack-tools_offline_dependency.tar

conntrack-tools离线安装包和其依赖

2021-08-14

kubeadm-init-list.zip

kudeadm init需要的依赖镜像包

2021-08-10

conntrack-tools离线宝及其依赖.zip

conntrack-tools离线宝及其依赖

2021-08-09

elrepo-release-6-12.el6.elrepo.noarch.rpm

centos6内核升级,elrepo原,docker安装centos6升级centos7

2021-03-23

keepalived-2.0.19.tar.gz

keepalived安装包,keepalived-2.0.19.tar.gz,省的去官网下载,太慢,可以试试这个,

2020-10-14

mysql-5.6.46-leo.tar

mysql-5.6.46安装包,不用安装,直接部署配置即可,省去了编译的步骤mysql-5.6.46安装包,不用安装,直接部署配置即可,省去了编译的步骤

2020-07-13

postgresql-9.6.9.tar.gz

postgresql9.6安装包

2020-07-09

gcc-4.9.4.包含依赖tar.gz

gcc 4.9.4安装包,包含了 mpc,mpfr,gmp依赖,直接编译就行,不用下载依赖,redis6需要gcc>=4.9

2020-05-14

gcc-4.8.2.tar.gz包含依赖

gcc满足c11,unrecognized command line option "-std=c11",更新gcc版本到新版本,包含了需要的依赖不需要再去下载依赖

2020-05-12

Posgresql主从流复制相关配置

Postgresql9.6基于主从流复制的配置,包含主节点: postgresql.conf, pg_hba.conf,从节点: postgresql.conf,pg_hba.conf,recovery.conf

2019-03-14

tomcat8开启apr需要相关的组件,包含jdk,apr,apr,util,tomcat8,openssl

tomcat8开启apr需要相关的组件,包含jdk,apr,apr,util,tomcat8,openssl-1.0.2o1.2里面的都在,想要Linux下的jdk都在里面

2018-08-16

人工智能的未来

人工智能,深度学习的介绍和相关的未来趋势人工智能,深度学习的介绍和相关的未来趋势人工智能,深度学习的介绍和相关的未来趋势

2018-08-15

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除