拉巴力的纸皮箱


  • 首页

  • 标签

  • 归档

  • 关于

  • 搜索

直播相关技术梳理总结

发表于 2022-05-04

视频直播的实现原理

  • 网络协议-流媒体协议
  • 首先,网络协议将编码好的视频流,从主播端推送到服务器,在服务器上有个运行了同样协议的服务端来接收这些网络包,从而得到里面的视频流,这个过程称为接流。
  • 服务端接到视频流之后,可以对视频流进行一定的处理,例如转码,也即从一个编码格式,转成另一种格式。因为观众使用的客户端千差万别,要保证他们都能看到直播。流处理完毕之后,就可以等待观众的客户端来请求这些视频流,观众的客户端请求的过程称为拉流。
  • 如果有非常多的观众,同时看一个视频直播,那都从一个服务器上拉流,压力太大了,因而需要一个视频的分发网络(CDN),将视频预先加载到就近的边缘节点,这样大部分观众看的视频,是从边缘节点拉取的,就能降低服务器的压力。
  • 当观众的客户端将视频流拉下来之后,就需要进行解码,也即通过上述过程的逆过程,将一串串看不懂的二进制代码,再转变成一帧帧生动的图片,在客户端播放出来,这样你就能看到直播视频啦。

基本概念

  • https://cloud.tencent.com/document/product/267/43393

转码

  • 转码是将视频码流转换成另一个视频码流的过程,是一种离线任务。通过转码,可以改变原始码流的编码格式、分辨率和码率等参数,从而适应不同终端和网络环境的播放。使用转码功能可以实现:
    • 适配更多终端:将原始视频转码成拥有更强终端适配能力的格式,使视频资源能够在更多设备上播放。
    • 适配不同带宽:将视频转换成流畅、标清、高清或超清输出,用户可根据当前网络环境选择合适码率的视频播放。
    • 节省带宽:采用更先进的编码方式转码,在不损失原始画质的情况下显著降低码率,节省播放带宽。

视频编码

  • H.265是新的编码协议,也即是H.264的升级版。H.265标准保留H.264原来的某些技术,同时对一些相关的技术加以改进。
  • 视频编码:指通过特定的压缩技术,将某个视频格式的文件转换成另一种视频格式文件的方式。常见的音频视频编码有MPEG系列与H.26X系列。

容器

  • https://blog.csdn.net/lxc1014/article/details/45666281
  • mp4,rmvb,mkv,avi从形式上来说首先都是视频文件的扩展名,其次它们也是视频文件的封装格式(即容器), mp4是MPEG-4标准的第14部分所制定的容器标准。所谓容器,就是把编码器生成的多媒体内容(视频,音频,字幕,章节信息等)混合封装在一起的标准。容器使得不同多媒体内容同步播放变得很简单,而容器的另一个作用就是为多媒体内容提供索引,也就是说如果没有容器存在的话一部影片你只能从一开始看到最后,不能拖动进度条(当然这种情况下有的播放器会话比较长的时间临时创建索引),而且如果你不自己去手动另外载入音频就没有声音。

普通直播

  • 直播类应用在产品功能上基本是这两类:
    • 1、直播的基础功能:连麦互动直播(支持多码率、多协议)等非功能性需求。— 流相关服务
    • 2、应用本身的个性化功能:比如答题场景中的发题目、作答、公布答案等。— 长连接相关服务

多人连麦/PK/聊天

  • 直播多人连麦技术简介
  • 直播连麦技术对比-互动直播调研必看
    • 基于WebRTC和RTMP结合的方案
    • 基于RTC的方案(声网、即构)
    • anyRTC直播方案
  • 直播连麦技术方案对比及测试方法
    • SD-RTN™:这是声网的连麦架构,直播连麦的鼻祖。基于UDP,主播端、连麦端、观众端都在基于SD-RTN™进行实时通信,大大降低延时。主播端和连麦端也可以转码到CDN推流。
    • RTMP改进方案:基于TCP协议,基本思路是:主播接受连麦嘉宾的视频,在本地合图;主播和连麦嘉宾的视频同时传到服务端合图,然后通过CDN推到观众端。
    • WebRTC改进:主播和连麦嘉宾,基于WebRTC进行“视频会议”,将“视频会议”在服务端合图后推到CDN进行分发。

传统直播形式

  • 一个主播推流,广播给直播间所有的观众。一个直播间对应一个主播,并且仅有一路推拉流。
  • 基于协议RTMP做的单路直播互动。该模式下主播一个人表演,其他观众根据IM系统跟主播进行文字互动。
  • 总结:主播端推一路流,观众端CDN拉一路流。

连麦直播形式

  • 两个主播(另一个主播可能是观众)推流,广播给直播间所有观众。
  • 基于UDP做的多路实时互动直播。该模式下主播跟观众除了基于IM系统沟通外,还可以进行音视频互动,极大的方便了观众,互动效果更直观,更能有效吸引用户。

1基于RTMP协议优化方案

  • 主播A和主播B之间通过原有的推拉流路径去拉取对方的流内容。也就是说,主播A在推流同时,拉取主播B的流,主播B推流的同时拉取主播A的流。对于两个主播来说互为对方的观众。此时对于直播间内的其他观众而言,是分别拉取主播A和主播B的流,并展示出来。
  • 在此基础上可以进一步对主播两路流做混流,这样观众端只需要拉一路流。
  • 混流:可以在服务端做,也可以在其中一个主播的客户端做混流再推流(对主播设备性能有一定要求)

2基于P2P协议方案

  • 此方案的实现方式是,主播A和主播B之间通过P2P协议进行音视频连接,正常情况下能够保证较低的延迟,保证主播A和主播B之间的互动。主播A在自己的流内容基础上加入主播B的流内容,统一推向服务端。此时直播间内仅有一路流,并且其他观众也只需要拉取这一路流内容。
  • 此方案的优点是显而易见的,主播AB之间的延迟降低,交互体验好,观众保持原有逻辑不变,拉取直播间固定流地址。但是缺点是:主播A在连麦过程中需要承担两路推流一路拉流的压力,即拉取主播B的流内容,将自己的流内容推给主播B,将主播A和主播B的流内容推给服务端;主播A的网速压力和性能压力将会巨大,同时主播AB之间一对一的连接也导致扩展性较差,无法满足2人以上的业务场景需求。

3基于多人视频通话系统方案 (目前主流)

  • 此方案的实现方式是将主播A和主播B的视频交互交由第三方处理,目前比较成熟的技术有视频会议系统和Google开源的WebRTC系统。在此架构下,主播A与主播B的流合成处理上传都是由这个交互系统完成。此方案对于方案2来说减轻了主播端的压力,并且采用UDP协议传输方式降低延迟。同时也兼容多人连接交互。
  • 此方案缺点是对服务端开发量大,要求高。

第三方支持

  • 语音连麦聊天室集成介绍:http://docs-im.easemob.com/rtc/scenario/tc
  • 腾讯音视频技术平台:https://cloud.tencent.com/document/product/267/44710

推流

  • 使用ffmpeg推流到rtmp服务

    • ffmpeg -re -i 1.mp4 -c copy -f flv rtmp://127.0.0.1:1935/live/movie
    • 如何开发一款H5小程序直播
  • OBS 推流:https://cloud.tencent.com/document/product/267/32726

  • VLC播放器:https://www.videolan.org/vlc/index.zh.html,
    https://cloud.tencent.com/document/product/267/32727

混流

  • 混流就是把多路音视频流合成单流。准确的说,混流应该叫做混音(音频流)混画(视频流)混流的过程包括解码、混流、编码和推流四个部分。混流这个环节包括做动冲,目的是把多路流进行画面对齐和音画同步,同时通过缓冲对抗网络抖动,以便混合成一路流以后能够达到良好的效果。在混流的过程中,难点是如何对抗网络抖动等不确定因素。

不混流的优势和劣势

  • 不混流的优势

    • 延迟低:不用混流,节省了混流消耗的时间,显著地降低了延迟
    • 成本低: 如果是在服务端进行混流,将会耗费计算资源。考虑到服务端计算资源比较昂贵,如果不用混流,将会节省宝贵的计算资源,显著地降低成本。虽然拉多流比起拉单流会消耗更多的带宽成本,但是拉多流节省计算资源成本,整体而言,成本是降低了。
    • 灵活性:在观众端,业务侧可以比较灵活的操控多路流,来满足多样化的业务需求。比如画中画大小画面相互切换,和对半分屏画面左右调换等效果,来提高观众端的用户体验。
  • 不混流的劣势

    • 拉多流会消耗更多的带宽。多路流被从服务端推到CDN, 然后观众端从CDN拉多流,都会耗费比较多的带宽成本。对于带宽成本占了运营成本显著,的确是需要慎重考量的。
  • 混流的优势

    • 成本:可以分为计算资源成本和带宽成本。由于预先做混流,因此计算资源成本会上去,但是由于只拉单流,因此带宽成本会下来。
    • 可录制:如果业务上有录制音视频流的需求,以备监管抽查或者观众回放的话,那么需要进行混流。如果不进行混流的话,录制的时候只能录制到其中一个路音视频流,也就是只能看到其中一个主播的画面。要录制全画面的话,必须要进行混流。
    • 易传播:如果业务上有通过音视频流地址链接(HLS)进行转发传播的需求,那么也需要进行混流,因为地址链接只会指向一路音视频流。如果不混流,使用转发的地址链接就只会播放出一个主播的音视频流。
  • 混流的劣势

    • 高延迟:由于在做混流的过程中,需要做抖动缓冲和实时转码等计算处理,将会耗费时间,从而造成额外的延迟。
    • 不灵活:由于观众端拉单流观看,多路音视频流已经被混合成单流,所以观众端无法再灵活地对多流进行操控,比如切换画中画的主次画面。
    • 服务器计算成本高:由于混流需要额外的计算资源,这里会导致额外的运营成本。
  • 服务端混流的优势

    • 低延迟
    • 计算资源可控
    • 网络带宽资源可控
    • 可控可扩展
  • 服务端混流的劣势

    • 服务器计算成本高
    • 服务端压力大
  • 混流工具: FFmpeg

    • 混流命令: ./ffmpeg -i “背景图” -i “rtmp://输入流1” -i “rtmp://输入流2” -filter_complex “nullsrc=size=1600x720 [base];[0:v] scale=1600x720 [main]; [1:v] crop=320:180:0:0 [photo1];[2:v] crop=320:180:0:0 [photo2];[base][main] overlay=x=0:y=0 [temp];[temp][photo1] overlay=x=1280:y=0 [temp1];[temp1][photo2] overlay=x=1280:y=180 [temp2]” -c:v libx264 -r 50 -bufsize 10M -f flv “rtmp://推流地址”
    • 录制命令: ./ffmpeg -i rtmp://混流地址 test.mp4
  • 基于FFmpeg混流及录制rtmp直播流

混音

  • 实时音频的混音在视频直播中的技术原理和实践总结
  • 混音,顾名思义,就是把两路或者多路音频流混合在一起,形成一路音频流。实时音频混音,指的只是音频流的混合。
  • 混音的逻辑可以在终端设备上实现,也可以在服务器上实现,因此可以分为终端混音和云端混音。终端混音一般应用于背景配音,音乐伴奏等场景。云端混音可以是云端混流的一部分,主要目的是利用云端的计算能力去做多路音视频流的音画对齐,还有降低下行带宽成本;也可以做纯粹的云端混音,来实现合唱直播等场景的需求。

实现例子1

  • 火爆的直播应用,你了解背后的技术架构吗?:https://mp.weixin.qq.com/s/2ucFww9MEdsKbMO8Wepg1A
  • 音视频流采用了腾讯云的直播解决方案,而业务数据流(活动、题目、答案、弹幕、红包等)则采用了自研的长连接方案。

产品功能

  • 直播类应用在产品功能上基本是这两类:
    • 1、直播的基础功能:连麦互动直播(支持多码率、多协议,多主播同框)、美颜特效、弹幕、IM聊天、点赞、屏幕共享等功能性需求,以及防盗链、涉黄涉政鉴别等非功能性需求。
    • 2、应用本身的个性化功能:比如答题场景中的发题目、作答、公布答案,电商场景中的商品展示、一键下单购买,网红直播场景中的礼物打赏。

面临的技术挑战

  • 音视频处理及传输:涉及音视频编码、实时美颜、视频推流、CDN加速分发、终端适配和播放,流量统计等诸多技术点
  • 高带宽压力:按照标清视频的标准,观看直播的码流至少为1Mbps,如果100W用户在线,光视频流的出口带宽能达到976.56G bps。1条弹幕可达到130字节,1秒要滚屏20条弹幕,如果需要同时推送给100W用户,弹幕的出口带宽也将达到19.37G bps.
  • 低延迟性要求:直播场景下如何整合视频流和业务数据流,做到声音、主播画面和题目同步,以保证用户体验

音视频处理及传输的方案选型

  • 第三方支持方案:音视频处理及传输,可以使用腾讯云的直播解决方案。主持人侧:通过演播室的专业摄像设备,搭载腾讯云提供的obs推流软件,即可进行视频录制和推流。用户侧:APP端集成腾讯云的SDK,动态拿到推流地址后即可观看直播。

业务数据流的方案选型

  • 业务数据是指除音视频以外的,和答题应用场景相关的数据(比如题目、答案、弹幕、红包等)。腾讯云提供了两种可选方案:
    • 1、题目预先设置好,直接由腾讯云的SDK通过音视频通道下发,打入直播流中。
    • 2、让题目先通过腾讯云的IM通道快速送达观众端APP,在观众端先缓存下来,等待播放器通知了预期的 NTP 时间戳之后,再把题目显示出来。
  • 自研业务数据流通道。这样视频流和业务数据流会分两个通道下发,因为业务流相对视频流的数据量很小,只要能保证业务逻辑的处理速度和业务数据的下行速度,“音-画-题”的延迟是可以接受的。毕竟当时已经是4G时代,如果用户侧的网速不行,视频流可能都无法正常观看了。

长连接、高性能的网关服务器

  • (支持100W用户同时在线,20W并发答题,弹幕实时推送等要求),我们的技术选型是:Netty、ProtoBuf、WebSocket,选型理由:
  • 1、Netty:Netty是当时最流行的高性能和异步NIO框架,直播答题的业务场景中,涉及到题目下发、弹幕、下红包雨等非常多的推送场景,而且一场答题活动中,客户端和服务端的通信频繁,长连接比短连接在性能上更优。
  • 2、ProtoBuf:作为客户端和服务端的数据交换格式,PB是一种效率和兼容性都很优秀的二进制数据传输格式,在码流和序列化速度上明显优于JSON、XML、hessian等主流格式,同时支持向前向后兼容以及各种主流语言。
  • 3、WebSocket:是 HTML5 一种新的协议,用来实现客户端与服务器端的长连接通讯。
    • 为什么使用WebSocket不使用TCP呢?TODO

基于TCP长连接的通信架构

  • 上面的通信架构用于业务数据流的传输,流程如下:
    • 1、客户端使用websocket与服务端进行通讯,用户进入答题直播间时建立连接,退出直播间时断开连接。
    • 2、Nginx对websocket做负载均衡。
    • 3、TCP网关基于netty实现,用于维持长连接和转发业务请求,不负责具体的业务逻辑,它和下层业务系统(答题系统)通过RPC接口进行交互,主要考虑后续其他业务可以复用TCP网关层,所以将业务下沉。客户端和网关之间通过心跳机制保证连接的有效性以及检测僵尸连接。
    • 4、消息推送(比如弹幕、下发题目、公布答案等诸多场景)由下层业务(答题系统)通过MQ通知TCP网关,再由TCP网关推送给客户端。

长连接通信中的数据传输格式定义

  • 客户端请求消息的格式
  • 客户端响应消息的格式

直播架构简单总结:

  • 1、音视频编码和传输,这些基础性的直播功能,除非公司有钱有实力,否则建议直接用腾讯云或者阿里云的解决方案(斗鱼、蘑菇街这些知名的直播应用都还用的腾讯云)。
  • 2、架构设计重点放在应用本身,根据直播应用的用户量级和业务特性先确定通信架构(长连接还是短链接,或者两者混用)。

实现例子2

  • 视频相亲背后的音视频方案 (多人连麦)
  • https://cloud.tencent.com/developer/article/1579968
  • [互动直播相亲交友源码低成本开发搭建如何处理音视频技术难点](https://www.bilibili.com/read/cv7985514/ 出处:bilibili)
  1. 开发成本高、周期长
    • 实时音视频技术栈包含音视频编解码、音视频前后处理、信令、网络传输、高并发、高可用、系统监控、多个平台的终端开发,技术储备和开发成本是非常大的挑战。
  2. 弱网环境下的音视频质量
    • 现实的网络环境非常复杂、用户使用中小网络运营商的服务,存在着非常多的不确定性,或多或少的丢包、不确定的网络延时和抖动。
  3. 终端极致的性能要求
    • 多人同屏视频连麦的直播间,面对终端有限的算力、内存,实时音视频终端软件架构的设计会对通信的质量、时延都带来影响。
  • 一套互动直播相亲交友程序源码主播端到观众端有下面几个步骤:

    • 1、视音频信号实时采集;
    • 2、经过预处理和音视频编码;
    • 3、封装发送到CDN源站;
    • 4、播放端从CDN边缘拉到数据;
    • 5、然后进行解码;
    • 6、经过音视频同步之后;
    • 7、给观众展现出来。
  • 互动直播相亲交友系统的主要技术难点在于:

    • 1)低延迟互动:保证主播和互动观众之间能够实时互动,两者之间就像电话沟通,因此必须保证两者能在秒级以内听到对方的声音,看到对方的视频;
    • 2)音画同步:互动直播中对音画同步的需求和单向直播中类似,只不过互动直播中的延迟要求更高,必须保证在音视频秒级传输情况下的秒级同步。
    • 3)音视频实时合成:其他观众需要实时观看到对话结果,因此需要在客户端或者服务端将画面和声音实时合成,然后以低成本高品质的方式传输观众端。

实现实例3

  • 会议系统

  • https://www.juhe.cn/news/index/id/1568

  • 缺点:

    • 会议系统很大程度上依赖专线服务,成本过高;
    • 会议系统很多不能在服务器做转码合成视频,对主播设备依赖过大。因为合图视频主要是由主播端编码、推送到CDN,一部分需要主播设备比较高端,另外需要主播网络比较好,否则依然解决不了连麦问题
  • RTC技术(WebRTC)

扩展

  • 为何一直推荐WebRTC?

其他

  • 播放网络视频,通常解析库我们可以有多个选择,如FFMPEG,Daniulive SDK 或者 vitamio。
  • [H5 直播流播放器 Jessibuca]

Reference

  • 火爆的直播应用,你了解背后的技术架构吗?:https://mp.weixin.qq.com/s/2ucFww9MEdsKbMO8Wepg1A
  • 直播新红海,狼人杀火爆背后的语音视频技术:https://zhuanlan.zhihu.com/p/33092831
  • 实时视频通话超低延迟架构的思考与实践:https://blog.csdn.net/zego_0616/article/details/79651875

后端技术思维导图

发表于 2022-04-29

elasticsearch学习记录

发表于 2022-04-28

基于ES 7.10下的简要总结

一、官方文档链接

  • 文档主页:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/index.html
  • rest api: https://www.elastic.co/guide/en/elasticsearch/reference/7.10/rest-apis.html
  • Java rest high client: https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.10/java-rest-high.html

二、基础简要总结

1、REST Client

  • Java Low Level REST Client: 低级别的REST客户端,通过http与集群交互,用户需自己编组请求JSON串,及解析响应JSON串。兼容所有ES版本。

  • Java High Level REST Client: 高级别的REST客户端,基于低级别的REST客户端,增加了编组请求JSON串、解析响应JSON串等相关api。使用的版本需要保持和ES服务端的版本一致,否则会有版本问题。

  • ES 7.0版本中弃用TransportClient客户端,且在8.0版本中完全移除它

2、文档元数据

  • 一个文档不仅仅包含它的数据 ,也包含 元数据 —— 有关 文档的信息。 三个必须的元数据元素如下:
    • (1)_index
      文档在哪存放 (粗暴认为相当于Mysql的database)
    • (2)_type
      文档表示的对象类别 (粗暴认为相当于Mysql的table)(在ES7版本开始已经被打上废弃标识)
      -(3)_id
      文档唯一标识
    • (4)其他预定义字段(通常以_开头)
        - _timestamp 记录文档索引时间,_ttl 存活时间,  _source 原始JSON文档, _all 所有字段
        - _size等

相关链接

  • ES7为什么废弃type类型:https://blog.csdn.net/numbbe/article/details/109656567

3、索引的分片数

  • (1)一个分片可以是 主分片或者 副本分片。 索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。
    (索引包含多个主分片,主分片包含多个文档), 技术上来说,一般一个主分片最大能够存储 Integer.MAX_VALUE - 128 个文档(2147483647 / 二十一亿);
  • (2)一个副本分片只是一个主分片的拷贝。 副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。
    在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改;
  • (3)索引在默认情况下会被分配5个主分片,但是可以在创建时修改;
    1
    2
    3
    4
    5
    6
    7
    PUT /blogs
    {
    "settings" : {
    "number_of_shards" : 3,
    "number_of_replicas" : 1
    }
    }
  • 3个主分片和一份副本(每个主分片拥有一个副本分片)
  • 拥有6个分片(3个主分片和3个副本分片)的索引可以最大扩容到6个节点,每个节点上存在一个分片,并且每个分片拥有所在节点的全部资源。
  • 如果只是在相同节点数目的集群上增加更多的副本分片并不能提高性能,因为每个分片从节点上获得的资源会变少。 你需要增加更多的硬件资源来提升吞吐量。
    但是更多的副本分片数提高了数据冗余量 。

4、索引查询路由

  • 路由一个文档到一个分片中

  • Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?

    • shard = hash(routing) % number_of_primary_shards
  • routing 是一个可变值,默认是文档的 _id

    • 这就解释了为什么要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,
    • 那么所有之前路由的值都会无效,文档也再也找不到了。

5、分析器(Analyzer)和分词器(Tokenizer)

  • Elasticsearch这种全文搜索引擎,会用某种算法对建立的文档进行分析,从文档中提取出有效信息(Token)
  • 对于es来说,有内置的分析器(Analyzer)和分词器(Tokenizer)
  • Es中也支持非常多的分词器:
    • Standard 默认的分词器根据 Unicode 文本分割算法,以单词边界分割文本。它删除大多数标点符号。它是大多数语言的最佳选择。
    • Letter 遇到非字母时分割文本
    • Lowercase 类似 letter ,遇到非字母时分割文本,同时会将所有分割后的词元转为小写
    • Whitespace 遇到空白字符时分割位文本

6、文档更新并发控制

  • 更新文档:POST,可以发送部分文档进行更新; upsert (更新或新增文档);使用脚本来更新文档(不常用)
  • 通过版本来实现并发控制(可以取时间戳作为version参数)
  • 版本冲突时自动重试更新操作(retry_on_conflict 参数)
  • 更新文档其实是先删除旧的文档,再索引新的文档。

7、文档查询

  • 在 Elasticsearch 中, 每个字段的所有数据 都是 默认被索引的。 即每个字段都有为了快速检索设置的专用倒排索引
  • 返回文档的一部分: 单个字段能用 _source 参数请求得到,多个字段也能使用逗号分隔的列表来指定

8、搜索提示

  • Elasticsearch Suggester- Google在用户刚开始输入的时候是自动补全的,而当输入到一定长度,如果因为单词拼写错误无法补全,就开始尝试提示相似的词。
  • 那么类似的功能在Elasticsearch里如何实现呢? 答案就在Suggesters API。 Suggesters基本的运作原理是将输入的文本分解为token,然后在索引的字典里查找相似的term并返回。

总结

  • Term Suggester,基于编辑距离,对analyze过的单个term去提供建议,并不会考虑多个term/词组之间的关系。
  • Phrase Suggester,在Term Suggester的基础上,通过ngram以词组为单位返回建议。
  • Completion Suggester,FST数据结构,类似Trie树,并非使用倒排索引,只能前缀匹配,快速返回
  • Context Suggester,在Completion Suggester的基础上,用于filter和boost

相关链接

  • elasticsearch suggest: https://www.elastic.co/guide/en/elasticsearch/reference/7.10/search-suggesters.html
  • elasticsearch Term Suggester :https://www.cnblogs.com/Neeo/articles/10694969.html
  • elasticsearch Phrase Suggester: https://blog.csdn.net/UbuntuTouch/article/details/103952092
  • elasticsearch completion Suggester: completion :https://www.jianshu.com/p/8a6b80813a34

其他

mapping中的store属性

  • Elasticsearch 理解mapping中的store属性:https://www.cnblogs.com/sanduzxcvbnm/p/12157453.html

searchType

  • QUERY_THEN_FETCH,QUERY_AND_FEATCH,DFS_QUERY_THEN_FEATCH和DFS_QUERY_AND_FEATCH
  • 总结一下, 从性能考虑 QUERY_AND_FETCH 是最快的, DFS_QUERY_THEN_FETCH 是最慢的。从搜索的准确度来说, DFS 要比非 DFS 的准确度更高。

相关链接

  • https://www.cnblogs.com/ningskyer/articles/5984346.html
  • https://segmentfault.com/a/1190000015409044

三、DSL全文搜索

  • Elasticsearch Query DSL之全文检索(Full text queries) : https://blog.csdn.net/prestigeding/article/details/102295397

1、match query

  • 标准的全文检索模式,包含模糊匹配、前缀或近似匹配等。
    1
    2
    3
    4
    5
    "query": {
    "match" : {
    "message" : "this out Elasticsearch"
    }
    }
    1
    2
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    sourceBuilder.query(QueryBuilders.matchQuery("message", "this out elasticsearch"));

2. match_phrase query

  • 与match query类似,但只是用来精确匹配的短语。
  • 如果查询字符串为 quick fox,分词后的词根序列为 quick fox,与原词根序列不匹配。如果指定slop属性,设置为1,则匹配,其表示每一个词根直接跳过一个词根形成新的序列,与搜索词根进行比较,是否匹配。

3. match_phrase_prefix query

  • 与match_phrase查询类似,但是在最后一个单词上执行通配符搜索。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
        "query": {
            "match_phrase_prefix" : {
                "message" : {
                    "query" : "quick brown f",
                    "max_expansions" : 10
                }
            }
        }
    }
  • 默认查找50组,受参数max_expansions控制,在使用时请设置合理的max_expansions,该值越大,查询速度将会变的更慢。该技术主要完成及时搜索,指用户在输入过程中,就根据前缀返回查询结果,随着用户输入的字符越多,查询的结果越接近用户的需求。

4. multi_match query (多字段查询)

type 属性

  • 指定multi_query内部的执行方式,取值如下:best_fields、most_fields、cross_fields、phrase、phrase_prefix (5种)。

总结

  • best_fields 按照match检索,所有字段单独计算得分并取最高分的field为最终_score,虽然是默认值,但不建议使用,数据量上来后查询性能会下降

  • most_fields 按照match检索,融合所有field得分为最终_score

  • cross_fields 将fields中的所有字段融合成一个大字段进行match检索,此时要求所有字段使用相同分析器

  • phrase 按照match_phrase检索,默认slop为0,执行短语精确匹配,所以即便设置 minimum_should_match 也无效; 取最高字段得分

  • phrase_prefix 按照match_phrase_prefix检索,滑动步长slop默认为0;取最高字段得分

  • bool_prefix 按照match_bool_prefix检索

  • slop参数告诉match_phrase查询词条能够相隔多远时仍然将文档视为匹配。相隔多远的意思是,你需要移动一个词条多少次来让查询和文档匹配?
    我们以一个简单的例子来阐述这个概念。为了让查询quick fox能够匹配含有quick brown fox的文档,我们需要slop的值为1

1、best_fields

  • type默认值,只要其中一个字段匹配则匹配文档(match query)。但是使用最佳匹配的字段的score来表示文档的分数,会影响文档的排序。

2、most_fields

  • 查找匹配任何字段并结合每个字段的_score的文档,Elasticsearch会为每个字段生成一个match查询,然后将它们包含在一个bool查询中。其算法的核心是各个字段的评分相加作为文档的最终得分参与排序。

  • 其建议场景是不同字段对同一关键字的存储维度不一样,例如字段一可能包含同义词、词干、变音符等;字段二可能包含原始词根,这种情况下综合各个字段的评分就会显的更加具有相关性。

3、phrase、phrase_prefix

  • 这两种类型score的计算采用best_fields方法,但是其查询方式分别为match_phrase、match_phrase_prefix

4、cross_fields

  • 交叉字段,对于需要匹配多个字段的结构化文档,cross_fields类型特别有用。例如,在查询“Will Smith”的first_name和last_name字段时,在一个字段中可能会有“Will”,而在另一个字段中可能会有“Smith”。这听起来很象most_fields,cross_fields与most_fields的两个明显区别如下:
  • 对于opreator、minimum_should_match的作用域不一样,most_fields是针对字段的,(遍历每个字段,然后遍历查询词根列表,进行逐一匹配),而cross_fields是针对词根的,即遍历词根列表,搜索范围是所有字段。
  • 相关性的考量不相同,cross_fields重在这个交叉匹配,对于一组查询词根,一部分出现在其中一个字段,另外一部分出现在另外一个字段中,其相关性计算评分将更高。

tie_breaker属性

  • 默认情况下,每个词汇混合查询将使用组中任何字段返回的最佳分数,然后将这些分数相加,以给出最终分数。tie_breaker参数可以改变每项混合查询的默认行为。
  • tie_breaker可选值如下:
    • 0.0 : 默认行为,使用最佳字段的score。
    • 1.0 :所有匹配字段socre的和。
    • 0.0 ~ 1.0 : 使用最佳匹配字段的score + (其他匹配字段score) * tie_breaker。

most_fields vs cross_fields

  • most_fields: Finds documents which match any field and combines the _score from each field.

  • cross_fields: Treats fields with the same analyzer as though they were one big field. Looks for each word in any field.

  • Elasticsearch搜索之cross_fields分析: https://www.cnblogs.com/clonen/p/6674939.html

  • cross_fields类型采用了一种以词条为中心(Term-centric)的方法,这种方法和best_fields及most_fields采用的以字段为中心(Field-centric)的方法有很大的区别。

most_field vs best_field

  • https://www.cnblogs.com/lovezhr/p/14421872.html

  • best-fields策略,主要是说将某一个field匹配尽可能多的关键词的doc优先返回回来

  • most-fields策略,主要是说尽可能返回更多field匹配到某个关键词的doc,优先返回回来

  • 两者差异

    • (1)best_fields,是对多个field进行搜索,挑选某个field匹配度最高的那个分数,同时在多个query最高分相同的情况下,在一定程度上考虑其他query的分数。简单来说,你对多个field进行搜索,就想搜索到某一个field尽可能包含更多关键字的数据
      • 优点:通过best_fields策略,以及综合考虑其他field,还有minimum_should_match支持,可以尽可能精准地将匹配的结果推送到最前面
      • 缺点:除了那些精准匹配的结果,其他差不多大的结果,排序结果不是太均匀,没有什么区分度了
      • 实际的例子:百度之类的搜索引擎,最匹配的到最前面,但是其他的就没什么区分度了
    • (2)most_fields,综合多个field一起进行搜索,尽可能多地让所有fieldquery参与到总分数的计算中来,此时就会是个大杂烩,出现类似best_fields案例最开始的那个结果,结果不一定精准,某一个document的一个field包含更多的关键字,但是因为其他document有更多field匹配到了,所以排在了前面;所以需要建立类似sub_title.std这样的field,尽可能让某一个field精准匹配query string,贡献更高的分数,将更精准匹配的数据排到前面
      • 优点:将尽可能匹配更多field的结果推送到最前面,整个排序结果是比较均匀的
      • 缺点:可能那些精准匹配的结果,无法推送到最前面
      • 实际的例子:wiki,明显的most_fields策略,搜索结果比较均匀,但是的确要翻好几页才能找到最匹配的结果

相关链接

  • https://blog.csdn.net/prestigeding/article/details/102295397
  • https://www.jianshu.com/p/2a27b4985331

5. common terms query

  • 相比match query,消除停用词与高频词对相关度的影响。
  • 定位:排除停用词或高频词对文档的匹配影响。提高文档匹配的精确度,同时不对性能产生影响。

6. query_string query

  • 查询字符串方式。query_string查询解析器支持对查询字符串按照操作符进行切割,每个部分独立分析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    GET /_search
    {
        "query": {
            "query_string" : {
                "default_field" : "content",
                "query" : "(new york city) OR (big apple)"
            }
        }
    }
  • 多字段支持(multi field)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    GET /_search
    {
        "query": {
            "query_string" : {
                "fields" : ["content", "name"],
                "query" : "this AND that"
            }
        }
    }
  • 其含义类似于:“query”: “(content:this OR name:this) AND (content:that OR name:that)”。

  • 同时query_string(查询字符串)模式同样支持match_query等查询对应的参数,其工作机制一样

7. simple_query_string query

  • 简单查询字符串方式
  • 使用SimpleQueryParser解析上下文的查询。与常规的query_string查询不同,simple_query_string查询永远不会抛出异常,并丢弃查询的无效部分

其他

  • match_all 查询
  • match 查询
  • multi_match 查询
  • range 查询
  • term 查询
  • terms 查询
  • exists 查询
  • missing 查询
  • bool 查询
    • must
      文档 必须 匹配这些条件才能被包含进来。
    • must_not
      文档 必须不 匹配这些条件才能被包含进来。
    • should
      如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
    • filter
      必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
      如果没有 must 语句,那么至少需要能够匹配其中的一条 should 语句。但,如果存在至少一条 must 语句,则对 should 语句的匹配没有要求。

四、扩展

1、索引别名和零停机

  • 重建索引的问题是必须更新应用中的索引名称。 索引别名就是用来解决这个问题的。
  • 索引 别名 就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用。
  • 在应用中使用别名而不是索引名。然后就可以在任何时候重建索引。
  • 准备好数据才能切换。
  • 新增新字段不需要重建索引。

重新索引数据

  • 尽管可以增加新的类型到索引中,或者增加新的字段到类型中,但是不能添加新的分析器或者对现有的字段做改动。 如果你那么做的话,结果就是那些已经被索引的数据就不正确, 搜索也不能正常工作。
  • 对现有数据的这类改变最简单的办法就是重新索引:用新的设置创建新的索引并把文档从旧的索引复制到新的索引。
  • 为了有效的重新索引所有在旧的索引中的文档,用 scroll 从旧的索引检索批量文档 , 然后用 bulk API 把文档推送到新的索引中。
  • (从Elasticsearch v2.3.0开始, Reindex API 被引入。它能够对文档重建索引而不需要任何插件或外部工具。)

使用reindex api将旧索引数据导入新索引

  • https://www.elastic.co/guide/en/elasticsearch/reference/7.10/docs-reindex.html
  • reindex的底层是scroll实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    POST _reindex
    {
     "source": {
      "index": "song.20220425_3"
     },
     "dest": {
      "index": "song.20220425_4"
     }
    }

新索引同时在写数据,如何防止冲突?

  • 默认情况下,当发生 version conflict 的时候,_reindex 会被 abort,任务终止【此时数据还没有 reindex 完成】,在返回体中的 failures 指标中会包含冲突的数据【有时候数据会非常多】,除非把 conflicts 设置为 proceed。
  • (1)只创建目标索引中缺少的文档
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    POST _reindex
    {
    "source": {
    "index": "pigg_test"
    },
    "dest": {
    "index": "pigg_test2",
    "op_type": "create"
    }
    }
  • (2)版本高的才复制
    • external 等同 external_gt
    • https://www.elastic.co/guide/en/elasticsearch/reference/7.10/docs-index_.html#index-version-types
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      POST _reindex
      {
      "source": {
      "index": "twitter"
      },
      "dest": {
      "index": "new_twitter",
      "version_type": "external"
      }
      }

如何提升迁移效率?

  • 默认情况下,_reindex使用1000进行批量操作,您可以在source中调整batch_size。

如何查看执行进度?

  • 默认执行同步返回结果的。

异步执⾏

  • 如果 reindex 时间过⻓,建议加上 wait_for_completion=false 的参数条件,这样 reindex 将直接返回 taskId。
    POST /_reindex?wait_for_completion=false
      {
          "source": {
              "index": "blog"
          },
          "dest": {
              "index": "blog_lastest"
          }
      }
      
      返回:
      
      {
        "task" : "dpBihNSMQfSlboMGlTgCBA:4728038"
      }

根据taskId可以实时查看任务的执行状态

  • 一般来说,如果我们的 source index 很大【比如几百万数据量】,则可能需要比较长的时间来完成 _reindex 的工作,可能需要几十分钟。而在此期间不可能一直等待结果返回,可以去做其它事情,如果中途需要查看进度,可以通过 _tasks API 进行查看。
  • GET /_tasks/{taskId}

如何取消任务?

  • POST _tasks/task_id/_cancel

相关链接

  • https://zhuanlan.zhihu.com/p/341337374

2、深分页问题

  • 需要从集群取回大量的文档,使用游标查询 Scroll:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/scroll-api.html

  • scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价。

  • 游标查询会取某个时间点的快照数据。 查询初始化之后索引上的任何变化会被它忽略。

  • 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引 视图 一样。

  • 深度分页的代价根源是结果集全局排序,如果去掉全局排序的特性的话查询结果的成本就会很低。

  • 启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间。

  • 这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。
    设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。

  • 查询的返回结果包括一个字段 _scroll_id, 它是一个base64编码的长字符串

  • 尽管我们指定字段 size 的值为1000,我们有可能取到超过这个值数量的文档。 当查询的时候, 字段 size 作用于单个分片,所以每个批次实际返回的文档数量最大为 size * number_of_primary_shards 。

  • 注意游标查询每次返回一个新字段 _scroll_id。每次我们做下一次游标查询, 我们必须把前一次查询返回的字段 _scroll_id 传递进去。当没有更多的结果返回的时候,我们就处理完所有匹配的文档了。

  • 理解为什么深度分页是有问题的,我们可以假设在一个有 5 个主分片的索引中搜索。 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点 ,协调节点对 50 个结果排序得到全部结果的前 10 个。

  • 现在假设我们请求第 1000 页–结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。

  • 可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。

  • 排序过程可能会变得非常沉重,使用大量的CPU、内存和带宽。因为这个原因,我们强烈建议你不要使用深分页。

  • 实际上, “深分页” 很少符合人的行为。当2到3页过去以后,人会停止翻页,并且改变搜索标准。

  • 会不知疲倦地一页一页的获取网页直到你的服务崩溃的罪魁祸首一般是机器人或者web spider。

  • 如果确实 需要从你的集群取回大量的文档,你可以通过用 scroll 查询禁用排序使这个取回行为更有效率。

3、highlight 参数

  • https://www.elastic.co/guide/en/elasticsearch/reference/7.10/highlighting.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    GET /_search
    {
     "query": {
      "match": {
       "content": "kimchy"
      }
     },
     "highlight": {
      "fields": {
       "content": {}
      }
     }
    }
  • ragment_size :指定高亮数据展示多少个字符回来; 
    fragment_size The size of the highlighted fragment in characters. Defaults to 100

4、聚合(aggregations)

  • 允许我们基于数据生成一些精细的分析结果

  • 加载和搜索相匹配的文档,并且完成各种计算。

  • 两种类型的聚集:桶型和度量型

    • 度量型 : 某个字段最大值,最小值,平均值等
    • 桶型 :某个论坛最流行的帖子等

5、集群扩容、故障转移

  • https://www.elastic.co/guide/en/elasticsearch/reference/7.10/scalability.html

6、运维监控

  • https://www.elastic.co/guide/en/elasticsearch/reference/7.10/monitor-elasticsearch-cluster.html

7、es 日志, 慢搜索,慢索引

  • https://www.elastic.co/guide/en/elasticsearch/reference/7.10/index-modules-slowlog.html

8、相关性

  • Elasticsearch 相关度评分 TF&IDF算法:https://www.jianshu.com/p/05219358a2e9

  • fuzzy 查询会计算与关键词的拼写相似程度,terms 查询会计算 找到的内容与关键词组成部分匹配的百分比,

  • 但是通常我们说的 relevance 是我们用来计算全文本字段的值相对于全文本检索词相似程度的算法。

  • Elasticsearch 的相似度算法 被定义为检索词频率/反向文档频率, TF/IDF ,包括以下内容:

    • 检索词频率
      检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。
    • 反向文档频率
      每个检索词在索引中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。
    • 字段长度准则
      字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。
  • 控制相关度:
    https://www.elastic.co/guide/en/elasticsearch/reference/7.10/search-rank-eval.html

9、explain api

  • https://www.elastic.co/guide/en/elasticsearch/reference/7.10/_explain_analyze.html

10、refresh API 和  flush API

  • 重点:新段会被先写入到文件系统缓存,refresh则是从这个文件缓存获取新段(写入和打开一个新段的轻量的过程叫做 refresh )
    默认一秒刷新,可以设置

  • 在 Elasticsearch 中,写入和打开一个新段的轻量的过程叫做 refresh 。 默认情况下每个分片会每秒自动刷新一次。这就是为什么我们说 Elasticsearch 是 近 实时搜索: 文档的变化并不是立即对搜索可见,但会在一秒之内变为可见。

  • 如果没有用 fsync 把数据从文件系统缓存刷(flush)到硬盘,我们不能保证数据在断电甚至是程序正常退出之后依然存在。为了保证 Elasticsearch 的可靠性,需要确保数据变化被持久化到磁盘。

  • 在 动态更新索引,我们说一次完整的提交会将段刷到磁盘,并写入一个包含所有段列表的提交点。Elasticsearch 在启动或重新打开一个索引的过程中使用这个提交点来判断哪些段隶属于当前分片。

  • 即使通过每秒刷新(refresh)实现了近实时搜索,我们仍然需要经常进行完整提交来确保能从失败中恢复。

  • Elasticsearch 增加了一个 translog ,或者叫事务日志,在每一次对 Elasticsearch 进行操作时均进行了日志记录。通过 translog

  • translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录。当 Elasticsearch 启动的时候, 它会从磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog 中所有在最后一次提交后发生的变更操作。

  • translog 也被用来提供实时 CRUD 。当你试着通过ID查询、更新、删除一个文档,它会在尝试从相应的段中检索之前, 首先检查 translog 任何最近的变更。这意味着它总是能够实时地获取到文档的最新版本。

  • 这个执行一个提交并且截断 translog 的行为在 Elasticsearch 被称作一次 flush 。 分片每30分钟被自动刷新(flush),或者在 translog 太大的时候也会刷新。

  • flush API 可以 被用来执行一个手工的刷新(flush)
    POST /blogs/_flush , POST /_flush?wait_for_ongoin

五、常用REST命令

  • PUT 谓词(“使用这个 URL 存储这个文档”),

  • POST 谓词(“存储文档在这个 URL 命名空间下”)

  • GET 查询

  • DELETE 删除

  • HEAD 检查文档是否存在

  • 某些特定语言(特别是 JavaScript)的 HTTP 库是不允许 GET 请求带有请求体的。 事实上,一些使用者对于 GET 请求可以带请求体感到非常的吃惊。

  • 而事实是这个RFC文档 RFC 7231— 一个专门负责处理 HTTP 语义和内容的文档 — 并没有规定一个带有请求体的 GET 请求应该如何处理!

  • 结果是,一些 HTTP 服务器允许这样子,而有一些 — 特别是一些用于缓存和代理的服务器 — 则不允许。

  • 对于一个查询请求,Elasticsearch 的工程师偏向于使用 GET 方式,因为他们觉得它比 POST 能更好的描述信息检索(retrieving information)的行为。
    然而,因为带请求体的 GET 请求并不被广泛支持,所以 search API 同时支持 POST 请求

扩展

  • https://www.cnblogs.com/nankezhishi/archive/2012/06/09/getandpost.html#!comments
  • 不是所有客户端都支持发起带有body的HTTP GET请求,比如jQuery就直接限制了

1、数据索引

  • 数据的mapping一般只执行一次,不使用代码方式创建,使用curl即可
    关掉自动映射:即”dynamic”:”false”, 未预先定义的字段不自动保存
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    $ curl -X PUT 'localhost:9200/accounts' -d '
    {
    "mappings": {
    "dynamic" : "false",
    "properties": {
    "user": {
    "type": "text",
    "analyzer": "ik_max_word",
    "search_analyzer": "ik_max_word"
    },
    "title": {
    "type": "text",
    "analyzer": "ik_max_word",
    "search_analyzer": "ik_max_word"
    },
    "desc": {
    "type": "text",
    "analyzer": "ik_max_word",
    "search_analyzer": "ik_max_word"
    }
    }
    }
    }'

2、分析查询

1
2
3
4
5
$ curl -X POST -H Content-Type:application/json 'localhost:9200/_analyze' -d '
{
"analyzer":"ik_max_word",
"text":"中华人民共和国国歌"
}'
1
2
3
4
5
POST _analyze
{
"text": "我爱北京天安门",
"analyzer": "icu_analyzer"

3、查看索引

  • curl -X GET "localhost:19200/xxxxxx/_mapping?pretty"
  • curl -X GET "localhost:19200/_cat/indices"

4、索引别名

1
2
3
4
5
6
7
8
9
curl -XPOST 'http://localhost:9200/_aliases' -d '
{
"actions": [{
"add": {
"index": "testtmp",
"alias": "test"
}
}]
}'

5、替换别名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST _aliases
{
"actions": [{
"remove": {
"index": "anchor.20220421",
"alias": "anchor"
}
}, {
"add": {
"index": "anchor.20220422",
"alias": "anchor"
}
}]
}

6、查看ES版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
curl -XGET localhost:19200
{
 "name": "node-1",
 "cluster_name": "es6_dev_2",
 "cluster_uuid": "MG_zNwBhQZC1C4Yjm8IFQA",
 "version": {
  "number": "6.7.1",
  "build_flavor": "default",
  "build_type": "tar",
  "build_hash": "2f32220",
  "build_date": "2019-04-02T15:59:27.961366Z",
  "build_snapshot": false,
  "lucene_version": "7.7.0",
  "minimum_wire_compatibility_version": "5.6.0",
  "minimum_index_compatibility_version": "5.0.0"
 },
 "tagline": "You Know, for Search"
}

7、文档索引

1
2
3
4
5
6
7
8
PUT anchor/_doc/1?version=2&version_type=external
{
"id": "54354",
"userId": 324325,
"nickname":"i love you",
"searchStatus": 1,
"weight": 99
}

8、查看索引数量

1
GET /_cat/count/song?v

六、其他

1、jdk版本

  • 由于Elasticsearch依赖于jdk,es和jdk有着对应的依赖关系。具体可见:
    https://www.elastic.co/support/matrix
    https://www.elastic.co/guide/en/elasticsearch/reference/7.10/setup.html

2、安装问题

  • https://www.cnblogs.com/heyongboke/p/11379472.html
  • https://www.jianshu.com/p/64d4b7472cfb

3、String类型已弃用

  • String:string(弃用), text, keyword(ElasticSearch 5.0开始支持)

  • ElasticSearch数据类型–string类型已死, 字符串数据永生: https://segmentfault.com/a/1190000008897731

text vs keyword

  • Elasticsearch中text与keyword的区别:
    https://www.cnblogs.com/sanduzxcvbnm/p/12177377.html

  • text类型

    • 1:支持分词,全文检索,支持模糊、精确查询,不支持聚合,排序操作;
    • 2:test类型的最大支持的字符长度无限制,适合大字段存储;
    • 使用场景:
      存储全文搜索数据, 例如: 邮箱内容、地址、代码块、博客文章内容等。
      默认结合standard analyzer(标准解析器)对文本进行分词、倒排索引。
      默认结合标准分析器进行词命中、词频相关度打分。
  • keyword

    • 1:不进行分词,直接索引,支持模糊、支持精确匹配,支持聚合、排序操作。
    • 2:keyword类型的最大支持的长度为——32766个UTF-8类型的字符,可以通过设置ignore_above指定自持字符长度,超过给定长度后的数据将不被索引,无法通过term精确匹配检索返回结果。
    • 使用场景:
      存储邮箱号码、url、name、title,手机号码、主机名、状态码、邮政编码、标签、年龄、性别等数据。
      用于筛选数据(例如: select * from x where status=’open’)、排序、聚合(统计)。
      直接将完整的文本保存到倒排索引中。

4、客户端代码转curl技巧

  • 该方法仅限于使用rest client的情况
  • 有时想知道代码实际发出的请求是怎样的,并且在控制台进行快速调整测试,可以怎么做?
  • 其中一个方法就是使用代理(fiddler、whistle等)抓包。

(1)连接 ES时指定代理

1
2
RequestConfig.Builder builder = RequestConfig.custom()
builder.setProxy(new HttpHost(proxyHost, proxyPort));

(2)如果连接的ES是HTTPS的,那么需要安装代理导出的证书

shell keytool -import -alias whistle -keystore cacerts -file rootCA.crt

扩展

基于MySQL如何自动同步到ES

  1. 使用canal监听MySQL的binlog
  2. 也可以考虑使用https://github.com/go-mysql-org/go-mysql-elasticsearch

后记

  • 很多东西不用纠结,以前每看一次es,都会纠结类型怎么使用合理,实际上这本书就是一个不合理的设计,官方最后也承认并废弃了

《驱动力》笔记

发表于 2022-04-04

行为的三种驱动力(drive)

  • ①生物性驱动力:人类以及其他动物饮食以止饿,饮水以解渴,交配以满足性欲。
  • ②外在动机:做出特定行为时环境会带来的奖励或惩罚。
  • ③内在奖励:完成任务取得的成绩,获得的愉悦感。

  • 传统的激励因素,也就是我所说的“如果–那么”型奖励(“如果你做这个,就能得到那个”),对于很多简单机械的推算型工作很有效,但对于现代经济赖以为生的需要创造力和概念思维能力的复杂右脑工作来说,这些激励因素大多没什么效果。
  • 自主、专精和目的是超越国家和语言界限的概念。这些概念不是美国的想法也不是中国的想法,它们是人类的理想。人类的天性决定了他们会寻求对自己命运的掌控权,希望自己引导自己。无论住在上海还是芝加哥,人们都希望能在有意义的工作上有所成就。此外,无论在哪里工作和生活我们每个人都想奉献,都想改变世界。

引言 科学向左,企业向右

  • 当时,科学家们认识到行为有两种主要的驱动力(drive)。第一种驱动力是生物性驱动力:人类以及其他动物饮食以止饿,饮水以解渴,交配以满足性欲。
  • 如果生物性驱动力来自内部,那么第二种驱动力则来自于外在动机:做出特定行为时环境会带来的奖励或惩罚。
  • 内在动机(intrinsic motivation)的东西的确存在,但是显然它从属于另外两种驱动力。如果猴子解开装置可以得到葡萄干作为奖励,那么它们本应该会毫无疑问地表现更好。但是哈洛这么做的时候,猴子们犯的错误却越来越多,装置解开的次数也有所减少。
  • “把金钱当做某种行为的外部奖励时,行为主体就失去了对这项活动的内在兴趣。”奖励只能带来短期的爆发,就像是少量咖啡因只能帮你多撑几个小时,但其效果会逐渐消失。更糟糕的是,它降低了人们继续这项工作所需的长期积极性。
  • 人类有“发现新奇事物、进行挑战、拓展并施展才能以及探索和学习的内在倾向”。但是,第三种驱动力比另外两种更脆弱,它只有在合适的环境下才能存在。
  • 第三种驱动力驱使,即我们想要主导自己的人生、学习并创造新事物,通过自己以及我们的世界做得更好的内在需求。

第一部分 驱动力3.0时代来临

  • 奖励只能带来短期的爆发,就像是少量咖啡因只能帮你多撑几个小时,更糟糕的是,它还降低了人们继续这项工作所需的长期积极性。
  • 驱动力1.0时代:生物冲动在很久很久以前,比如50 000年前,对人类行为的根本假设很简单也很真实——我们要想尽一切办法生存下来。
  • 驱动力2.0时代:寻求奖励,避免惩罚人类形成了更加复杂的社会,身边的陌生人越来越多。为了做成一件事,我们需要与人合作,纯粹建立在生物性驱动力之上的操作系统出现了局限。
  • 事实上,有时候我们甚至需要对这种驱动力加以限制,以免你偷走了我的午餐,我拐走了你的另一半。因此,我们逐步用与我们的工作和生活方式更匹配的新版本代替了旧版本,这真是人类文化工程上一次非凡的壮举。人类不只是生物冲动的集合。第一种驱动力依然在起作用,这一点毋庸置疑,但它不能完全解释我们是谁。我们也有第二种驱动力:寻求奖励,避免惩罚。从这个角度来看,全新的操作系统——驱动力2.0诞生了。当然,其他动物也会对奖励和惩罚做出反应,但事实证明,只有人类能够疏导这种驱动力,并利用它发明了从合同法到便利店等各种新事物。驾驭第二种驱动力对推进全球经济发展的进程至关重要,近两个世纪尤为如此。
  • 为了确保这种情况的发生,你仅仅需要奖励你鼓励的行为,惩罚你不鼓励的行为。人们会理性地对这些外部作用力(外在动机)做出反应,他们自身以及整个系统都会得到发展。
  • 尽管驱动力2.0系统越来越精密,志向也越来越远大,但它却没有让人变得高尚起来。归根结底,它认为人类和马匹没有太大区别,要想让我们朝正确的方向前进,只需在我们面前放个更脆的胡萝卜或者挥舞更锋利的大棒就行了。
  • 1960年,麻省理工学院管理学教授道格拉斯· 麦格雷戈(Douglas McGregor)把马斯洛的一些观点引入商界,对人的本质是懒惰的、若没有外部奖励或惩罚他们就会安于现状的假设提出了挑战。他说,人有其他更高级的驱动力。如果管理人员和企业领导者尊重这些驱动力,它们就能让企业受益。
  • 以乐为本的内在动机,也就是参与项目时能感受到的创造力是最强大、最常见的动机。
  • 经济学家研究的是人的行为,而不是人的言语,因为我们做出的是对我们最有利的行为,我们是理性的自我经济利益计算者。
  • 我们并不总是理性的个人经济利益计算者,通常不会讨价还价以使自己的利益最大化。
  • “工作,基本上是简单但不怎么有趣的任务。让人们工作的唯一方法是适当激励、严格监督。”在20世纪初,泰勒的话还是有几分道理的。
  • 外部奖励和惩罚(胡萝卜与大棒并用)对推算型工作很适用,但是对探索型工作可能具有破坏性。
  • 千篇一律、没什么意思的重复性工作需要管理,而有意思的非重复性工作依靠的则是自我管理。

第2章 奖励的惩罚,胡萝卜大棒失效了

  • 鼓励不道德的行为,带来瘾嗜,滋生短视的思维。但胡萝卜大棒并非总是坏东西。它们对于机械的重复性工作也许很有效,这是因为这种工作几乎没有内在积极性可供破坏,也没有多少创造力来扼杀。对于非重复性的脑力劳动来说,奖励更加危险,“如果-那么”型奖励尤为严重。

  • 对一个行为加以奖励会让这种行为发生的频率增加;对一个行为施以惩罚会让这种行为发生的频率减少。

  • 任何有关工作积极性的讨论都出自生活中一个简单的事实:人必须赚钱谋生。工资、合同款、补助、小费都是我所说的“基线报酬”(baseline reward)。如果一个人得到的基线报酬不足或者分配不公,他的关注点就会放在所处环境的不公以及对环境的焦虑上。因此,你既得不到外在激励因素的可预测性,也得不到内在激励因素难以捉摸的效果,基本上你没法激励他。但是一旦过了这道门槛,胡萝卜大棒就可能会得到与其初衷正好相反的结果。本来是想要提高积极性的机制最后降低了积极性;本来是想要激发创造力的策略最后却抑制了创造力;本来是想要让好人好事越来越多实际上却让好人好事越来越少。与此同时,奖励和惩罚没能限制消极行为,反而让它们大爆发,让欺骗、瘾癖和目光短浅的危险行为越来越多。

  • 从这个有趣的情节里,马克· 吐温提炼出了一条重要的有关积极性的原则:“所谓‘工作’就是一个人被迫要干的事情,至于‘玩’就是一个人没有义务要干的事情。”他还写道:“在夏季,英国有钱的绅士每天驾着四轮马拉客车沿着同样的路线走上30~50公里,他们为这种特权花了很多钱。可是如果因此付钱给他们,那就把这桩事情变成了工作,他们就会撒手不干了。”换言之,奖励有时候很奇怪,它就像对人的行为施了魔法:把有意思的工作变成苦工,把游戏变成工作。它通过减少内在激励因素,让成绩、创造性甚至善行都像多米诺骨牌一样接连倾倒,我们称之为“汤姆索亚效应”(Sawyer Effect)。一些有趣的实验抽样调查揭示了这一效应发生的四个领域,同时也再一次证明:科学向左,企业向右。

  • 有形的奖励实际上会对内在激励因素产生消极影响。如果家庭、学校、企业、运动队等关注的是短期目标,选择的是控制人们的行为,它们就会对长期效果造成相当的损害。

  • 人们用奖励来提高其他人的积极性,提高某种行为发生的频率,希望能从中获益,但他们经常破坏人们对某种行为的内在积极性,无意中增加了隐形成本。

  • 由于印度农村的生活成本要比北美低得多,因此研究人员不必倾家荡产就能提供大量奖励。

  • 奖励会使人们的关注面变窄,遮蔽他们宽广的视野,让他们没法看到常见事物的新用法。

  • “在艺术学院学习期间,越少表现出外在动机的学生,在艺术的道路上就越成功,无论是毕业几年内还是近20年后都是如此。”这一点对男性尤为明显。对受内在动机激励的画家和雕刻家来说,发现的乐趣和创作带来的挑战就是奖励,他们更能经受住考验,渡过没有报酬、不被认可的艰苦时期,而这段时间在他们的艺术生涯中难以避免。第三种驱动力也有自己的爱丽丝仙境,在这个世界中另一个悖论也随之出现。

  • 研究表明:“有些艺术家之所以能在绘画和雕刻道路上苦苦追寻,是因为创作本身的快乐而非外部奖励,而且他们所创造的艺术也得到了更多社会认可。最终得到外部奖励的人,恰恰是那些最没有动力追求外部奖励的人。”

  • 阿马布勒和其他研究者发现,外部奖励对推算型工作,也就是那些按照既有方式就能得出合理结论的工作很有效。但是对于更依赖右脑的工作,也就是那些需要灵活的问题解决能力、发明创造能力以及思维理解能力的工作来说,有条件的奖励可能会很危险。获得奖励的对象在探寻事物的细微之处、找到原创的解决方案方面,通常会遇到更大的困难。这是社会科学中最毋庸置疑的发现之一

  • 它玷污了这种利他举动,把做善事的内在欲望“挤了出去”。

  • 综上所述,胡萝卜大棒的7个致命弱点如下:

    • ● 它们会令内在动机消失;
    • ● 它们会令成绩下降;
    • ● 它们会扼杀创造力;
    • ● 它们会抑制善行;
    • ● 它们会鼓励欺诈、走捷径以及不道德行为;
    • ● 它们会让人上瘾;
    • ● 它们会滋生短视思维。
  • 基本要求:任何外部奖励都需要是别人预想不到的,而且只有在任务完成后才能给出。
    换言之,“如果–那么”型奖励是个错误,应把它改成“既然–那么”型奖励。比如说,“现在你做好了海报,而且干得很出色,既然这样,那我请你出去吃午饭吧。”

  • 如果物质奖励是在人们完成一项任务后出其不意地给出的,那么人们比较不容易认为奖励是做这项任务的理由,它对内部积极性造成危害的可能性也比较小。

  • 支付高于平均水平的工资是一种绕开“如果–那么”型奖励的优雅方式,它能减少员工对不公平的关注,把钱的问题从桌面上拿开。这是让人们把关注点放在工作本身的又一个方法。事实上,其他经济学家也已经证明,基本工资更多比有吸引力的奖金结构更能提高绩效,更能增加组织认同感。

第3章 做I型人还是X型人

  • I型行为更少关注某一活动带来的外部奖励,而更多关注这项活动本身的内在满足感。

  • 自我决定理论认为人类有三种内在需求:能力的需求(competence)、自主性的需求(autonomy)和归属的需求(relatedness)。如果这些需求得到了满足,我们就会行动积极、工作高效、心情愉悦;但如果这些需求受到阻碍,我们的积极性、工作效率和心情愉悦度就会直线下降。瑞安在与我们的一次谈话中说道:“如果我们天性中有什么东西是基础性的,那这个东西就是感兴趣的能力。有些事情会促进这种能力,而有些事情则会破坏这种能力。”换句话说,我们都有第三种驱动力,它是我们身为人类意义的一部分。但是,我们人性中的这一部分是否会在生活中表现出来,还要看周围环境是否允许。

  • 我们的工作重点,应该是营造一个能够激发各种内在心理需求的环境。

  • 人类有自主、独立、寻求归属感的内在动机。如果这个动机被释放出来,人们就能取得更多成就,就能生活得更加充实。

  • A型行为模式当然是与B型相对应。A型人终日摇旗呐喊,手舞足蹈,像是得了“匆忙症”,而B型人则与他们不同,他们在生活中似乎很少匆匆忙忙,也不会因为自己的愿望而显得有敌意。

  • 在研究中,弗里德曼和罗森曼发现B型人与A型人一样聪慧,而且通常和A型人一样拥有雄心壮志,只是他们用了不同的方法表现自己的雄心壮志。提到B型人时(当时使用以男性为中心的语言很正常),弗里德曼说:“也许他也有相当的‘驱动力’,但是他的性格是那种动力似乎会让他安心的类型,驱动力给了他信心和安全感,不像A型人,驱动力会困扰他、刺激他、惹怒他。”因此,降低心脏病死亡率、改善公共健康的重点在于帮助A型人变得像B型人。

  • 大多数管理者认为他们的员工从骨子里讨厌工作,要是情况允许一定会逃避工作。这些没有个性的奴隶害怕承担责任,迫切需要安全感,极度渴望被指引。因此,“必须强迫他们,控制他们,给他们指引,用惩罚威胁他们,让他们努力工作,并最终达到企业的目标”。但是,麦格雷戈提出,可以从另外一个视角看待员工。这个视角可以更精确地评估人类的境况,让公司的运营有一个更高的起点。这种观点认为:对工作感兴趣“和玩乐、休息一样自然”,很多人都善于创造,他们足智多谋,只要情况允许,他们就会接受甚至主动寻求责任感。

  • 为了解释这两种截然相反的观点,麦格雷戈从字母表最后挖来了两个字母。他称第一种观点为X理论,第二种观点为Y理论。他说,如果你的出发点是X理论,那么你的管理方法产生的效果难免会有局限,有时甚至可能事与愿违;如果你相信“大众皆平庸”,那么平庸就会成为你无法逾越的天花板。但是如果你的出发点是Y理论,结果就会有无限可能,不只是员工潜能无限,公司的表现水准也会大不一样。因此,想让商业机构更好的运营,就要把管理思维从X理论转向Y理论。

  • X型行为的核心是第二种驱动力,I型行为的核心是第三种驱动力。

  • 如果我们打算增强组织能量、跳出成绩平平的怪圈,如果我们觉得我们的生意、我们的生活、我们的世界出了问题而且想要解决这些问题,那就需要从X型转向I型。我用这两个字母来代表外在和内在,同时也向道格拉斯· 麦格雷戈致敬。的确,把人类的行为简单分为两类似乎忽略了行为之间的一些细微差别。没有人在分分秒秒、时时刻刻都是X型,也没有人完全是I型,但是的确,我们经常表现出明显不同的个性。

  • 对于X型人来说,外在奖励是主要动机,拥有更深层的满足感当然很好,但这只排第二位;对I型人来说,自由、挑战、担当是主要动机,拥有其他的好处也不错,不过那些只是额外奖励而已。

  • 两者之间的6大区别。

    • 第一,I型行为是后天习得而不是先天形成的。这些行为模式不是一成不变,而是基于一定的环境、经验、场景而形成的习惯。由于I型行为部分是出于人类的普遍需求,年龄、性别和国籍对它的影响不大。科学证明,一旦人们学习了基本的行为方式和态度,能够把它们作为背景知识,他们的积极性和最终成绩都会提升。任何X型人都能变为I型人。
    • 第二,从长远的角度看,I型人比X型人表现水准更高。由内在因素激励的群体所取得的成就通常高于寻求外部奖励的竞争对手,但是在短期内情况不见得如此。高度关注外部奖励能够更快达到目标,但问题是这种方法难以持续,对满足专精的欲望也没什么帮助,要知道专精的欲望是长期努力的能量之源。有证据显示,最成功的人一般不直接追求通常意义上的成功。他们之所以努力工作、克服困难、持之以恒,是因为他们有着掌控自己生活、了解外部世界、完成某个长期目标的内在欲望。
    • 第三,I型人不会对金钱和认可嗤之以鼻。I型人和X型人都在意金钱。如果报酬没有达到之前所说的基线,也就是说公司给他的钱不够多或者与其他人的相比不公平,员工无论更倾向于I型还是X型都会失去积极性。然而,一旦报酬达到了那条基线,金钱对I型人和X型人的影响就不一样了。I型人不会拒绝升职或加薪。公正地给他们足够的报酬至关重要,因为这样他们就不再考虑金钱问题,而把注意力集中到工作本身。相比之下,对很多X型人来说金钱才是问题本身,是他们做事的原因所在。得到认可也一样,I型人希望得到认可,是因为认可是对他们工作的反馈,但是与X型人的不同之处在于,认可不是他们的目标所在。
    • 第四,I型行为是可再生资源。我们可以把X型行为比做煤炭,把I型行为比做太阳。近几个世纪里,煤是最便宜、最容易得到、使用效率最高的能源。但是煤有两个缺点。首先,它会带来空气污染、温室气体这些讨厌的东西;其次,它是有限的,使用得越多它就越难获得,价格也会逐年升高。X型行为与之类似,强调奖励或惩罚需要大量的其他外在激励因素(在第2章中已经举了多个例子)。“如果–那么”型的激励因素成本会越来越高,但是I型行为依靠的是内在动机,它所需要的能量很容易加满,能量损耗也比较小。它和清洁能源一样:价格低廉,使用安全,可以再生。
    • 第五,I型行为能让生理和心理状态更好。大量心理研究报告显示,相比需要外在动机推动的群体,由内在动机推动的群体自我评价更高,人际关系更好,总体健康状态也更好。相比之下,那些主要动机是金钱、名誉和美貌等这些外在因素的群体,心理健康状态较差。X型和A型之间存在某种联系。德西发现:倾向于控制和外部奖励的人群自我意识更强,自我保护意识更强,也更容易表现出A型行为模式。
    • 第六,I型行为依赖于三种营养素:自主、专精和目的。I型人由自己决定方向,他的目标在于让一件重要的事情变得越来越好,把追求卓越与更崇高的目标联系在一起。也许有人会对这种见解不屑一顾,认为这种观点太理想主义,太模棱两可,但是科学可不这么认为。科学已经确认了这种行为对人类的重要性,如今在快速变化的社会里,这对任何人和机构的成功都至关重要。因此,我们可以做个选择。我们可以不顾现代科学的观点,坚持以古老的习惯来看待人类的积极性,或者我们可以听听这些研究,把我们的商业行为和个人行为升级到21世纪,用一个新的系统来帮助我们、我们的公司以及我们的世界更好地运转。
  • I型行为是后天习得而不是先天形成的

  • 首先问一个大问题:“你的那句话是什么”;再问一个小问题:“今天的我比昨天更优秀吗”

  • 来次施德明吧

    • 每7年,施德明就会关闭他的平面设计工作室,告诉客户这一年内他都不在,他要去享受一次365天的长假。他用这段时间去旅行,去他从未去过的地方生活,去试行自己的新项目。
    • 为身心俱疲做好准备。正因为如此,可以做到刻意练习的人才少得可怜,但这也正是它有效的原因所在。

第二部分 驱动力3.0的三大要素

  • 人们需要在做什么、什么时候做、和谁做以及如何做上能够自主。也许是时候把“管理”这个词扔进语言学的烟灰堆里了,这个时代不需要更好的管理,它需要自我管理的复兴。

  • 知识工作者决定自己的工作内容及其结果非常必要,这是因为他们必须自主。工作者应该仔细思考他们的工作规划,并按照这个规划执行。我应该关注哪个地方?我负责的事情应该有怎样的结果?最后期限应该是什么时候?

  • 但从长期来看,创新物美价廉,中庸才昂贵,而自主能够当做解毒剂。

  • ROWE:“只问结果的工作环境”(results–only work environment)。

    • 他们不需要某个时间前到达办公室,他们任何时间都可以不在办公室,只要把工作做完就行了。至于怎么做、什么时候做、在哪做都由他们自己来决定。
    • 管理不是走来走去看别人在不在办公室里。”管理是营造能让人们以最佳状态工作的环境。
    • 对他们来说,工作就是手艺,他们需要很多自主权。
    • 在ROWE的环境里,人们不会因为工资涨了10 000或者20 000就跳槽去另一家公司。他们工作时享有的自由比涨工资更有价值、更难以比拟,而且员工的爱人、朋友、家人都是ROWE最坚实的拥护者。
  • 工作自主

    • 充满好奇心、倾向于自我管理才是我们的本性
    • 自主,这一人类天性的基本品质,是自我决定理论的关键所在。
    • 人们会寻求自主,希望用自主来改善自己的生活
    • 支持自主的老板手下的员工能得到更多满足感。这些老板从员工的角度看问题,给员工有意义的反馈和信息,在要做什么、怎么做的问题上为员工留有很大的选择空间,鼓励员工执行新项目
    • 管理不是解决方法,而是问题本身。
    • 卡农布鲁克斯告诉我:“我们总是坚持认为金钱是你唯一能损失的东西。如果你给的薪水不够多,你就会失去人才。但是除了这个以外,金钱也不能成为激励因素,重要的是其他部分。”
    • 要出现I型行为,人们必须在以下4件事上能够自主:工作内容、工作时间、工作方法和工作团队。
    • 20世纪三四十年代,3M董事长威廉· 麦克奈特(William McKnight),为人谦虚低调、想法别具一格。他信奉一个简单的信条,这在那时看来有点颠覆性:“雇用好人,然后不管他们。”
    • 一个人投入时间的多少和他生产的价值之间的联系没有规律可循,也无法预测。
    • 人们想要做好工作,那么,我们应该让他们关注工作本身而不是他们工作所花费的时间。
    • 如果你想要和更多的I型人一起工作,最好的方法就是把自己也变成其中之一。
    • 鼓励自主并不是鼓励不负责任。
  • 评论:自主不是不管不闻不问,任其自由发展,等到出问题的时候来收拾或者给人家评语定性,真正的自主是在员工自我提升自发工作的时候给以及时的提醒和帮助。

  • 驱动力2.0假设:如果人们拥有了自由,他们就会逃避,自主是一种绕开责任的方法。驱动力3.0则基于不同的假设:人们想要负责任,而确保他们对自己的工作内容、工作时间、工作方法、工作团队有控制权,是达到这个目标的必经之路。

  • 我们天生就是玩家,而不是小兵。我们想要做的是自主的个体,而不是机器人。

  • 而I型方式则与此不同。成绩单不是潜在的奖励,而是在学习过程中向学生提供有用反馈的方法。而且I型学生了解,自我评价是获取成绩单的好方法。

  • 给孩子零花钱,让孩子做家务,但不要把这两者混在一起告诉你为什么零花钱对孩子有益:给他们一点自己的钱,让他们决定是存还是花,能够让他们有一些自主权,教他们对钱负责。告诉你为什么家务活对孩子有益:家务活儿能告诉孩子们家庭是建立在彼此的义务之上的,家庭成员需要互相帮助。

  • 确定自己是不是已经精通了某件事的最好方法就是,把它教给别人。

第5章 专精:把想做的事情做得越来越好

  • 控制带来的是服从,自主带来的则是投入。

  • 驱动力2.0需要服从,而驱动力3.0需要投入。只有投入才能带来专精,把某件重要的事做得越来越好。对专精的追求是我们第三种驱动力中非常重要、但经常隐匿起来的一部分。专精是由“心流”开始的,即当我们所面临的挑战与我们的能力恰好吻合时的最佳体验。

  • 专精是一种心理定向:它需要一项本领,它不仅不认为我们的能力有限,还认为能力能够无限提高;专精是一种痛苦:它需要努力、坚毅以及刻意练习。专精是一条渐近线:它不可能完全实现,但正因如此,它既让人崩溃又令人着迷。

  • 你会发现一些事情能够让你得到深层的满足,而且它们对你来说非常具有挑战性,想要做这些事情的欲望能够激起最高层次的创造力,无论在艺术、科学还是商业上都是如此。

  • 控制带来的是服从,自主带来的则是投入。正是因为这个差异,I型行为的第二个要素出现了:专精,是指把想做的事情做得越来越好的欲望。

  • 服从让我们能够撑过白天,但投入能让你撑过晚上

  • 他用一个新词代替了那个来自于希腊语的晦涩的形容词,用来描述这些处于最佳状态的时刻:心流。人们生活中最兴奋、最令人满意的体验就是他们处在心流之中的时候。

  • “金凤花任务”(goldilocks task):既不太热也不太冷、既不会过于困难也不会过于简单的挑战。工作让人崩溃的原因之一是人们必须做的事情和他们能够做的事情之间不匹配。如果他们必须做的事情超过了他们的能力范围,结果就会是焦虑。如果他们必须做的事情达不到他们的能力范围,结果就会是厌倦。

  • 目标可以分为两类:表现目标(performance goals)和学习目标(learning goals)。在法语课上得A是表现目标,能够说法语是学习目标。德韦克写道:“为了坚持下去并不断尝试,有学习目标的学生不会觉得他们已经擅长一个东西了。毕竟,他们的目标是学习,而不是证明他们很聪明。”

  • 在通往专精的道路上挫折无可避免,他们甚至认为挫折可以当做路途上的指示牌。

  • 尽管更加努力的重要性很容易理解,但长时间工作而不转换目标的重要性可能就没那么好理解了……想要取得更高的成就,坚毅与天分同样关键,在任何领域都是如此。

  • 朱利叶斯· 欧文(Julius Erving)说:“专业就是做你喜欢做的事情,即使在你不想做的时候。”

  • 一个设计的最终成品,永远不会是在一瞬间灵光乍现创造而来的;相反,他带着无限的警惕接近它,跟踪它,一会儿从这个角度,一会儿从那个角度……对他来说,最终成品是一条他无限接近但永远不会到达的渐近线。”

  • 这是专精的自然属性:专精是一条渐近线。归根结底,专精之所以吸引人,正是因为它总是在闪躲。

  • 驱动力3.0所需的深层次投入感并非无关紧要的细枝末节。它是必需品,我们需要它才能存活,它是我们灵魂的氧气。

  • 让你的团队成为“无竞争”地带。一些领导让员工互相竞争,希望竞争能激励他们表现得更好,但这种方法很少奏效,而且它经常会破坏内在积极性。如果你一定要用一个以字母C开头的单词,不要用竞争(competition),改成合作(collaboration)、协作(cooperation)

  • 试试工作轮换。如果有人已经厌倦了手头的任务,看看他能不能把这门已经精通了的技艺传授给其他人。然后再看看他能不能承担一些经验更丰富的成员所做的工作。

  • 用目的鼓舞,而不要用奖励激励。没有什么比共同的使命更能将团队团结在一起了。人们越是追求一项共同的事业,你的团队所做的工作就越是出色、越是让人满意,无论这项事业是让某个事物好得出奇,还是把外部竞争者比下去,甚至是改变世界。

第6章 目的:超越自身的渴望

  • 你是不是常听到“效率”、“利益”、“价值”、“优势”、“焦点”、“差异”这样的词语,这些目标很重要,但它们缺乏唤醒人类心灵的能力。我们常常以利润最大化为中心,而驱动力3.0在不拒绝利润的同时,强调的是目的最大化:如果一个人感觉不到自己属于更伟大更长久的事物,他就无法过上真正出色的生活。寻找目的是我们的天性,我们在复兴属于我们的商业,重塑属于我们的世界……
  • 一个人的生命价值,可以用他对处于逆境中的人的影响力来衡量。既然死亡对每个人来说都是一件确定的事,那么从出生到死亡这段时间内,一个人的生活质量就变得更为重要了。
  • 如果一个人感觉不到自己从属于更伟大、更长久的事物,那他就没法过上真正精彩的生活。米哈伊·希斯赞特米哈伊积极心理学大师
  • I型行为这张三角桌的前两条腿:自主和专精至关重要。但是,为了保持平衡,我们还需要第三条腿:目的,它能为它的两个伙伴提供内容。朝专精努力的自主的人会有高水准的表现,特别是,如果这么做是为了实现更宏伟的目标,那么这样的人就能取得更多成就。那些由个人内心深处驱动的人,会把他们的愿望系于比自己更宏大的事业上,更不用说那些效率最高、自我满足感最多的人了。
  • 这些安装了驱动力3.0系统的公司的目标,不是在遵守道德准则、法律规范的同时追逐利润,它们的目标是追寻目的,利润是它们的催化剂而不是目标。
  • 研究发现,人们如何花钱至少和他们赚了多少钱一样,都是最重要的。具体来说,把钱花在别人身上,比如给你的爱人买花而不是给你自己买MP3,或者花在某项事业上,比如捐给某个宗教机构而不是去理个价值不菲的发,都能增加我们的主观幸福感。
  • 那些认为自己正在实现目标(积累财富,获得赞誉)的人称,他们的自我满意度、自我评价和积极情感水平并不比做学生的时候高。
  • 满足不仅仅取决于有目标,而且取决于有正确的目标。
  • 一个健康的社会、一个健康的商业机构是从目的开始的,它们把利润看做朝这个目的进发的方式,或者是取得成就后让人愉快的副产品。
  • 专业表现水平高的秘密不是我们的生物性驱动力或者追求奖励、逃避惩罚的第二种驱动力,而是我们的第三种驱动力,是我们想要主导我们的生活、延展我们的能力、让生活更有意义的深层欲望。
  • 我们知道人生中最富足的体验不是得到别人的认可,而是能够倾听自己的声音:做重要的事情,做好它,为了达成自己的事业而努力。

总结

  • 第三种驱动力驱使,即我们想要主导自己的人生、学习并创造新事物,通过自己以及我们的世界做得更好的内在需求。
  • 驱动力2.0,对于机械的重复性工作也许很有效,这是因为这种工作几乎没有内在积极性可供破坏,也没有多少创造力来扼杀。
  • 汤姆索亚效应
  • 外部奖励对推算型工作,也就是那些按照既有方式就能得出合理结论的工作很有效。但是对于更依赖右脑的工作,也就是那些需要灵活的问题解决能力、发明创造能力以及思维理解能力的工作来说,有条件的奖励可能会很危险。
  • 对于无聊的任务,奖励不会破坏人们的内在积极性,因为根本就没有积极性可以破坏。
    • 尽管这些看得见摸得着的有条件的奖励经常会破坏内在积极性和创造力,但在这里这些坏处没那么重要。这项任务既不用激发深层的热情,也不需要深度思考。在这个案例里,胡萝卜有益无害。
    • 对这项工作的必要性做出合理解释。若一项本身不那么有意思的工作成为一个大目标的一部分,它就可以变得更有意义,也会更吸引人。解释一下为何海报如此重要,为何寄出它们对机构的使命至关重要。
    • 承认此项任务枯燥无趣。当然,这是为别人着想的做法。承认这一点能让人们明白为何这种“如果–那么”型奖励只是特殊情况而已。
    • 让人们用自己的方式完成任务。给他们自主的权利,不要控制他们。说明你需要的结果。
  • 更好的策略是让报酬数量合理,然后再把它们扔到注意力覆盖范围之外。高效的公司给员工报酬的数量和方式能够让员工几乎忘了报酬这回事,而只是专注于工作本身。
  • I型行为更少关注某一活动带来的外部奖励,而更多关注这项活动本身的内在满足感。
  • 知识工作者决定自己的工作内容及其结果非常必要,这是因为他们必须自主。支持自主的老板手下的员工能得到更多满足感。管理不是解决方法,而是问题本身。
  • 控制带来的是服从,自主带来的则是投入。专精是一种心理定向:它需要一项本领,它不仅不认为我们的能力有限,还认为能力能够无限提高;专精是一种痛苦:它需要努力、坚毅以及刻意练习。
  • 目标可以分为两类:表现目标(performance goals)和学习目标(learning goals)。在法语课上得A是表现目标,能够说法语是学习目标。
  • 一个人的生命价值,可以用他对处于逆境中的人的影响力来衡量。既然死亡对每个人来说都是一件确定的事,那么从出生到死亡这段时间内,一个人的生活质量就变得更为重要了。
  • 这些安装了驱动力3.0系统的公司的目标,不是在遵守道德准则、法律规范的同时追逐利润,它们的目标是追寻目的,利润是它们的催化剂而不是目标。
  • 满足不仅仅取决于有目标,而且取决于有正确的目标。
  • 成就高的人反而焦虑和抑郁,原因之一是他们没有良好的人际关系。他们忙于赚钱,到处应酬,这意味着他们的生活中留给爱、关心、照顾、同情这些真正重要的东西的空间变少了。

个人总结

  • 在工作中寻找驱动力3.0;自由、挑战、担当是主要动机,拥有其他的好处也不错,不过那些只是额外奖励而已。
  • 努力做B型人(驱动力给了他信心和安全感,不像A型人,驱动力会困扰他、刺激他、惹怒他。”因此,降低心脏病死亡率、改善公共健康的重点在于帮助A型人变得像B型人)
  • 为员工创造驱动力3.0的环境,而不是一味压迫。为什么不让大家开心的工作呢?
  • 如何让团队合理的预估时间和工作呢?有条件的情况下,应该让两个团队一起竞争,做相同的事情。最后的评判标准是预估的合理性和效率。失败的团队并不一定要淘汰,而是可以吸取经验,成长成更靠谱的团队。当然如果一直失败,可能要考虑对团队进行重组了。
  • ROWE环境:支持自主的老板手下的员工能得到更多满足感。管理不是解决方法,而是问题本身。(前提是人才密度)
  • 利润是它们的催化剂而不是目标。满足不仅仅取决于有目标,而且取决于有正确的目标。
  • 成就高的人反而焦虑和抑郁,原因之一是他们没有良好的人际关系。他们忙于赚钱,到处应酬,这意味着他们的生活中留给爱、关心、照顾、同情这些真正重要的东西的空间变少了。
  • 有第三种驱动力的人,喜欢欣赏自己的代码,精益求精。虽然有时候似乎没人欣赏有点失落,但是没关系,这样做至少能提升自己的效率,和后续写代码的体验

附录

  • 一位老人在一个小乡村里休养,但附近却住着一些十分顽皮的孩子,他们天天互相追逐打闹,喧哗的吵闹声使老人无法好好休息,在屡禁不止的情况下,老人想出了一个办法——他把孩子们都叫到一起,告诉他们谁叫的声音越大,谁得到的奖励就越多,他每次都根据孩子们吵闹的情况给予不同的奖励。到孩子们已经习惯于获取奖励的时候,老人开始逐渐减少所给的奖励,最后无论孩子们怎么吵,老人一分钱也不给。结果,孩子们认为受到的待遇越来越不公正,认为“不给钱了谁还给你叫”,再也不到老人所住的房子附近大声吵闹了。
  • 评论:社会上流传一句话:有钱人才叫生活,普通人只叫做生存。
    当然,当今社会,只要不懒,生存应该都没有问题。
    而高质量的生活在主流社会的语境里应该是房子,车子,票子,即丰富的物质生活。
    本文的所说的高质量生活很明显不是物质生活,而是追求伟大的目的,让生活变得有意义

index_merge导致死锁问题

发表于 2022-04-03

死锁日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
2021-09-02T17:29:31.339235+08:00 10137065 [Note] InnoDB: Transactions deadlock detected, dumping detailed information.
2021-09-02T17:29:31.339268+08:00 10137065 [Note] InnoDB:
*** (1) TRANSACTION:

TRANSACTION 54557980, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
LOCK WAIT 9 lock struct(s), heap size 1136, 4 row lock(s)
MySQL thread id 10137067, OS thread handle 140359751816960, query id 204818581 10.22.45.73 xxx_feed updating
UPDATE `t_user_xxx_2` SET `end` = 1, `updateTime` = now() WHERE `userId`= 186435331 AND `type` = 2 AND `plotId`=1544270738147465267 AND `end` = 0
2021-09-02T17:29:31.339367+08:00 10137065 [Note] InnoDB: *** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 329 page no 2076 n bits 112 index PRIMARY of table `d_xxx_feed`.`t_user_xxx_2` trx id 54557980 lock_mode X locks rec but not gap waiting
Record lock, heap no 33 PHYSICAL RECORD: n_fields 24; compact format; info bits 0
0: len 8; hex 956e50cb8d06b6fb; asc nP ;;
1: len 6; hex 000003307520; asc 0u ;;
2: len 7; hex b0000000320110; asc 2 ;;
3: len 8; hex 800000006f1fc602; asc o ;;
4: len 4; hex 80000002; asc ;;
5: len 24; hex e69c89e4b880e8b5b7e8818ae5a4a9e79a84e59097efbc9f; asc ;;
6: len 2; hex 5b5d; asc [];;
7: len 4; hex 80000000; asc ;;
8: len 8; hex 8000000000000000; asc ;;
9: len 8; hex 8000000000000000; asc ;;
10: len 30; hex 7b22706c6f744964223a313534343236303535373335313230323637352c; asc {"plotId":1544260557351202675,; (total 233 bytes);
11: len 12; hex e58f98e68081e4b8bbe4baba; asc ;;
12: len 30; hex 68747470733a2f2f73747564696f696d67627373646c2e636c6f75642e6b; asc https://xxxximgbssdl.cloud.k; (total 75 bytes);
13: len 4; hex 80000002; asc ;;
14: len 8; hex 95551c4bc84d1d5b; asc U K M [;;
15: len 0; hex ; asc ;;
16: len 5; hex 99aa828508; asc ;;
17: len 5; hex 99aa828507; asc ;;
18: len 4; hex 80000000; asc ;;
19: len 8; hex 956e50cb8990af73; asc nP s;;
20: len 1; hex 80; asc ;;
21: len 1; hex 80; asc ;;
22: len 1; hex 81; asc ;;
23: len 1; hex 80; asc ;;

2021-09-02T17:29:31.342776+08:00 10137065 [Note] InnoDB: *** (2) TRANSACTION:

TRANSACTION 54557981, ACTIVE 0 sec fetching rows, thread declared inside InnoDB 5000
mysql tables in use 3, locked 3
11 lock struct(s), heap size 1136, 7 row lock(s)
MySQL thread id 10137065, OS thread handle 140359754024704, query id 204818583 10.31.45.123 xxx_feed updating
UPDATE `t_user_xxxx_2` SET `end` = 1, `updateTime` = now() WHERE `userId`= 186435331 AND `type` = 2 AND `pId`=132434347351202675 AND `end` = 0
2021-09-02T17:29:31.342867+08:00 10137065 [Note] InnoDB: *** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 329 page no 2076 n bits 112 index PRIMARY of table `d_xxx_feed`.`t_user_xxx_2` trx id 54557981 lock_mode X locks rec but not gap
Record lock, heap no 33 PHYSICAL RECORD: n_fields 24; compact format; info bits 0
0: len 8; hex 956e50cb8d06b6fb; asc nP ;;
1: len 6; hex 000003307520; asc 0u ;;
2: len 7; hex b0000000320110; asc 2 ;;
3: len 8; hex 800000006f1fc602; asc o ;;
4: len 4; hex 80000002; asc ;;
5: len 24; hex e69c89e4b880e8b5b7e8818ae5a4a9e79a84e59097efbc9f; asc ;;
6: len 2; hex 5b5d; asc [];;
7: len 4; hex 80000000; asc ;;
8: len 8; hex 8000000000000000; asc ;;
9: len 8; hex 8000000000000000; asc ;;
10: len 30; hex 7b22706c6f744964223a313534343236303535373335313230323637352c; asc {"plotId":1544260557351202675,; (total 233 bytes);
11: len 12; hex e58f98e68081e4b8bbe4baba; asc ;;
12: len 30; hex 68747470733a2f2f73747564696f696d67627373646c2e636c6f75642e6b; asc https://xxximgbssdl.cloud.k; (total 75 bytes);
13: len 4; hex 80000002; asc ;;
14: len 8; hex 95551c4bc84d1d5b; asc U K M [;;
15: len 0; hex ; asc ;;
16: len 5; hex 99aa828508; asc ;;
17: len 5; hex 99aa828507; asc ;;
18: len 4; hex 80000000; asc ;;
19: len 8; hex 956e50cb8990af73; asc nP s;;
20: len 1; hex 80; asc ;;
21: len 1; hex 80; asc ;;
22: len 1; hex 81; asc ;;
23: len 1; hex 80; asc ;;

2021-09-02T17:29:31.345307+08:00 10137065 [Note] InnoDB: *** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 329 page no 574 n bits 680 index idx_userId_type of table `d_xxx_feed`.`t_user_xxx_2` trx id 54557981 lock_mode X locks rec but not gap waiting
Record lock, heap no 567 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 8; hex 800000006f1fc602; asc o ;;
1: len 4; hex 80000002; asc ;;
2: len 8; hex 956e50cb8d06b6fb; asc nP ;;

2021-09-02T17:29:31.345666+08:00 10137065 [Note] InnoDB: *** WE ROLL BACK TRANSACTION (1)

表结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
CREATE TABLE `t_user_xxx_2` (
`id` bigint(20) NOT NULL COMMENT 'xxId',
`userId` bigint(20) DEFAULT '0' COMMENT '发布人userId',
`type` int(11) DEFAULT '0' COMMENT 'xxx',
`content` varchar(4000) DEFAULT '' COMMENT '文字',
`images` varchar(4000) DEFAULT '' COMMENT '图片',
`deleted` int(11) NOT NULL DEFAULT '1' COMMENT '动态状态,0:新增,1:删除',
`commentCnt` bigint(20) DEFAULT '0' COMMENT '评论数',
`likeCnt` bigint(20) DEFAULT '0' COMMENT '点赞数',
`contentExt` varchar(4000) DEFAULT '' COMMENT '内容扩展',
`nickname` varchar(255) DEFAULT '' COMMENT '昵称',
`userLogo` varchar(255) DEFAULT '' COMMENT '头像',
`userType` int(11) DEFAULT '0' COMMENT 'xxx',
`rId` bigint(20) DEFAULT '0' COMMENT 'xxxid',
`remark` varchar(255) DEFAULT '' COMMENT '备注',
`createTime` datetime DEFAULT NULL COMMENT '创建时间',
`updateTime` datetime DEFAULT NULL COMMENT '更新时间',
`roomId` int(11) NOT NULL DEFAULT '0' COMMENT '房间id',
`pId` bigint(20) NOT NULL DEFAULT '0' COMMENT 'xxid',
`end` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否结束',
`hideType` tinyint(4) NOT NULL DEFAULT '0' COMMENT '隐藏类型:xxx',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '审核状态:0审核中,1审核通过,2审核不通过',
`contentStatus` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0不违规,1违规',
PRIMARY KEY (`id`),
KEY `idx_userId_type` (`userId`,`type`) USING BTREE,
KEY `idx_createTime` (`createTime`) USING BTREE,
KEY `idx_type` (`type`) USING BTREE,
KEY `idx_user_rId` (`userId`,`rId`) USING BTREE,
KEY `idx_pId` (`pId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='以动态发布者userId模10分表'

四种类型的行锁

  • 记录锁,间隙锁,Next-key 锁和插入意向锁。这四种锁对应的死锁日志各不相同,如下:
    • 记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
    • 间隙锁(LOCK_GAP): lock_mode X locks gap before rec
    • Next-key 锁(LOCK_ORNIDARY): lock_mode X
    • 插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention

分析

  • 通过explain分析explain SELECT 1 FROM t_user_xxx_2 WHERE userId = xxxx AND type = 2 AND pId = 434444 得出以下
  • 语句的type是index_merge,Extra的信息是Using intersect(idx_user_type,idx_pId),执行计划走了index_merge优化,单个语句通过两个索引(idx_userId_type,idx_pId)来提取记录集合并取交集获得最终结果集。

解决方式

  1. 关闭:index_merge_intersection=off (解决思路:直接关闭MySQL的优化)
  2. 删掉多余的索引idx_pId(解决思路:建立合理的索引)— 此案例中,根据业务实际情况,使用该方案解决
  3. 先查,再使用主键或唯一索引来更新(解决思路:缩小索引范围) (推荐)
  4. 强制走idx_userId_type 索引
  5. 添加userId + type + pId的组合索引,这样就可以避免掉index merge

Reference

  • [MySQL update use index merge(Using intersect) increase chances for deadlock]
    (https://developer.aliyun.com/article/8963)
  • https://dev.mysql.com/doc/refman/8.0/en/index-merge-optimization.html#index-merge-intersection

Scrum笔记

发表于 2022-04-02
  • https://www.jianshu.com/p/7d3e2b86c737

  • Scrum是一个过程框架,自上世纪90年代初以来,它就已经被应用于管理复杂产品的开发上。Scrum并不是构建产品的一种过程或一项技术

  • Scrum的精髓在于小团队。

  • Scrum基于经验过程控制理论,或称之为经验主义。

  • Scrum规定了4个正式事件,用于检视与适应上,这4个事件在Scrum事件章节中会加以描述:

    Sprint计划会议
    每日Scrum站会
    Sprint评审会议
    Sprint回顾会议

  • Scrum团队由一名产品负责人、开发团队和一名ScrumMaster组成。Scrum团队是跨职能的自组织团队。自组织团队自己选择如何以最好的方式来完成工作,而不是由团队之外的人来指导。跨职能团队拥有完成工作所需的全部技能,不需要依赖团队之外的人。Scrum的团队模式乃是设计用来提供最佳的灵活性、创造力和生产力。

    Scrum团队迭代增量式地交付产品,籍此最大化获得反馈的机会。增量式交付“完成”的产品保证了一个可工作产品的潜在可用版本总是存在。

  • ScrumMaster服务于产品负责人
    ScrumMaster服务于开发团队
    ScrumMaster服务于组织

  • Scrum事件
    Sprint除了本身作为一个事件以外,它还是其他所有事件的容器

  • Sprint是Scrum的核心,其长度(持续时间)为一个月或更短时间的限时,在这段时间内构建一个“完成的”、可用的和潜在可发布的产品增量。在整个开发过程期间,Sprint的长度通常保持一致。前一个Sprint结束后,新的下一个Sprint紧接着立即开始。

    Sprint由Sprint计划会议、每日Scrum站会、开发工作、Sprint评审会议和Sprint回顾会议构成。

  • 取消Sprint

Java代码段查阅

发表于 2022-04-01

集合处理

List集合拼接成以逗号分隔的字符串

  1. list.stream().collect(Collectors.joining(","));
  2. String.join(",", list)

两个List集合取交集

  • list1.retainAll(list2)

交集、并集、差集

1
2
3
4
5
6
/*交集*/
List<AClass> intersectResult = aClassList1.stream().filter(aClassList2::contains).collect(Collectors.toList());
/*并集*/
Stream.of(list1, list2).flatMap(Collection::stream).collect(Collectors.toList());
/*差集*/
List<AClass> differenceResult = aClassList1.stream().filter(x -> !aClassList2.contains(x)).collect(Collectors.toList());

使用parallelStream时要注意线程安全问题

  • 在并发时使用HashMap和ArrayList等非线程安全的类是会存在问题的
  • 应使用
    1. list合并:
      1
      2
      3
      4
      Stream.of().parallel()
      .mapToObj(index -> {
      return Collections.<T>emptyList();
      }).flatMap(Collection::stream).collect(Collectors.toList());
    2. map合并:
      1
      2
      3
      4
      5
      6
      Stream.of().parallel()
      .mapToObj(index -> {
      return Collections.<K, V>emptyMap();
      })
      .flatMap(x -> x.entrySet().stream())
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1));

排序

  • list.sort(Comparator.comparing(XXX::getXX, Comparator.reverseOrder()).thenComparing(XXX::getXXX))
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    list.sort(
    Comparator.comparing((Function<XXXBO, Integer>)xxxBO -> {
    if (ids.contains(xxxBO.getId())) {
    return 0;
    }
    return 1;
    }).thenComparing(xxxBO -> {
    if (ptIds.contains(xxxBO.getTId())) {
    return 0;
    }
    return 1;
    })
    .thenComparing(XXXBO::getAddTime, Comparator.reverseOrder())
    .thenComparing(XXXBO::getId, Comparator.reverseOrder()));

字符串

首字母转成大写

  • StringUtils.capitalize(str);

重复拼接字符串

  • StringUtils.repeat("ab", 2);

字符串分割

1
2
3
4
5
6
7
Iterable<String> split = Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.split(sourceStr);
return Sets.newHashSet(split)

Sets.newHashSet(Splitter.on(",").omitEmptyStrings().trimResults().splitToList(topics));

日期

Date类型转String类型

  • String date = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");

String类型转Date类型

  • Date date = DateUtils.parseDate("2021-05-01 01:01:01", "yyyy-MM-dd HH:mm:ss");

计算一个小时后的日期

  • Date date = DateUtils.addHours(new Date(), 1);

java8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
long tomorrowZeroTime = LocalDate.now().plusDays(1).atTime(LocalTime.MIN).atZone(ZoneId.systemDefault()).toEpochSecond();
long days = (expireZeroTime - nowZeroTime) / SECONDS_PER_DAY;
long beginTime = LocalDateTime.now().plusHours(-2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long endTime = LocalDateTime.now().plusHours(-1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long exportStartTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).minusDays(1).atZone(ZoneId.systemDefault()).toEpochSecond();
long exportStartTime =LocalDateTime.now().withHours(0).plusHours(-1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
Long millisecond = Instant.now().toEpochMilli(); // 精确到毫秒
Long second = Instant.now().getEpochSecond();// 精确到秒
LocalDateTime.ofInstant(Instant.ofEpochMilli(time),ZoneId.systemDefault())
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate dt = LocalDate.parse("20191020", formatter);
dt.plusDays(1);
String day = dt.format(formatter);
//Java8的DateTimeFormatter是线程安全的,而SimpleDateFormat并不是线程安全。

开源库

commons-collections4

1
2
3
4
5
6
// 两个集合取交集
Collection<String> collection = CollectionUtils.retainAll(listA, listB);
// 两个集合取并集
Collection<String> collection = CollectionUtils.union(listA, listB);
// 两个集合取差集
Collection<String> collection = CollectionUtils.subtract(listA, listB);

common-beanutils 操作对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
User user = new User();
BeanUtils.setProperty(user, "id", 1);
BeanUtils.setProperty(user, "name", "yideng");
System.out.println(BeanUtils.getProperty(user, "name")); // 输出 yideng
System.out.println(user); // 输出 {"id":1,"name":"yideng"}
对象和map互转

// 对象转map
Map<String, String> map = BeanUtils.describe(user);
System.out.println(map); // 输出 {"id":"1","name":"yideng"}
// map转对象
User newUser = new User();
BeanUtils.populate(newUser, map);
System.out.println(newUser); // 输出 {"id":1,"name":"yideng"}

commons-io 文件流处理

1
2
3
4
5
6
7
8
9
10
File file = new File("demo1.txt");
// 读取文件
List<String> lines = FileUtils.readLines(file, Charset.defaultCharset());

FileUtils.readFileToString(new File("xxx"));
// 写入文件
FileUtils.writeLines(new File("demo2.txt"), lines);
// 复制文件
FileUtils.copyFile(srcFile, destFile);

Google Guava 工具类库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//创建集合
List<String> list = Lists.newArrayList();
List<Integer> list = Lists.newArrayList(1, 2, 3);
// 反转list
List<Integer> reverse = Lists.reverse(list);
System.out.println(reverse); // 输出 [3, 2, 1]
// list集合元素太多,可以分成若干个集合,每个集合10个元素
List<List<Integer>> partition = Lists.partition(list, 10);

Map<String, String> map = Maps.newHashMap();
Set<String> set = Sets.newHashSet();

//Multimap 一个key可以映射多个value的HashMap
Multimap<String, Integer> map = ArrayListMultimap.create();
map.put("key", 1);
map.put("key", 2);
Collection<Integer> values = map.get("key");
System.out.println(map); // 输出 {"key":[1,2]}
// 还能返回你以前使用的臃肿的Map
Map<String, Collection<Integer>> collectionMap = map.asMap();

//BiMap 一种连value也不能重复的HashMap
BiMap<String, String> biMap = HashBiMap.create();
// 如果value重复,put方法会抛异常,除非用forcePut方法
biMap.put("key","value");
System.out.println(biMap); // 输出 {"key":"value"}
// 既然value不能重复,何不实现个翻转key/value的方法,已经有了
BiMap<String, String> inverse = biMap.inverse();
System.out.println(inverse); // 输出 {"value":"key"}

//Table 一种有两个key的HashMap
// 一批用户,同时按年龄和性别分组
Table<Integer, String, String> table = HashBasedTable.create();
table.put(18, "男", "yideng");
table.put(18, "女", "Lily");
System.out.println(table.get(18, "男")); // 输出 yideng
// 这其实是一个二维的Map,可以查看行数据
Map<String, String> row = table.row(18);
System.out.println(row); // 输出 {"男":"yideng","女":"Lily"}
// 查看列数据
Map<Integer, String> column = table.column("男");
System.out.println(column); // 输出 {18:"yideng"}
// Multiset 一种用来计数的Set
Multiset<String> multiset = HashMultiset.create();
multiset.add("apple");
multiset.add("apple");
multiset.add("orange");
System.out.println(multiset.count("apple")); // 输出 2
// 查看去重的元素
Set<String> set = multiset.elementSet();
System.out.println(set); // 输出 ["orange","apple"]
// 还能查看没有去重的元素
Iterator<String> iterator = multiset.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 还能手动设置某个元素出现的次数
multiset.setCount("apple", 5);

其他

比较两个对象是否相等

  • Objects.equals(strA, strB); (防止空指针)

异步

1.

1
2
3
CompletableFuture.supplyAsync(
() -> {
});

2.

1
2
CompletableFuture.runAsync(() -> {
});

重写toString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this,
ToStringStyle.SHORT_PREFIX_STYLE);
}

@Override
public String toString() {
ReflectionToStringBuilder builder = new ReflectionToStringBuilder(
this, ToStringStyle.SHORT_PREFIX_STYLE) {
@Override
protected boolean accept(Field field) {
return !"createTime".equals(field.getName()) &&
!field.getName().equals("updateTime") &&
!field.getName().equals("xxx");
}
};
return builder.toString();
}

Stream

1
2
3
4
5
6
Map<String, String> requestMap =
request.getParameterMap().entrySet().stream().collect(
Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue()[0], (v1, v2) -> {
throw new RuntimeException(String.format("Duplicate key for values %s and %s", v1, v2));
},
TreeMap::new));

Optional

1
.orElseThrow(() -> new ContextedRuntimeException("notExist").addContextValue("id", id));

Optional中map和flatMap

  • https://blog.csdn.net/qq_28988969/article/details/80995927
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class FlightTicketInfo {

private String orderNumber;

public String getOrderNumber() {
return orderNumber;
}

}

/**
* desc :
* create_user : cheng
* create_date : 2018/7/4 11:21
*/
public class OptionalTest {

@Test
public void testMap() {
FlightTicketInfo flightTicketInfo = null;

Optional<Optional<String>> s1 = Optional.ofNullable(flightTicketInfo).map(OptionalTest::getOrderNumber);

Optional<String> s2 = Optional.ofNullable(flightTicketInfo).map(FlightTicketInfo::getOrderNumber);

Optional<String> s3 = Optional.ofNullable(flightTicketInfo).flatMap(OptionalTest::getOrderNumber);
}

private static Optional<String> getOrderNumber(FlightTicketInfo flightTicketInfo) {
return Optional.ofNullable(flightTicketInfo).map(f -> f.getOrderNumber());
}

}
  • 1.9新增 or()方法是作为orElse()和orElseGet()方法的改进而出现的,使用方法一致,但后两个方法在执行完成后返回的并非包装值。如果需要执行一些逻辑并返回Optional时,可以使用or()方法。该方法传入Supplier接口的实例,当value有值时直接返回自身Optional,当为空时,自动执行suuplier的get()方法,并包装成Optional返回,其源码中包装的语句如下:
1
2
Optional<T> r = (Optional<T>) supplier.get();
return Objects.requireNonNull(r);
  • stream()方法则不用多说,是一个提供给流式编程使用的方法,功能上是一个适配器,将Optional转换成Stream:没有值返回一个空的stream,或者包含一个Optional的stream。其源码如下:
1
2
3
4
5
if (!isPresent()) {
return Stream.empty();
} else {
return Stream.of(value);
}
  • 其原因是orElseGet()的参数是Supplier目标类型的函数,简单来说,Suppiler接口类似Spring的懒加载,声明之后并不会占用内存,只有执行了get()方法之后,才会调用构造方法创建出对象,而orElse()是快加载,即使没有调用,也会实际的运行。
    这个特性在一些简单的方法上差距不大,但是当方法是一些执行密集型的调用时,比如远程调用,计算类或者查询类方法时,会损耗一定的性能。
    orElseThrow()方法与orElseGet()方法的参数都是Supplier参数都是函数类型的,这意味着这两种方法都是懒加载,但针对于必须要使用异常控制流程的场景,orElseThrow()会更加合适,因为可以控制异常类型,使得相比NPE会有更丰富的语义。

BigDecimal

  • user.getMoney().stripTrailingZeros().toPlainString()
1
2
3
4
5
6
public BigDecimal sum(Function<XXX, BigDecimal> get, List<XXX> list) {
return list.stream().map(get).reduce((acc, item) -> {
acc = acc.add(item);
return acc;
}).get().divide(BigDecimal.valueOf(100));
}

包装临时对象

  • 当一个方法需要返回两个及以上字段时,可以使用Pair和Triple
  • 返回两个字段
    ImmutablePair<Integer, String> pair = ImmutablePair.of(1, "yideng");
  • 返回三个字段
    ImmutableTriple<Integer, String, Date> triple = ImmutableTriple.of(1, "yideng", new Date());

临时文件返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@RestController
public class XXXController {

@GetMapping(value = "/xxdata.txt")
public void bidata(HttpServletResponse response) {

String content = "success|=|333\nsuccess|=|333";
try {
response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.addHeader("Content-Disposition","attachment;filename=xxdata.txt");
OutputStream output = response.getOutputStream();
output.write(content.getBytes(Charset.forName("UTF-8")));
output.flush();
} catch (Exception e) {
e.printStackTrace();
}
}

@GetMapping(value = "/xxdata.txt")
public String bidata(HttpServletResponse response) {

response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.addHeader("Content-Disposition","attachment;filename=xxdata.txt");
return "success|=|333\nsuccess|=|333";

}

@GetMapping(value = "/xxvideo/{uri}")
public void proxy(@PathVariable("uri") String uri, HttpServletResponse response) {
try {

response.setHeader("Access-Control-Allow-Origin", "*.xx.cn, *.xx.xx.com");

OutputStream output = response.getOutputStream();
String url = xxxService.getRealUrl(uri);

if (StringUtils.isBlank(url)) {
response.setStatus(HttpStatus.NOT_FOUND_404);
return;
}
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(forwardProxyHost, forwardProxyPort));
try (InputStream input = new URL(url).openConnection(proxy).getInputStream()) {
IOUtils.copy(input, output);
} finally {
}
} catch (Exception e) {
LOGGER.error("error,uri:{}", uri, e);
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
}
}
}

线程池

1
ExecutorService executor = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors() * 2, new BasicThreadFactory.Builder().namingPattern("common-schedule-pool-%d").daemon(true).build()); 
  • Java线程池中三种方式创建 ThreadFactory 设置线程名称: https://blog.csdn.net/u010648555/article/details/106137206

扫描注解

1
2
3
4
5
6
7
8
9
10

Reflections reflections = new Reflections(new ConfigurationBuilder()
.addUrls(ClasspathHelper.forPackage("com.xxx"))
.setScanners(new MethodAnnotationsScanner()));

for (final Method method : reflections.getMethodsAnnotatedWith(xxxxx.class)) {
Class<?> clazz = method.getDeclaringClass();
Collection<?> beans = applicationContext.getBeansOfType(clazz).values();
int size = beans.size();
}
1
2
3
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>

并发

1
2
3
4
5
 CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
return true;
});
CompletableFuture<Boolean> future2 = CompletableFuture.supplyAsync(() -> true);
CompletableFuture<Boolean> future3 = CompletableFuture.supplyAsync(() -> true);
1
2
3
4
5
6
7
8
9
List<Supplier<Boolean>> tasks = new ArrayList<>(2);
tasks.add(() -> {
return true;
});

tasks.add(() -> {
return true;
});
tasks.stream().parallel().forEach(Supplier::get);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
CompletableFuture<CallResult> future1 = CompletableFuture.supplyAsync(() -> {
return CallResult.FAIL;
});

CompletableFuture<CallResult> future2 = CompletableFuture.supplyAsync(() -> {
return CallResult.FAIL;
});

List<CallResult> resultList = Stream.of(future1, future2).map(CompletableFuture::join).collect(
Collectors.toList());

if (resultList.stream().filter(v -> CallResult.FAIL.equals(v)).count() > 0) {
throw new Exception(" call exception");
}
1
2
3
return IntStream.range(0, DBTableConfig.SIZE).parallel()
.mapToObj(index -> mapper.selectAllList(index, Date.from(Instant.ofEpochSecond(startTime)),
Date.from(Instant.ofEpochSecond(endTime)), xxx, Arrays.asList(xx, xx), limit)).flatMap(Collection::stream).collect(Collectors.toList());
1
2
3
4
5
6
7
8
9
CompletableFuture<Void> cf6 = CompletableFuture.allOf(cf3, cf4, cf5);
CompletableFuture<String> result = cf6.thenApply(v -> {
//这里的join并不会阻塞,因为传给thenApply的函数是在CF3、CF4、CF5全部完成时,才会执行 。
result3 = cf3.join();
result4 = cf4.join();
result5 = cf5.join();
//根据result3、result4、result5组装最终result;
return "result";
});

开源类库

  • apache commons
  • commons-lang3
  • commons-collections

Web

1
2
3
4
5
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    /**
* curl -XPOST -F "materialFile=@/D:/Downloads/5c1.FBX" "http://127.0.0.1:19201/addMaterial"
* @param materialFile
* @return
*/
@CheckToken(false)
@PostMapping("/addMaterial")
public ApplicationResponse addMaterial(@RequestParam("materialFile") MultipartFile materialFile
) {
return ApplicationResponse.ok().build();
}

spring.http.multipart.maxFileSize=10MB
spring.http.multipart.maxRequestSize=10MB
  • long 前端失精问题
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Bean("jackson2ObjectMapperBuilderCustomizer")
    @ConditionalOnProperty(name = "config.long2string", havingValue = "true", matchIfMissing = true)
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    Jackson2ObjectMapperBuilderCustomizer customizer = new Jackson2ObjectMapperBuilderCustomizer() {
    @Override
    public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
    jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
    .serializerByType(Long.TYPE, ToStringSerializer.instance);
    }
    };
    return customizer;
    }

MyBatis

1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="queryBookInfo" parameterType="com.tjt.platform.entity.BookInfo" resultType="java.lang.Integer">
select count(*) from t_rule_BookInfo t
<where>
<if test="title !=null and title !='' ">
title = #{title}
</if>
<if test="author !=null and author !='' ">
AND author = #{author}
</if>
</where>
</select>

UPDATE 操作也一样,可以用标记代替 1=1。
1
2
3
4
5
6
7
8
9
10
11
12
13
@Select({
"<script>",
"SELECT " + FIELDS,
"FROM `t_xxxx_task` t",
"WHERE createTime &gt;= #{startTime} AND createTime &lt; #{endTime} ",
"<when test = 'statusList != null and statusList.size > 0'>",
"AND `status` in (<foreach collection = 'statusList' item = 'status' index='index' open='' close='' separator=','>#{status}</foreach>)",
"</when>",
"LIMIT #{limit}",
"</script>"
})
List<TxxxxTask> selectList(@Param("startTime")Date startTime, @Param("endTime")Date endTime, @Param("statusList") List<Integer> statusList, @Param("limit") int limit);

Reference

  • 实现同样逻辑,代码量减少90%,Java程序员必会的工具库
  • CompletableFuture原理与实践-外卖商家端API的异步化

轻量级微服务公共库设计

发表于 2022-03-22

目的

  • 减少拷贝代码,抽象公共业务组件和复用,快速支持产品需求

  • 提升开发效率,增强排查能力

  • 统一维护,提升代码质量,减少重复错误,提高服务可控性

  • 公共嵌入式的sdk,其实有点类似一个轻量级的网关,sidecar,localproxy

  • 为什么不使用API网关?
    1. 有一定的机器和维护成本
    2. 跟组织架构有一定关系

关于轻量级分布式事件通知的思考

发表于 2022-03-12
  • 有些业务场景,我们需要发出一个事件,通知到每一个进程。
    比如数据变更,通知每个进程更新本地缓存。

  • 使用MQ的话,以NSQ为例,将channel名设置为ip,那么每个进程都会收到这个事件;但是目前现在是k8s的时代,使用k8s部署进程,那么会导致某些旧ip的channel仍然存在,从而造成事件堆积。

  • redis虽然支持事件,但是微服务时代,不太建议每个业务的微服务都连同一个redis(同业务内的通知可以考虑);使用一个公共的zk获取是一个可以考虑的方案,各服务启动时watch相应的节点即可。

经典排序算法

发表于 2022-02-28
  • 十大经典排序算法(动图演示)

排序算法分类

(一)

  • 内部排序和外部排序

(二)

  1. 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。(非线性时间)
  2. 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。 (线性时间)

(三)

  1. 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。如:快速排序、归并排序、堆排序、冒泡排序等。在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。
  2. 线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。如:计数排序、基数排序、桶排序。非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组arr,计算arr之前有多少个元素,则唯一确定了arr在排序后数组中的位置。

(四)

  1. 比较类排序
    • 交换排序 (swap)
      • 冒泡排序 (bubble Sort) - 双重遍历,相邻元素两两比较交换
      • 快速排序 (quick Sort) - 较复杂
    • 插入排序 (insertion)
      • 简单插入排序 (insertion sort) - 分左右两批,遍历有序列表,将无序的元素插入有序的列表
      • 希尔排序 (shell sort) (缩小增量排序) - 较复杂
    • 选择排序 (selection)
      • 简单选择排序 (selection) - 双重遍历,每次找到最小的元素,最后和该趟遍历的第一个元素进行交换
      • 堆排序 (heap sort)
    • 归并排序 (merge sort) - 即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。 (递归)
      • 二路归并排序
      • 多路归并排序
  2. 非比较类排序
    • 计数排序 (counting sort)
    • 桶排序 (bucket sort)
    • 基数排序 (radix sort)(分配排序)

(五)

  • 算法复杂度
排序方法 时间复杂度(平均) 时间复杂度(最坏) 时间复杂度(最好) 空间复杂度 稳定性
冒泡排序 O(n^2) O(n^2) O(n) O(1) 稳定
快速排序 O(nlog2n) O(n^2) O(nlog2n) O(nlog2n) 不稳定
简单插入排序 O(n^2) O(n^2) O(n) O(1) 稳定
希尔排序 O(n1.3) O(n^2) O(n) O(1) 不稳定
简单选择排序 O(n^2) O(n^2) O(n^2) O(1) 不稳定
堆排序 O(nlog2n) O(nlog2n) O(nlog2n) O(1) 不稳定
归并排序 O(nlog2n) O(nlog2n) O(nlog2n) O(n) 稳定
计数排序 O(n+k) O(n+k) O(n+k) O(n+k) 稳定
桶排序 O(n+k) O(n^2) O(n) O(n+k) 稳定
基数排序 O(n*k) O(n*k) O(n*k) O(n+k) 稳定
  • 相关概念:

1、时间复杂度

  • 时间复杂度可以认为是对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
  • 常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n), 线性对数阶O(nlog2n),平方阶O(n2)
  • 时间复杂度O(1):算法中语句执行次数为一个常数,则时间复杂度为O(1),

2、空间复杂度

  • 空间复杂度是指算法在计算机内执行时所需存储空间的度量,它也是问题规模n的函数
  • 空间复杂度O(1):当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1)
  • 空间复杂度O(log2N):当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为O(log2n), ax=N,则x=logaN,
  • 空间复杂度O(n):当一个算法的空间复杂度与n成线性比例关系时,可表示为0(n).

复杂度分析

  • 常见排序算法分类(排序算法,是算法的基石,你掌握多少?)
  • 大O时间复杂度表示法:表示代码执行时间随数据规模增长的变化趋势,又称渐进时间复杂度
  • 大O空间复杂度表示法:表示代码执行所占的内存空间随数据规模增长的变化趋势,又称渐进空间复杂度
  • 复杂度分析法则
    • 单段代码,看循环的次数。
    • 多段代码,看代码循环量级。
    • 嵌套代码求乘积,比如递归和多重循环。
    • 多个规模求加法,方法中并行的两个循环。
  • 常用的复杂度级别
    • 多项式阶:随着数据规模的增长,算法的执行时间和空间占用,按照多项式的比例增长,包括,O(1)(常数阶)、O(logn)(对数阶)、O(n)(线性阶)、O(nlogn)(线性对数阶)、O(n2)(平方阶)、O(n3)(立方阶)。
    • 非多项式阶:随着数据规模的增长,算法的执行时间和空间占用暴增,这列算法性能极差。包括,O(2^n)(指数阶)、O(n!)(阶乘阶)

(六)

  • 稳定性
  • 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,A1=A2,且A1在A2之前,而在排序后的序列中,A1仍在A2之前,则称这种排序算法是稳定的;否则称为不稳定的。
  • 稳定也可以理解为一切皆在掌握中,元素的位置处在你在控制中.而不稳定算法有时就有点碰运气,随机的成分.当两元素相等时它们的位置在排序后可能仍然相同.但也可能不同.是未可知的.
  • 不稳定排序算法
    • 快选希堆(快速排序,选择排序,希尔排序,堆排序)

(七)

  • 算法总结
  • 所需辅助空间最多:归并排序;
    所需辅助空间最少:堆排序;
    平均速度最快:快速排序;
    不稳定:快速排序,希尔排序,堆排序。

冒泡排序

  • 1、冒泡排序是一种用时间换空间的排序方法,n小时好
  • 2、最坏情况是把顺序的排列变成逆序,或者把逆序的数列变成顺序,最差时间复杂度O(N^2)只是表示其操作次数的数量级
  • 3、最好的情况是数据本来就有序,复杂度为O(n)

快速排序

  • 1、n大时好,快速排序比较占用内存,内存随n的增大而增大,但却是效率高不稳定的排序算法。
  • 2、划分之后一边是一个,一边是n-1个,
    这种极端情况的时间复杂度就是O(N^2)
  • 3、最好的情况是每次都能均匀的划分序列,O(N*log2N)

归并排序

  • 1、n大时好,归并比较占用内存,内存随n的增大而增大,但却是效率高且稳定的排序算法。

Reference

  • 十大经典排序算法(动图演示)
  • Go语言经典排序算法实现
  • [算法总结] 十大排序算法
  • 排序算法的稳定性
  • 各种排序算法时间复杂度
  • 常见排序算法分类(排序算法,是算法的基石,你掌握多少?) !!!
<1…789…16>

153 日志
165 标签
RSS
© 2025 Kingson Wu
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4