民工哥

民工哥 查看完整档案

其它编辑  |  填写毕业院校不知名互联网企业  |  不知名程序员 编辑 github.com/mingongge 编辑
编辑

民工哥,10多年职场老司机的经验分享,坚持自学一路从技术小白成长为互联网企业信息技术部门的负责人。

我的新书:《Linux系统运维指南》

微信公众号:民工哥技术之路

民工哥:知乎专栏

欢迎关注,我们一同交流,相互学习,共同成长!!

个人动态

民工哥 发布了文章 · 10月25日

弃用Notepad++,还有 5 款更牛逼的选择!

作为文本编辑工具,有比 Notepad++ 更好的替代工具:

Sublime Text (非开源)

Sublime Text 是一个轻量、简洁、高效、跨平台的编辑器。

Sublime Text 的特色功能:

  • 良好的扩展功能,官方称之为安装包(Package)。
  • 右边没有滚动条,取而代之的是代码缩略图,这个功能非常赞
  • 强大的快捷命令“可以实时搜索到相应的命令、选项、snippet 和 syntex, 按下回车就-可以直接执行,减少了查找的麻烦。”
  • 即时的文件切换。
  • 随心所欲的跳转到任意文件的任意位置。
  • 多重选择(Multi-Selection)功能允许在页面中同时存在多个光标。
  • 支持 VIM 模式
  • 支持宏,简单地说就是把操作录制下来或者自己编写命令,然后播放刚才录制的操作或者命令。
  • 更新非常勤快

Visual Studio Code

Visual Studio Code 是一个运行于 OS X,Windows 和 Linux 之上的,针对于编写现代 Web 和云应用的跨平台编辑器。

Github Atom

Atom 是 GitHub 专门为程序员推出的一个跨平台文本编辑器。具有简洁和直观的图形用户界面,并有很多有趣的特点:支持 CSS,HTML,JavaScript 等网页编程语言。它支持宏,自动完成分屏功能,集成了文件管理器。

HBuilder

HBuilder 是DCloud(数字天堂)推出一款支持HTML5的Web开发IDE。“快,是HBuilder的最大优势,通过完整的语法提示和代码输入法、代码块及很多配套,HBuilder能大幅提升HTML、js、css的开发效率。

BowPad

BowPad 是一个带有功能区 UI 的简单而快速的文本编辑器

显著特性:

  • 超过100种文件类型和语言的语法高亮显示
  • 处理许多不同的编码,包括 UTF-8、UTF-16 甚至 UTF-32
  • 垂直滚动条中的导航提示
  • 根据路径对打开的标签进行着色
  • 可用 JScript 或 VBScript 编写的插件扩展

Editra

Editra是一个支持多平台的文本编辑器,可以支持基本语法和二十种语言。它使用方便,可以用颜色标注重点部分,支持进行内嵌式编辑,也可以进行代码编辑。

原文:https://www.oschina.net/news/...

image

查看原文

赞 0 收藏 0 评论 1

民工哥 发布了文章 · 10月25日

Nginx + Spring Boot 实现负载均衡


本文来源:http://8rr.co/LSUH

前言

本篇文章主要介绍的是Nginx如何实现负载均衡。

负载均衡介绍

在介绍Nginx的负载均衡实现之前,先简单的说下负载均衡的分类,主要分为硬件负载均衡和软件负载均衡,硬件负载均衡是使用专门的软件和硬件相结合的设备,设备商会提供完整成熟的解决方案,比如F5,在数据的稳定性以及安全性来说非常可靠,但是相比软件而言造价会更加昂贵;软件的负载均衡以Nginx这类软件为主,实现的一种消息队列分发机制。

简单来说所谓的负载均衡就是把很多请求进行分流,将他们分配到不同的服务器去处理。比如我有3个服务器,分别为A、B、C,然后使用Nginx进行负载均衡,使用轮询策略,此时如果收到了9个请求,那么会均匀的将这9个请求分发给A、B、Cf服务器,每一个服务器处理3个请求,这样的话我们可以利用多台机器集群的特性减少单个服务器的压力。

Nginx实现负载均衡的示例图:

负载均衡策略

NGINX开源支持四种负载平衡方法,而NGINX Plus又增加了两种方法。

1.Round Robin: 对所有的请求进行轮询发送请求,默认的分配方式。

nginx.conf 配置示例:

upstream xuwujing {
   server www.panchengming.com;
   server www.panchengming2.com;
}

注:上面的域名也可以用IP替代。

2.Least Connections:以最少的活动连接数将请求发送到服务器,同样要考虑服务器权重。

nginx.conf 配置示例:

upstream xuwujing {
    least_conn;
    server www.panchengming.com;
    server www.panchengming2.com;
}

3.IP Hash : 发送请求的服务器由客户机IP地址决定。在这种情况下,使用IPv4地址的前三个字节或整个IPv6地址来计算散列值。该方法保证来自相同地址的请求到达相同的服务器,除非该服务器不可用。

upstream xuwujing {
     ip_hash;
     server www.panchengming.com;
     server www.panchengming2.com;
}

4.Generic Hash: 请求发送到的服务器由用户定义的键决定,该键可以是文本字符串、变量或组合。

upstream xuwujing {
     hash $request_uri consistent;
     server www.panchengming.com;
        server www.panchengming2.com;
 }

5.Least Time (NGINX Plus only) – 对于每个请求,NGINX Plus选择具有最低平均延迟和最低活动连接数的服务器,其中最低平均延迟是根据包含least_time指令的下列参数计算的:

  • header :从服务器接收第一个字节的时间。
  • last_byte:从服务器接收完整响应的时间。
  • last_byte inflight:从服务器接收完整响应的时间。

    upstream xuwujing { least_time header; server www.panchengming.com; server www.panchengming2.com; }

6.Random:每个请求将被传递到随机选择的服务器。如果指定了两个参数,首先,NGINX根据服务器权重随机选择两个服务器,然后使用指定的方法选择其中一个。

  • least_conn :活动连接的最少数量
  • least_time=header (NGINX Plus):从服务器接收响应标头的最短平均时间 ($upstream_header_time)。
  • least_time=last_byte (NGINX Plus) :从服务器接收完整响应的最短平均时间($upstream_response_time)。

    upstream xuwujing {
    random two least_time=last_byte;
    server www.panchengming.com;
    server www.panchengming2.com;
    }

Nginx+SpringBoot实现负载均衡

环境准备

  • 依赖JDK1.8以上的版本;
  • 依赖Nginx环境;

这里的项目就用本人之前的一个springboot项目,SpringBoot的项目地址: https://github.com/xuwujing/s...

首先我们下载这个项目,输入:mvn clean package 将项目进行打包为jar文件,然后将application.properties和此jar项目放在一个文件夹中,然后复制该文件夹(这里为了清晰所以进行复制,实际不复制更改端口重启也行),修改复制文件夹application.properties的端口,比如改为8086。

Nginx 配置

我们找到nginx的配置文件nginx.conf,该配置在nginx/conf/nginx.conf目录下,然后我们来修改该配置,新增如下配置:

upstream pancm{
   server 127.0.0.1:8085;
   server 127.0.0.1:8086;
}
  • upstream pancm:定义一个名称,随意就行;
  • server + ip:端口 or 域名;

如果不想使用Round Robin策略,也可以换成其他的。

然后在server添加/修改如下配置:

server {
        listen       80;
        server_name  127.0.0.1;
        location / {
            root   html;
            proxy_pass http://pancm;
            proxy_connect_timeout 3s;
            proxy_read_timeout 5s;
            proxy_send_timeout 3s;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

配置说明:

  • server: 虚拟主机的名称,一个http中可以配置多个server;
  • listen:Nginx默认的端口;
  • server_name:Nginx服务的地址,可以使用域名,多个用空格分隔。
  • proxy_pass:代理路径,一般配置upstream后面的名称用于实现负载均衡,可以直接配置ip进行跳转;

nginx.conf 完整的配置:

events {
    worker_connections  1024;
}
error_log nginx-error.log info;
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
      upstream pancm{
       server 127.0.0.1:8085;
       server 127.0.0.1:8086;
    }
    
    server {
        listen       80;
        server_name  127.0.0.1;
        location / {
            root   html;
            proxy_pass http://pancm;
            proxy_connect_timeout 3s;
            proxy_read_timeout 5s;
            proxy_send_timeout 3s;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

负载均衡测试

在完成Nginx配置之后,我们启动Nginx。linux输入/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf,如果已经启动可以使用/usr/local/nginx/sbin/nginx -s reload命令进行热加载配置文件,Windows直接点击Nginx目录下的nginx.exe或者 cmd运行start nginx进行启动,如果启动了依旧可以使用nginx -s reload进行热加载。

Nginx启动完成之后,我们依次启动刚刚下载的springboot和复制更改端口的项目,输入:java -jar springboot-jsp-thymeleaf.jar启动。

都启动成功之后,我们在浏览器输入服务的ip即可进行访问。

示例图:

注:这里我使用的是windows系统做测试,实际linux也是一样的。

然后我们进行操作,并查看控制台日志!

从上述示例图中我们进行4次界面刷新请求,最终平均分配到两个服务中去了,从上述的测试结果中我们实现了负载均衡。

这里我在说一下使用Nginx的注意事项,在进行学习和测试的时候,使用nginx默认的端口实现负载均衡一般没有什么问题,但是当我们在项目中使用的时候,特别有登录界面的并且端口不是80的时候,会出现登录的界面无法跳转,进行调试的话会出现 net::ERR_NAME_NOT_RESOLVED这样的错误,出现这个原因的是因为nginx默认的端口是80,那么默认跳转的也是这个,所以出现这种情况的时候,需要在location 下添加proxy_set_header Host $host:port 这个配置,port 和listen 的端口保持一致就可以了。

image

查看原文

赞 3 收藏 3 评论 0

民工哥 发布了文章 · 10月24日

1024节快乐!!955 不加班的公司名单分享给大家

与 996.ICU 相呼应,955 公司白名单。旨在让更多的人逃离 996,加入 955 的行列。

996 工作制:即每天早 9 点到岗,一直工作到晚上 9 点。每周工作 6 天。

955 工作制:即每天早 9 点到岗,一直工作到下午 5 点。每周工作 5 天。

11-5-5 工作制:即每天上午 11 点到岗,一直工作到下午 5 点。每周工作 5 天。

996.ICU: 工作 996,生病 ICU。

955.WLB: 工作 955,work–life balance (工作与生活的平衡)。

1155.Life: 工作 11-5-5,生活为先

说明

对于 955 名单中的公司,基本不属于 996 的公司,相对接近 955/965 的水平,但是依旧要看部门和地区,不能保证完全准确性。

  • 不能保证所有部门都是 955/10-6-5/965/10-7-5 的水平
  • 就算部门的平均水平是 955/10-6-5/965/10-7-5,也有可能部分时间要加班
  • 名单中部分公司的部分部门可能有 975 甚至 985 的情况,不要以偏概全
  • 名单中部分公司的部分甚至较多部门可能有 945、10-5-5 或者 11-6-5 的情况,我们也把他们加入在名单中
  • 目前名单上的公司在上海的居多,是因为作者身处上海,对于上海的情况相对了解。并不意味着 955 的公司大多都在上海。欢迎童鞋们继续投票,我会根据投票情况,加入更多公司。

955 的公司名单

其中,加粗的为新增公司。

  • Amazon - 北京/上海
  • AMD - 上海
  • Airbnb - 北京
  • Apple - 北京/上海
  • Autodesk - 北京/上海
  • Booking - 上海
  • Citrix - 南京
  • Cisco - 北京/上海/杭州/苏州
  • Coolapk (酷安) - 北京/深圳
  • Douban (豆瓣) - 北京
  • eBay - 上海
  • EMC - 上海
  • Ericsson - 上海
  • GE - 上海
  • Google - 北京/上海
  • Grab - 北京
  • Honeywell - 上海
  • HP - 上海
  • HSBC - 上海/广州/西安
  • Hulu - 北京
  • IBM - 上海 (GBS除外)
  • iHerb - 上海
  • Intel - 上海
  • LeetCode - 上海
  • Linkedin - 北京
  • Microsoft - 北京/上海/苏州
  • MicroStrategy - 杭州
  • National Instruments - 上海
  • NVIDIA - 北京/上海
  • Oracle - 上海
  • PayPal - 上海
  • Pivotal - 北京/上海
  • Red Hat - 北京/上海/深圳
  • RingCentral - 厦门
  • SAP - 上海
  • Shopee - 深圳
  • Splunk - 上海
  • SUSE - 北京/上海/深圳
  • ThoughtWorks - 西安/北京/深圳/成都/武汉/上海/香港
  • Trend Micro - 南京
  • Ubisoft - 上海
  • Unity - 上海
  • Vipshop (唯品会) - 上海
  • VMware - 北京/上海
  • WeWork - 上海
  • Works Applications - 上海
  • Zhihu (知乎) - 北京
  • Zoom - 合肥/杭州/苏州
内容来源:https://github.com/formulahen...

image

查看原文

赞 5 收藏 3 评论 0

民工哥 发布了文章 · 10月23日

什么是 SRE?一文详解 SRE 运维体系

可观测性系统

在任何有一定规模的企业内部,一旦推行起来整个SRE的运维模式,那么对于可观测性系统的建设将变得尤为重要,而在整个可观测性系统中,通常我们会分为如下三个方面:

  • 指标监控:即各种指标监控,比如基础资源指标,服务性能指标,业务的调用指标。
  • 日志:各种设备以及服务的运行日志监控。
  • 调用链:业务层面的调用链分析,通常在分布式系统中帮助运营、开发以及运维人员快速识别整体调用的瓶颈点

一整套的可观测系统,它能确保你洞察系统,跟踪系统的健康状态、可用性以及系统内部发生的事情。

对于整个可观测系统的建设,需要注意如下两点:

  • 确定质量标准是什么,并确保系统持续逼近或保持在质量标准极限范围内
  • 系统地关注这项工作—而不应该只是随机地查看一下系统

在整个企业级可观测系统中,我认为至少应该包括如下几个特征:

  • 完备指标采集:可以对接企业内大部分的设备与技术栈相应的监控指标;同时,支持常见设备的监控指标体系,可以快速接入监控设备和指标,避免所有设备监控都是从头构建;对于日志数据的采集支持
  • 海量设备支持:企业IT系统数量和规模越来越大,因此监控系统比以前需要监控海量设备监控。
  • 监控数据存储和分析:监控数据是运维分析、运维自动化和智能化的基础,因此海量监控数据存储以及基于监控数据的可视化分析是一个监控系统的基本能力。
  • 可观测系统是整个运维体系的基础,它需要提供整个运维体系的数据化支持。

因此,一个企业级的可观测性系统应该是平台化的。一方面可以通过配置或者开发实现更多 运维指标的接入;另一方面,亦可对接更多的专业运维工具,整合并打通多元的运维数据,为更多运维场景提供数据服务。从整体上,可观测性系统为企业运维提供了一个数据基础,让我们对事故响应以及容量预测等方面更多使用数据而非凭借以往经验和拍脑袋做出决策。

故障响应

如果有什么东西出了故障,该如何提醒大家并做出回应?工具可以帮助解决这个问题,国为它可以定义提醒人类的规则。

故障响应是建立在使用可观测性系统构建的数据之上,并借助反馈循环,来帮助我们加强对服务的监控。

故障响应通常包含如下几个动作:

  • 关注: 不论是主动发现瓶颈点或异常点,还是通过可观测性系统被动暴露瓶颈点,我们都应该进行主动关注
  • 交流: 及时将观察到风险点通知到相关方,并告知影响面以及相关的补救措施
  • 恢复: 三方达成一致后,根据补救措施进行修复相关风险点和异常点

需要注意的是,如果在前期整个可观测性系统能够做好,通常故障应当始于一个简单的告警信息或一个报障电话,因此,通常情况下,可观测系统做的足够好仅能起到追溯和排查的作用,但是无法起到及时发现的作用,此时就需要依赖于各个观测数据进行计算和评估告警,以及时将相关的告警通知到相关人,以暴露风险点。

告警只是整个故障响应的第一个环节,解决的是故障如何发现的问题,而大多数的故障响应工作都是关于定义处理策略和提供培训的,以便人们在收到警报时知道该怎么做,通常这部分更多的是过去历史经验和运维经历的总结和沉淀,包括经验的一些抽象和工具化沉淀,以保证故障响应的效率和普遍化(即不依赖人为经验)。

而对于整个告警系统来说,需要确保的是告警的有效性,否则,整个报警系统很有可能沦落为垃圾数据制造机,告警有效性意味着需要满足如下两个需求:

  • 告警及时性: 系统有问题需要及时通过告警信息告知运维处理人员及时处理告警;
  • 告警准确性: 只要有告警信息系统必然出现问题(对于很多企业可能存在大量的无用告警,比如磁盘问题,mem等相关问题,当然这里涉及到了自动化、业务形态、告警阈值的问题);

在整个运维过程中,我们经常会发现有大量的无关紧要的告警信息,让运维人员的注意力迷失在告警海洋当中,而通常非运维领域的领导会关注整个告警的响应程度,因此,抑制和消除无效的告警,让运维人员不被告警风暴所吞没,也是告警管理中重点建设的内容。

通常情况,在我们的各个可观测系统构建完成后,可以通过整合到监控平台中的各种监控数据,应用趋势预测、短周期检测、间歇性恢复、基线判断、重复压缩等算法和手段实现告警压缩收敛,强化告警的有效性。

同时,面向一线的运维人员,我们需要根据同一个系统或设备的多个监控指标进行综合性建模和分析,汇总成一个健康度的分值,给予一线运维人员系统的基于健康度的系统分层评价体系,真实、直观反映系统运行状态,实现问题快速定界。

比如,通过基础资源的多个指标进行综合加权计算来整体评估该资源的利用率;通过一个应用关联的全部资源的资源利用率以及应用的运维架构整体建模分析来计算一个分值来整体评估该应用的健康程度。

这个过程如果做得成熟一些,可以根据内部已有的解决方案和告警进行闭环打通,一个简单的场景就是,当磁盘满时,告警会首先触发一次标准化的磁盘巡检,并进行相关的可丢弃数据的删除,如果依然无法解决该报警,下次可直接关联到一线运维进行人工干预,之后进行标准化经验总结。

故障复盘

故障复盘就是对于过去的一些服务异常和服务中断情况进行回顾和总结,以确保相同问题下次不会再出现。为了让大家团结协作,我们希望建立一种无指责、透明的事后文化。个人不应该害怕事故,而是确信如果事故发生,团队将会响应和改进系统。

备注: 其实在国内的SRE文化中,一般只有对大型,对业务有重大影响的事故才会进行复盘,但实际上如果在时间和经历允许的情况下,对于一般的普通事故也应该在小范围进行复盘,正所谓大的故障都是从不断的小问题一点一点积累的。另外,其实对于运维相关的个人而言,我们也应当及时的进行小故障复盘,以不断加强个人的故障处理和修复能力。

我认为SRE的一个关键共识正是承认了系统的不完美性,追求永不停机的系统是不现实的。基于不完美系统,我们无可避免要面对和经历系统故障与失败。

所以我们重要的并非找到为这个故障责任的这个人或者那个人,而是更应该创根问底地复盘这个故障和失败的根本原因是什么,以及如何避免再次出现相同的故障。系统可靠性是整个团队共同奋斗的方向,从失败中快速恢复并吸取教训,每个人放心地提出问题,应对停机,并努力改进系统。

备注: 通常很多企业内部在故障复盘过程中,相关人员可能将故障和失败的根因追溯 不经意间 当做了故障定责和一系列的惩罚措施,通过一些惩戒措施来强行约定故障的发生,这种方式往往是非常不可取的,试想每个人都不想出现事故,要么是认知之外,要么是规则缺陷,永远没有一个人明知会有故障而偏偏去制造故障的。

需要牢记的是: 故障是我们可以从中学习的东西,而不是让人害怕和羞耻的事情!

在日常运维过程中,出现故障等事故对于我们而言其实是一个很好的复盘学习机会。通过历史监控数据,分析事故其中的根本原因,制定后续应对策略,并且通过运维平台将这些应对策略编辑成标准化、可重用、自动化的运维应用场景,为后续相同问题的处理提供标准且快捷的解决方案。这正是事后回顾这个过程最真实的价值体现。

测试与发布

测试与发布对于整个稳定性和可靠性的主要出于一个预防的作用,预防是指尝试限制发生的事故数量,并确保在发布新代码时基础架构和服务能够保持稳定。

作为一个长期从事运维工作的人,可能内心中最为恐惧的莫过于新应用版本发布。因为除了硬件和网络设备损坏这个属于天灾级别的概率事件外,新应用版本发布的第二天通常是停机与事故的高危期。所以,对于一些量级较大的产品通常会在节假日以及重要活动前夕进行封网操作,以避免新版本上线而导致的业务bug出现。

而测试是在成本和风险之间找到适当的平衡活动。如果过于冒险,你们可能就会疲于应付系统失败;反过来说,如果你太保守,你就不能足够快地发布新东西,让企业在市场上生存下来。

在错误预算比较多(即在一段时间内故障导致系统停机时长较少)的情况下,可以适当减少测试资源并放宽系统上线的测试和条件,让业务可以有更多的功能上线,以保持业务的敏态;在错误预算比较少(即在一段时间内故障导致系统停机时长较多)的情况下,则要增加测试资源并收紧系统上线的测试,让系统的潜在风险得到更多有效的释放,避免系统停机保持系统的稳态。这种敏态与稳态之间的平衡,需要整个运维与开发团队来共同承担。

除了测试外,应用发布也是一项运维团队通常要承担的责任。SRE的一个原则是将一切可以重复性劳动代码化和工具化;此外,应用发布的复杂程度往往与系统的复杂程度成正比。因此在应用系统上规模企业,往往已经着手基于自动化框架构建自动化的应用发布过程。

通过自动化发布工具,我们可以构建流水线实现部署的过程中所有的操作(如编译打包、测试发布、生产准备、告警屏蔽、服务停止、数据库执行、应用部署、服务重启等)全部自动化。

容量规划

容量规划是关于预测未来和发现系统极限的,容量规划也是为了确保系统可以随着时间的推移得到完善和增强。

规划的主要目标是管理风险和期望,对于容量规划,涉及到将容量扩展到整个业务;所关注的期望是人们在看到业务增长时期望服务如何响应。风险是在额外的基础设施上花费时间和金钱来处理这个问题。

容量规划首先是对未来预测性的分析与判断,其预测的基础正是海量的运维数据。因此,容量规划除了有相应的架构和规划团队外,一个全面的运维数据中心是实现系统容量规划的必须设施。

容量趋势预警和分析将综合地从各种运维监控、流程管理等数据源中收集、整理、清洗并结构化地存储各种运维数据,将这些来自于各种工具的运维数据打通融合并且构建各种数据主题。

应用这些数据主题的数据用于帮助运维人员对问题进行评估,包括:

  • 当前的容量是多少
  • 何时达到容量极限
  • 应该如何更改容量
  • 执行容量规划

运维平台除了可以提供必要的数据支持外,还需要提供必要的数据可视化支持能力。运维数据可视化提供了一些必要的能力保障运维人员可以更好地利用其中的运维数据评估容量。

首先,运维平台需要有极强的数据检索能力。运维平台存储着海量的运维数据,运维人员为了尝试建立和验证一个探索性场景的时候,往往多次反复检索和查询特定数据。如果运维数据分析平台的数据查询很慢或者查询角度很少的情况下,运维人员建立场景的时间就会拖得很长甚至进行不下去。因此,运维人员可通过平台可以实现关键字、统计函数、单条件、多条件、模糊多维度查找功能,以及实现海量数据秒级查询,才能更有效帮助运维人员更便捷分析数据。

其二,平台需要强大的数据可视化能力。人们常说“千言万语不及一图”,运维人员经常会通过各系统的运维数据进行统计分析并生成各类实时报表,对各类运维数据(如应用日志、交易日志、系统日志)进行多维度、多角度深入分析、预测及可视化展现,将他们分析的预测结果和经验向他人表达和推广。

自动化工具开发

SRE不仅涉及运营,还涉及软件开发,当然这部分指的是和运维以及SRE领域相关的工具和平台开发。在Google的SRE体系中,SRE工程师将花费大约一半的时间来开发新的工具和服务,这些工具的一部分用于自动化一些手动任务,而其他部分用于来不断填补和修复整个SRE体系内部的其他系统。

通过编写代码把自己和其他人从重复的工作中解放出来,如果我们不需要人类来完成任务,那么就编写代码,这样人类就不需要参与其中了。

SRE从内心上鄙视重复性的工作,将从原有的人工加被动响应,转变为更高效、更为自动化的运维体系。

自动化运维框架:

自动化运维工具的优势和必要性:

  • 提高效率: 由程序自动化操作,有效地降低运维人力资源的投入,也让运维人员的精力得以释放并投向更为重要的领域。
  • 操作的标准化: 将原来许多复杂、易错的手工操作实现统一运维操作入口,实现运维操作白屏化,提升运维操作的可管理性;

    同时,减少由于运维人员情绪带来手工误操作,避免“从删库到跑路”这样的悲剧的发生。

  • 运维经验能力的传承: 运维自动化工具将原来许多运维团队积累的经验以代码方式总结为各种运维工具,实现自动化和白屏化的运维操作。运维团队的后来者,可以有效地继承、重复使用并优化它们。这种代码化的工作传承,将个人能力转变为团队能力,并减少人员流动带来对工作的影响。

构建自动化运维体系就必须以运维场景为基础,这些运维场景是在本企业内反复迭代和打造,是企业中最常用的运维场景。

比如常见的运维场景:软件安装部署、应用发布交付、资产管理、告警自动处理、故障分析、资源申请、自动化巡检等等。因此,整个自动化运维体系建设时也应支持多种不同类型的自动化作业配置能力,通过简单的脚本开发、场景配置和可视化定制流程实现更多运维场景的实现。

用户体验

用户体验这一层要说的是,作为SRE来讲,从用户的角度来保证业务的稳定性和可用性才是最终目标。这个才传统意义上的运维人员是不会关注这一点的,因为大家通常只会考虑到我底层运维的系统或底层资源是否稳定,但实际上整个业务的稳定才是SRE需要关心的问题,而业务的稳定性和可用性通常需要站在用户的角度来模拟和衡量整体的可用性和可靠性。

在前面提到的所有SRE相关的工作范畴,无论是监控、事故响应、回顾、测试与发布、容量规划以及构建自动化工具,无非都是为了提供更好的系统用户业务体验而服务的。因此,我们在运维的过程中无不需要注意关注系统的用户体验。

而在实际运维工作中,我们往往可以通过应用日志、监控数据、业务拔测等业务相关的用户体验信息。在运维数据平台中,通过这些用户体验监测数据之间的关联和串联,重现用户的最终业务调用链路以及各应用环节对性能数据的关系。最终形成从业务用户体验数据入手,逐步实现系统运行状态数据、设备运行状态数据链路的打通,让运维体系实现以最终用户体验为中心的目标。

这些用户体验的信息,对于运维团队掌握客户整体的用户体验情况、系统可用性的监测以及系统针对性的优化提供着无可替代的作用。

其实,SRE运维体系更为强调以用户的体验为核心,以自动化和运维数据为手段,实现应用业务连续性保障,从这个点出发,我们会发现和以往的传统运维还是有很大的区别的,我们不再仅仅是单纯的安装和部署工程师,我们需要通过一系列的技术手段来不断保障上层业务的稳定性和可靠性。

_来自:BGBiao的SRE人生
链接:https://bgbiao.top/post/sre运...

image

查看原文

赞 1 收藏 1 评论 0

民工哥 回答了问题 · 10月22日

解决把服务器yum删除了 怎么重新安装yum?

第一:什么样的操作删除的?
第二:可以去的机器将这个文件拷贝过来
第三:重新安装

关注 3 回答 2

民工哥 发布了文章 · 10月22日

打造一款高逼格的 Vim 神器

Vim 是一个上古神器,本篇文章主要持续总结使用 Vim 的过程中不得不了解的一些指令和注意事项,以及持续分享一个前端工作者不得不安装的一些插件,而关于 Vim 的简介,主题的选择,以及为何使用 vim-plug 来管理插件等内容,有兴趣的同学下来可以自己了解和对比下。

安装

sudo apt-getinstall vim// Ubuntu

其他平台,可以自行谷歌。

新手指南

vimtutor// vim 教程

上面是史上最简单,最全面的Vim基础教程,至今无人超越。

下面是作者基于上面的归纳:

移动光标

# hjkl
# 2w 向前移动两个单词
# 3e 向前移动到第 3 个单词的末尾
# 0 移动到行首
# $ 当前行的末尾
# gg 文件第一行
# G 文件最后一行
# 行号+G 指定行
# <ctrl>+o 跳转回之前的位置
# <ctrl>+i 返回跳转之前的位置

退出

# <esc> 进入正常模式
# :q! 不保存退出
# :wq 保存后退出

删除

# x 删除当前字符
# dw 删除至当前单词末尾
# de 删除至当前单词末尾,包括当前字符
# d$ 删除至当前行尾
# dd 删除整行
# 2dd 删除两行

修改

# i 插入文本
# A 当前行末尾添加
# r 替换当前字符
# o 打开新的一行并进入插入模式

撤销

# u 撤销
# <ctrl>+r 取消撤销
复制粘贴剪切
# v 进入可视模式
# y 复制
# p 粘贴
# yy 复制当前行
# dd 剪切当前行

状态

#<ctrl>+g 显示当前行以及文件信息
查找
# / 正向查找(n:继续查找,N:相反方向继续查找)
# ? 逆向查找
# % 查找配对的 {,[,(
# :set ic 忽略大小写
# :set noic 取消忽略大小写
# :set hls 匹配项高亮显示
# :set is 显示部分匹配

替换

# :s/old/new 替换该行第一个匹配串
# :s/old/new/g 替换全行的匹配串
# :%s/old/new/g 替换整个文件的匹配串

折叠

# zc 折叠
# zC 折叠所有嵌套
# zo 展开折叠
# zO 展开所有折叠嵌套

执行外部命令

# :!shell 执行外部命令

.vimrc

.vimrc 是 Vim 的配置文件,需要我们自己创建:

cd Home               // 进入 Home 目录
touch .vimrc          // 配置文件
# Unix
# vim-plug
# Vim
curl -fLo ~/.vim/autoload/plug.vim --create-dirs 
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
# Neovim
curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs 
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

其他平台,可以查看 vim-plug[1]。

基本配置

取消备份
set nobackup
set noswapfile
文件编码
setencoding=utf-8
显示行号
setnumber
取消换行
setnowrap
显示光标当前位置
setruler
设置缩进
set cindent
set tabstop=2
set shiftwidth=2
突出显示当前行
setcursorline
查找
set ic
set hls
set is

左下角显示当前vim模式

setshowmode
代码折叠
#启动 vim 时关闭折叠代码
set nofoldenable
主题
syntax enable
set background=dark
colorscheme solarized

◈ altercation/vim-colors-solarized[2]
◈ Anthony25/gnome-terminal-colors-solarized[3]

插件配置

树形目录
Plug 'scrooloose/nerdtree'
Plug 'jistr/vim-nerdtree-tabs'
Plug 'Xuyuanp/nerdtree-git-plugin'
autocmd vimenter * NERDTree
map <C-n> :NERDTreeToggle<CR>
let NERDTreeShowHidden=1
let g:NERDTreeShowIgnoredStatus = 1
let g:nerdtree_tabs_open_on_console_startup=1
let g:NERDTreeIndicatorMapCustom = {
     "Modified"  : "✹",
     "Staged"    : "✚",
     "Untracked" : "✭",
     "Renamed"   : "➜",
     "Unmerged"  : "═",
     "Deleted"   : "✖",
     "Dirty"     : "✗",
     "Clean"     : "✔︎",
     'Ignored'   : '☒',
     "Unknown"   : "?"
     }
# o 打开关闭文件或目录
# e 以文件管理的方式打开选中的目录
# t 在标签页中打开
# T 在标签页中打开,但光标仍然留在 NERDTree
# r 刷新光标所在的目录
# R 刷新当前根路径
# X 收起所有目录
# p 小写,跳转到光标所在的上一级路径
# P 大写,跳转到当前根路径
# J 到第一个节点
# K 到最后一个节点
# I 显示隐藏文件
# m 显示文件操作菜单
# C 将根路径设置为光标所在的目录
# u 设置上级目录为根路径
# ctrl + w + w 光标自动在左右侧窗口切换
# ctrl + w + r 移动当前窗口的布局位置
# :tabc 关闭当前的 tab
# :tabo   关闭所有其他的 tab
# :tabp   前一个 tab
# :tabn   后一个 tab
# gT      前一个 tab
# gt      后一个 tab

◈ scrooloose/nerdtree[4]
◈ vim-nerdtree-tabs[5]
◈ nerdtree-git-plugin[6]

代码,引号,路径补全
Plug 'Valloric/YouCompleteMe'
Plug 'Raimondi/delimitMate'
Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }

◈ Valloric/YouCompleteMe[7]
◈ Raimondi/delimitMate[8]
◈ Shougo/deoplete.nvim[9]

语法高亮,检查
Plug 'sheerun/vim-polyglot'
Plug 'w0rp/ale'
let g:ale_linters = {
    'javascript': ['eslint'],
    'css': ['stylelint'],
}
let g:ale_fixers = {
    'javascript': ['eslint'],
    'css': ['stylelint'],
}
let g:ale_fix_on_save = 1
let g:ale_sign_column_always = 1
let g:ale_sign_error = '●'
let g:ale_sign_warning = '▶'
nmap <silent> <C-k> <Plug>(ale_previous_wrap)
nmap <silent> <C-j> <Plug>(ale_next_wrap)

◈ w0rp/ale[10]
◈ sheerun/vim-polyglot[11]

文件,代码搜索
Plug 'rking/ag.vim'
Plug 'kien/ctrlp.vim'

◈ kien/ctrlp.vim[12]
◈ ggreer/the_silver_searcher[13]
◈ rking/ag.vim[14]

加强版状态栏
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
let g:airline_theme='papercolor'

◈ vim-airline/vim-airline[15]
◈ vim-airline/vim-airline-themes[16]

代码注释
Plug 'scrooloose/nerdcommenter'
# <leader>cc // 注释
# <leader>cm 只用一组符号注释
# <leader>cA 在行尾添加注释
# <leader>c$ /* 注释 */
# <leader>cs /* 块注释 */
# <leader>cy 注释并复制
# <leader>c<space> 注释/取消注释
# <leader>ca 切换 // 和 /* */
# <leader>cu 取消注释
let g:NERDSpaceDelims = 1
let g:NERDDefaultAlign = 'left'
let g:NERDCustomDelimiters = {
             'javascript': { 'left': '//', 'leftAlt': '/**', 'rightAlt': '*/' },
             'less': { 'left': '/**', 'right': '*/' }
         }

◈ scrooloose/nerdcommenter[17]

git

Plug 'airblade/vim-gitgutter'
Plug 'tpope/vim-fugitive'

◈ airblade/vim-gitgutter[18]
◈ tpope/vim-fugitive[19]

Markdown

Plug 'suan/vim-instant-markdown'
let g:instant_markdown_slow = 1
let g:instant_markdown_autostart = 0
# :InstantMarkdownPreview

◈ suan/vim-instant-markdown[20]

Emmet

Plug 'mattn/emmet-vim'
let g:user_emmet_leader_key='<Tab>'
let g:user_emmet_settings = {
          'javascript.jsx' : {
             'extends' : 'jsx',
          },
       }

◈ mattn/emmet-vim[21]

html 5

Plug'othree/html5.vim'

◈ othree/html5.vim[22]

css 3

Plug 'hail2u/vim-css3-syntax'
Plug 'ap/vim-css-color'
augroup VimCSS3Syntax
  autocmd!
  autocmd FileType css setlocal iskeyword+=-
augroup END

◈ hail2u/vim-css3-syntax[23]
◈ ap/vim-css-color[24]

JavaScipt

Plug 'pangloss/vim-javascript'
let g:javascript_plugin_jsdoc = 1
let g:javascript_plugin_ngdoc = 1
let g:javascript_plugin_flow = 1
set foldmethod=syntax
let g:javascript_conceal_function             = "ƒ"
let g:javascript_conceal_null                 = "ø"
let g:javascript_conceal_this                 = "@"
let g:javascript_conceal_return               = "⇚"
let g:javascript_conceal_undefined            = "¿"
let g:javascript_conceal_NaN                  = "ℕ"
let g:javascript_conceal_prototype            = "¶"
let g:javascript_conceal_static               = "•"
let g:javascript_conceal_super                = "Ω"
let g:javascript_conceal_arrow_function       = "⇒"
let g:javascript_conceal_noarg_arrow_function = " "
let g:javascript_conceal_underscore_arrow_function = " "
set conceallevel=1

◈ pangloss/vim-javascript[25]
(注:上述脚本中存在特殊字符,有的情况下显示不正确,请直接用上述链接的内容。)

React

Plug 'mxw/vim-jsx'
let g:jsx_ext_required = 0

◈ mxw/vim-jsx[26]

Prettier

Plug 'prettier/vim-prettier', {
   'do': 'yarn install',
   'for': ['javascript', 'typescript', 'css', 'less', 'scss', 'json', 'graphql'] }
let g:prettier#config#bracket_spacing = 'true'
let g:prettier#config#jsx_bracket_same_line = 'false'
let g:prettier#autoformat = 0
autocmd BufWritePre *.js,*.jsx,*.mjs,*.ts,*.tsx,*.css,*.less,*.scss,*.json,*.graphql PrettierAsync
# :Prettier

◈ prettier/vim-prettier[27]

总结

最后,呈上参考配置 .vimrc[28],如果关于 vim 有更好的 idea,欢迎在评论中交流。


作者:枫上雾棋
链接:https://segmentfault.com/a/11...

image

查看原文

赞 3 收藏 2 评论 1

民工哥 发布了文章 · 10月22日

8 种常见 SQL 错误用法

来源:yq.aliyun.com/articles/72501


1、LIMIT 语句

分页查询是最常用的场景之一,但也通常也是最容易出问题的地方。比如对于下面简单的语句,一般 DBA 想到的办法是在 type, name, create_time 字段上加组合索引。这样条件排序都能有效的利用到索引,性能迅速提升。

SELECT * 
FROM   operation 
WHERE  type = 'SQLStats' 
 AND name = 'SlowLog' 
ORDER  BY create_time 
LIMIT  1000, 10;

好吧,可能90%以上的 DBA 解决该问题就到此为止。但当 LIMIT 子句变成 “LIMIT 1000000,10” 时,程序员仍然会抱怨:我只取10条记录为什么还是慢?

要知道数据库也并不知道第1000000条记录从什么地方开始,即使有索引也需要从头计算一次。出现这种性能问题,多数情形下是程序员偷懒了。

在前端数据浏览翻页,或者大数据分批导出等场景下,是可以将上一页的最大值当成参数作为查询条件的。SQL 重新设计如下:

SELECT   * 
FROM     operation 
WHERE    type = 'SQLStats' 
AND      name = 'SlowLog' 
AND      create_time > '2017-03-16 14:00:00' 
ORDER BY create_time limit 10;

在新设计下查询时间基本固定,不会随着数据量的增长而发生变化。

2、隐式转换

SQL语句中查询变量和字段定义类型不匹配是另一个常见的错误。比如下面的语句:

mysql> explain extended SELECT * 
 > FROM   my_balance b 
 > WHERE  b.bpn = 14000000123 
 >       AND b.isverified IS NULL ;
mysql> show warnings;
| Warning | 1739 | Cannot use ref access on index 'bpn' due to type or collation conversion on field 'bpn'

其中字段 bpn 的定义为 varchar(20),MySQL 的策略是将字符串转换为数字之后再比较。函数作用于表字段,索引失效。

上述情况可能是应用程序框架自动填入的参数,而不是程序员的原意。现在应用框架很多很繁杂,使用方便的同时也小心它可能给自己挖坑。

3、关联更新、删除

虽然 MySQL5.6 引入了物化特性,但需要特别注意它目前仅仅针对查询语句的优化。对于更新或删除需要手工重写成 JOIN。

比如下面 UPDATE 语句,MySQL 实际执行的是循环/嵌套子查询(DEPENDENT SUBQUERY),其执行时间可想而知。

UPDATE operation o 
SET    status = 'applying' 
WHERE  o.id IN (SELECT id 
 FROM   (SELECT o.id, 
 o.status 
 FROM   operation o 
 WHERE  o.group = 123 
 AND o.status NOT IN ( 'done' ) 
 ORDER  BY o.parent, 
 o.id 
 LIMIT  1) t);

执行计划:

+----+--------------------+-------+-------+---------------+---------+---------+-------+------+-----------------------------------------------------+
| id | select_type        | table | type  | possible_keys | key     | key_len | ref   | rows | Extra                                               |
+----+--------------------+-------+-------+---------------+---------+---------+-------+------+-----------------------------------------------------+
| 1  | PRIMARY            | o     | index |               | PRIMARY | 8       |       | 24   | Using where; Using temporary                        |
| 2  | DEPENDENT SUBQUERY |       |       |               |         |         |       |      | Impossible WHERE noticed after reading const tables |
| 3  | DERIVED            | o     | ref   | idx_2,idx_5   | idx_5   | 8       | const | 1    | Using where; Using filesort                         |
+----+--------------------+-------+-------+---------------+---------+---------+-------+------+-----------------------------------------------------+

重写为 JOIN 之后,子查询的选择模式从 DEPENDENT SUBQUERY 变成 DERIVED,执行速度大大加快,从7秒降低到2毫秒。

UPDATE operation o 
 JOIN  (SELECT o.id, 
 o.status 
 FROM   operation o 
 WHERE  o.group = 123 
 AND o.status NOT IN ( 'done' ) 
 ORDER  BY o.parent, 
 o.id 
 LIMIT  1) t
 ON o.id = t.id 
SET    status = 'applying'

执行计划简化为:

+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key   | key_len | ref   | rows | Extra                                               |
+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------------------------------+
| 1  | PRIMARY     |       |      |               |       |         |       |      | Impossible WHERE noticed after reading const tables |
| 2  | DERIVED     | o     | ref  | idx_2,idx_5   | idx_5 | 8       | const | 1    | Using where; Using filesort                         |
+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------------------------------+

4、混合排序

MySQL 不能利用索引进行混合排序。但在某些场景,还是有机会使用特殊方法提升性能的。

SELECT * 
FROM   my_order o 
 INNER JOIN my_appraise a ON a.orderid = o.id 
ORDER  BY a.is_reply ASC, 
 a.appraise_time DESC 
LIMIT  0, 20

执行计划显示为全表扫描:

+----+-------------+-------+--------+-------------+---------+---------+---------------+---------+-+
| id | select_type | table | type   | possible_keys     | key     | key_len | ref      | rows    | Extra 
+----+-------------+-------+--------+-------------+---------+---------+---------------+---------+-+
|  1 | SIMPLE      | a     | ALL    | idx_orderid | NULL    | NULL    | NULL    | 1967647 | Using filesort |
|  1 | SIMPLE      | o     | eq_ref | PRIMARY     | PRIMARY | 122     | a.orderid |       1 | NULL           |
+----+-------------+-------+--------+---------+---------+---------+-----------------+---------+-+

由于 is_reply 只有0和1两种状态,我们按照下面的方法重写后,执行时间从1.58秒降低到2毫秒。

SELECT * 
FROM   ((SELECT *
 FROM   my_order o 
 INNER JOIN my_appraise a 
 ON a.orderid = o.id 
 AND is_reply = 0 
 ORDER  BY appraise_time DESC 
 LIMIT  0, 20) 
 UNION ALL 
 (SELECT *
 FROM   my_order o 
 INNER JOIN my_appraise a 
 ON a.orderid = o.id 
 AND is_reply = 1 
 ORDER  BY appraise_time DESC 
 LIMIT  0, 20)) t 
ORDER  BY  is_reply ASC, 
 appraisetime DESC 
LIMIT  20;

5、EXISTS语句

MySQL 对待 EXISTS 子句时,仍然采用嵌套子查询的执行方式。如下面的 SQL 语句:

SELECT *
FROM   my_neighbor n 
 LEFT JOIN my_neighbor_apply sra 
 ON n.id = sra.neighbor_id 
 AND sra.user_id = 'xxx' 
WHERE  n.topic_status < 4 
 AND EXISTS(SELECT 1 
 FROM   message_info m 
 WHERE  n.id = m.neighbor_id 
 AND m.inuser = 'xxx') 
 AND n.topic_type <> 5

执行计划为:

+----+--------------------+-------+------+-----+------------------------------------------+---------+-------+---------+ -----+
| id | select_type        | table | type | possible_keys     | key   | key_len | ref   | rows    | Extra   |
+----+--------------------+-------+------+ -----+------------------------------------------+---------+-------+---------+ -----+
|  1 | PRIMARY            | n     | ALL  |  | NULL     | NULL    | NULL  | 1086041 | Using where                   |
|  1 | PRIMARY            | sra   | ref  |  | idx_user_id | 123     | const |       1 | Using where          |
|  2 | DEPENDENT SUBQUERY | m     | ref  |  | idx_message_info   | 122     | const |       1 | Using index condition; Using where |
+----+--------------------+-------+------+ -----+------------------------------------------+---------+-------+---------+ -----+

去掉 exists 更改为 join,能够避免嵌套子查询,将执行时间从1.93秒降低为1毫秒。

SELECT *
FROM   my_neighbor n 
 INNER JOIN message_info m 
 ON n.id = m.neighbor_id 
 AND m.inuser = 'xxx' 
 LEFT JOIN my_neighbor_apply sra 
 ON n.id = sra.neighbor_id 
 AND sra.user_id = 'xxx' 
WHERE  n.topic_status < 4 
 AND n.topic_type <> 5

新的执行计划:

+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+
| id | select_type | table | type   | possible_keys     | key       | key_len | ref   | rows | Extra                 |
+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+
|  1 | SIMPLE      | m     | ref    | | idx_message_info   | 122     | const    |    1 | Using index condition |
|  1 | SIMPLE      | n     | eq_ref | | PRIMARY   | 122     | ighbor_id |    1 | Using where      |
|  1 | SIMPLE      | sra   | ref    | | idx_user_id | 123     | const     |    1 | Using where           |
+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+

6、条件下推

外部查询条件不能够下推到复杂的视图或子查询的情况有:

  • 聚合子查询;
  • 含有 LIMIT 的子查询;
  • UNION 或 UNION ALL 子查询;
  • 输出字段中的子查询;

如下面的语句,从执行计划可以看出其条件作用于聚合子查询之后:

SELECT * 
FROM   (SELECT target, 
 Count(*) 
 FROM   operation 
 GROUP  BY target) t 
WHERE  target = 'rm-xxxx'
+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+
| id | select_type | table      | type  | possible_keys | key         | key_len | ref   | rows | Extra       |
+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+
|  1 | PRIMARY     | <derived2> | ref   | <auto_key0>   | <auto_key0> | 514     | const |    2 | Using where |
|  2 | DERIVED     | operation  | index | idx_4         | idx_4       | 519     | NULL  |   20 | Using index |
+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+

确定从语义上查询条件可以直接下推后,重写如下:

SELECT target, 
 Count(*) 
FROM   operation 
WHERE  target = 'rm-xxxx' 
GROUP  BY target

执行计划变为:

+----+-------------+-----------+------+---------------+-------+---------+-------+------+--------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+-------+---------+-------+------+--------------------+
| 1 | SIMPLE | operation | ref | idx_4 | idx_4 | 514 | const | 1 | Using where; Using index |
+----+-------------+-----------+------+---------------+-------+---------+-------+------+--------------------+

关于 MySQL 外部条件不能下推的详细解释说明请参考文章:

http://mysql.taobao.org/month...

7、提前缩小范围

先上初始 SQL 语句:

SELECT * 
FROM   my_order o 
 LEFT JOIN my_userinfo u 
 ON o.uid = u.uid
 LEFT JOIN my_productinfo p 
 ON o.pid = p.pid 
WHERE  ( o.display = 0 ) 
 AND ( o.ostaus = 1 ) 
ORDER  BY o.selltime DESC 
LIMIT  0, 15

该SQL语句原意是:先做一系列的左连接,然后排序取前15条记录。从执行计划也可以看出,最后一步估算排序记录数为90万,时间消耗为12秒。

+----+-------------+-------+--------+---------------+---------+---------+-----------------+--------+----------------------------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref             | rows   | Extra                                              |
+----+-------------+-------+--------+---------------+---------+---------+-----------------+--------+----------------------------------------------------+
|  1 | SIMPLE      | o     | ALL    | NULL          | NULL    | NULL    | NULL            | 909119 | Using where; Using temporary; Using filesort       |
|  1 | SIMPLE      | u     | eq_ref | PRIMARY       | PRIMARY | 4       | o.uid |      1 | NULL                                               |
|  1 | SIMPLE      | p     | ALL    | PRIMARY       | NULL    | NULL    | NULL            |      6 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+--------+---------------+---------+---------+-----------------+--------+----------------------------------------------------+

由于最后 WHERE 条件以及排序均针对最左主表,因此可以先对 my_order 排序提前缩小数据量再做左连接。SQL 重写后如下,执行时间缩小为1毫秒左右。

SELECT * 
FROM (
SELECT * 
FROM   my_order o 
WHERE  ( o.display = 0 ) 
 AND ( o.ostaus = 1 ) 
ORDER  BY o.selltime DESC 
LIMIT  0, 15
) o 
 LEFT JOIN my_userinfo u 
 ON o.uid = u.uid 
 LEFT JOIN my_productinfo p 
 ON o.pid = p.pid 
ORDER BY  o.selltime DESC
limit 0, 15

再检查执行计划:子查询物化后(select_type=DERIVED)参与 JOIN。虽然估算行扫描仍然为90万,但是利用了索引以及 LIMIT 子句后,实际执行时间变得很小。

+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows   | Extra                                              |
+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL    | NULL    | NULL  |     15 | Using temporary; Using filesort                    |
|  1 | PRIMARY     | u          | eq_ref | PRIMARY       | PRIMARY | 4       | o.uid |      1 | NULL                                               |
|  1 | PRIMARY     | p          | ALL    | PRIMARY       | NULL    | NULL    | NULL  |      6 | Using where; Using join buffer (Block Nested Loop) |
|  2 | DERIVED     | o          | index  | NULL          | idx_1   | 5       | NULL  | 909112 | Using where                                        |
+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+

8、中间结果集下推

再来看下面这个已经初步优化过的例子(左连接中的主表优先作用查询条件):

SELECT    a.*, 
 c.allocated 
FROM      ( 
 SELECT   resourceid 
 FROM     my_distribute d 
 WHERE    isdelete = 0 
 AND      cusmanagercode = '1234567' 
 ORDER BY salecode limit 20) a 
LEFT JOIN 
 ( 
 SELECT   resourcesid, sum(ifnull(allocation, 0) * 12345) allocated 
 FROM     my_resources 
 GROUP BY resourcesid) c 
ON        a.resourceid = c.resourcesid

那么该语句还存在其它问题吗?不难看出子查询 c 是全表聚合查询,在表数量特别大的情况下会导致整个语句的性能下降。

其实对于子查询 c,左连接最后结果集只关心能和主表 resourceid 能匹配的数据。因此我们可以重写语句如下,执行时间从原来的2秒下降到2毫秒。

SELECT    a.*, 
 c.allocated 
FROM      ( 
 SELECT   resourceid 
 FROM     my_distribute d 
 WHERE    isdelete = 0 
 AND      cusmanagercode = '1234567' 
 ORDER BY salecode limit 20) a 
LEFT JOIN 
 ( 
 SELECT   resourcesid, sum(ifnull(allocation, 0) * 12345) allocated 
 FROM     my_resources r, 
 ( 
 SELECT   resourceid 
 FROM     my_distribute d 
 WHERE    isdelete = 0 
 AND      cusmanagercode = '1234567' 
 ORDER BY salecode limit 20) a 
 WHERE    r.resourcesid = a.resourcesid 
 GROUP BY resourcesid) c 
ON        a.resourceid = c.resourcesid

但是子查询 a 在我们的SQL语句中出现了多次。这种写法不仅存在额外的开销,还使得整个语句显的繁杂。使用 WITH 语句再次重写:

WITH a AS 
( 
 SELECT   resourceid 
 FROM     my_distribute d 
 WHERE    isdelete = 0 
 AND      cusmanagercode = '1234567' 
 ORDER BY salecode limit 20)
SELECT    a.*, 
 c.allocated 
FROM      a 
LEFT JOIN 
 ( 
 SELECT   resourcesid, sum(ifnull(allocation, 0) * 12345) allocated 
 FROM     my_resources r, 
 a 
 WHERE    r.resourcesid = a.resourcesid 
 GROUP BY resourcesid) c 
ON        a.resourceid = c.resourcesid

总结

数据库编译器产生执行计划,决定着SQL的实际执行方式。但是编译器只是尽力服务,所有数据库的编译器都不是尽善尽美的。

上述提到的多数场景,在其它数据库中也存在性能问题。了解数据库编译器的特性,才能避规其短处,写出高性能的SQL语句。

程序员在设计数据模型以及编写SQL语句时,要把算法的思想或意识带进来。

编写复杂SQL语句要养成使用 WITH 语句的习惯。简洁且思路清晰的SQL语句也能减小数据库的负担 。

查看原文

赞 11 收藏 9 评论 1

民工哥 发布了文章 · 10月21日

Kubernetes 上对应用程序进行故障排除的 6 个技巧

从 Docker 迁移到 Docker Swarm,再到 Kubernetes,然后处理了多年来的所有各种 API 更改之后,我非常乐意发现部署中出现的问题和把问题进行修复。

我今天分享下我认为最有用的5条故障排除技巧,以及一些其他的使用技巧。

kubectl –“瑞士军刀”

kubectl 就是我们的瑞士军刀,我们经常在出现问题的时候使用他们,在出现问题如何使用他们很重要,让我们从5个“实际案例”开始,看出现问题时如何使用它们。

情况将是:我的YAML已被接受,但我的服务未启动且已启动,但无法正常工作。

1.kubectl get deployment/pods

这个命令如此重要的原因是它无需显示大量内容即可显示很有用的信息。如果要为工作负载使用部署,则有两种选择:

kubectl get deploy
kubectl get deploy -n  名称空间
kubectl get deploy –all-namespaces [或“ -A”]

理想情况下,您希望看到的是1/1或等值的2/2,以此类推。这表明您的部署已被接受,并已尝试进行部署。

接下来,您可能需要查看kubectl get pod,以查看部署的后备Pod是否正确启动。

  1. kubectl get events

我感到惊讶的是,我不得不经常向与Kubernetes有问题的人们解释这个小技巧。此命令将打印出给定名称空间中的事件,非常适合查找关键问题,例如崩溃的pod或无法pull容器镜像。

Kubernetes中的日志是“未排序的”,因此,您将需要添加以下内容,这些内容取自OpenFaaS文档。

$ kubectl get events --sort-by=.metadata.creationTimestamp kubectl get事件的另一个接近的命令是是kubectl  describe,就像get deploy / pod一样,它与对象的名称一起工作:

kubectl describe deploy/figlet -n openfaas

您会在这里获得非常详细的信息。您可以描述大多数事情,包括节点,这些节点将显示由于资源限制或其他问题而无法启动 Pod。

  1. kubectl logs

这个命令肯定经常大家经常使用,但很多人使用了错误的方式。

如果您进行了部署,比方说cert-manager命名空间中的cert-manager,那么很多人认为他们首先必须找到Pod的长(唯一)名称并将其用作参数。不对。

kubectl logs deploy/cert-manager -n cert-manager

要跟踪日志,请添加-f

kubectl logs deploy/cert-manager -n cert-manager -f

您可以将所有三个结合起来。

如果您的 Deployment 或 Pod 有任何标签,则可以使用 -l app = name 或任何其他标签集来附加到一个或多个匹配Pod的日志中。

kubectl logs -l app=nginx

有一些工具,例如 stern 和 kail,可以帮助您匹配模式并节省一些键入操作,但我发现它们会分散您的注意力。

4.kubectl get -o yaml

当您开始使用由另一个项目或诸如Helm之类的其他工具生成的YAML时,您将很快需要它。在生产中检查镜像的版本或您在某处设置的注释也很有用。

kubectl run nginx-1 --image=nginx --port=80 --restart=Always

输出yaml

kubectl get deploy/nginx-1 -o yaml

现在我们知道了。而且,我们可以添加–export并将YAML保存在本地以进行编辑并再次应用。

实时编辑YAML的另一个选项是kubectl edit,如果您对vim感到困惑,不知道如何使用,请在命令前加上VISUAL = nano,使用这个简化编辑器。

  1. kubectl scale  您打开和关闭它了吗?

Kubectl scale可用于将Deployment及其Pod缩小为零个副本,实际上杀死了所有副本。当您将其缩放回1/1时,将创建一个新的Pod,重新启动您的应用程序。语法非常简单,您可以重新启动代码并再次进行测试。

kubectl scale deploy/nginx-1 --replicas=0
kubectl scale deploy/nginx-1 --replicas=1
  1. Port forwarding

我们需要这个技巧, 通过kubectl进行的端口转发使我们可以在我们自己计算机上的本地或远程群集上公开一项服务,以便在任何已配置的端口上访问它,而无需在Internet上公开它。

以下是在本地访问Nginx部署的示例:

kubectl port-forward deploy/nginx-1 8080:80

有人认为这仅适用于部署或Pod,这是错误的。服务间是公平的,通常是转发的选择,因为它们将模拟生产集群中的配置。

如果您确实想在Internet上公开服务,通常会使用LoadBalancer服务,或运行kubectl暴露:

kubectl expose deployment nginx-1 --port=80 --type=LoadBalancer

技巧说完了,可以现在尝试一下,我希望您发现这6条命令和技巧有用, 现在,您可以在真实的集群上对其进行测试了。

来源:https://www.mindg.cn/?p=2578

image

查看原文

赞 1 收藏 0 评论 0

民工哥 发布了文章 · 10月21日

人生第一次领奖!

上周六(10.17)去了次北京!这也是我人生中第一次到北京。

为什么去北京呢?

国庆10.3号,人邮的刘鑫老师发微信告诉我,说我获得了2020年人民邮电出版社/异步社区《最具影响力作者》,说实话,看到这个信息的时候,我还有点怀疑是不是信息发错人了。

就这样,才有了我人生中第一次的北京之行。

活动时间是17日下午13:30开始,官方的活动流程大体如下:

这里就给大家附上几张照片了,现场也来很多大咖分享,有大学教授、有创业CEO、有知名互联网行业的技术Leader,收获很多。

当然,对于我来说还有一个重要意义,第一次写书(新书上市5个月,重印4次),第一次上台领奖!!!

大家猜一猜哪个是我(可以文后留言告诉我),哈哈哈哈哈。。

在这里,感谢人邮和异步社区给我们这些技术爱好者提供了一个分享的平台、感谢人邮编辑张老师幕后的辛苦付出、感谢Linux系统开发之父林纳斯大神、感谢一直关注、支持我的家人、朋友与读者们、最后感谢这次活动的组织团队(人邮的营销部门的小伙伴们),谢谢大家,再次感谢。

会后,交流期间也认识了很多有名的作者,会后交流了很多很多,收获不少,技术人多交流,多讨论,才会有不同的见解,每个人的角度与思路不同,才能碰撞出不同的火花,才能进步。

会后还在抽奖活动,可惜运气不佳,三轮都没有抽中....。

正好,趁着这个机会,也在北京顺便转了转,去了北大、清华,北海公园,鸟巢、水立方,老北京的胡同里转了转,天安门,还有一些特色的街区,像王府井、鲜鱼口等,可惜的是由于时间关系,没有去长城。

北京的天气还不错,不过就是比较的干,一开始很不适应,北京的景点太多了,转不完,下次有机会再去了,本来还准备去拜访一些大佬的,可惜时间、精力不允许。

没去北京之前就听讲北京堵,不过我是周未去的,总体看来还行,后来打车听打车的司机说,北京多数都是工作日堵,因为周未都不大出来。北京的消费确实高(第一晚上周边不熟悉,点了个外卖,三个菜80+,还特难吃,不由得感叹,北京的生活压力真大,这一个月得挣多少钱呢?才够花....),房价就不用说了,司机师傅说对于他们北京人来讲,一套实用面积40平的房子就是一件很幸福的事了,北京人民也生活在水深火热之中。。

难怪,在大会听有的大佬说,他非常敬佩在北京打拼、创业的朋友们,你们很不容易,房价这么高,交通这么堵,租房这么贵,消费这么高。。。

不过,北京的地铁确实方便、也还便宜,建议大家有去北京旅游的朋友,可以下载一个叫亿通行的APP,地铁、公交直接刷码非常方便,省去排队买车的经验。

好了,水了这么多了,就是和大家分享一下我的激动的尽情,同时也感谢一下读者朋友们对我的关注与支持。

最后,给大家晒一下我的奖杯与证书,奖杯是真的挺重的。

最后,也祝生活、打拼在北京及全国各地的朋友们,都能工作开心、生活愉快!!!

赚更多的钱,实现更多的人生目标,一起加油!!!

image

查看原文

赞 17 收藏 0 评论 5

民工哥 发布了文章 · 10月20日

挺带劲,这款国人开源的监控系统真强大~

项目简介

集监控点监控、日志监控、数据可视化以及监控告警为一体的国产开源监控系统,直接部署即可使用。  

监控数据类型丰富,提供多种富有表现力的图表,满足对数据可视化的需要,目前支持折线图、饼图、地理位置图,后续会引入  更多富有表现力的图表以加强对数据可视化的支持。

相比其它开源监控系统优势:

  • 支持插件功能, 监控插件无需开发,自由选择监控插件,安装即可使用
  • 集成告警功能, 支持多种告警方式
  • 集成分布式日志系统功能
  • 支持多种部署方式    
    a、集中部署(全部服务部署在一台机器,适合个人或者小团队开发者)

    b、分布式部署(分布式部署在多台机器,适合小中型企业大规模监控需求)

  • 支持自动化配置(机器部署agent后自动注册到监控系统无需在控制台配置、视图根据上报自动绑定相关上报机器)
  • 支持多用户访问(子账号由管理员账号在控制台添加)
  • 上报接口支持主流开发语言,数据上报api 提供类似公共库接口的便捷

特色功能推荐

IP地址库: 支持通过IP地址上报时将IP地址转为物理地址,相同物理地址归并展示一个监控API 即可轻松生成监控。

数据的物理地址分布图

监控插件市场: 让监控成为可以复用的组件,更多监控插件持续开发中。

分布式日志系统: 支持大规模系统日志上报,日志上报支持频率限制、日志染色、自定义字段等高级功能,控制台日志查看支持按关键字、排除关键字、上报时间、上报机器等方式过滤日志,从茫茫日志中轻松找到您需要的日志。

视图机制: 监控图表支持视图定制模式,视图可按上报服务器、监控点随意组合,轻松定制您需要的监控视图,并可在监控图表上直接设置告警值。

告警集成: 集成告警功能, 支持邮件、短信、微信、PC客户端等告警方式,告警功能无需开发直接可用。

在线部署

安装脚本会先检查当前系统是否支持在线安装, 如不支持您可以下载源码后在系统上编译安装。

在线部署目前只支持集中部署方式, 即所有服务部署在一台机器上, 该机器上需要安装 mysql/apache。

安装脚本使用中文 utf8 编码, 安装过程请将您的终端设置为 utf8, 以免出现乱码。

安装脚本同时支持 root 账号和普通账号操作, 使用普通账号执行安装部署要求如下:

  1. 在线部署使用动态链接库, 需要在指定目录下执行安装脚本, 目录为: /home/mtreport
  2. 普通账号某些目录可能无权操作, 需要授权才能正常安装

我们强烈建议您先在本地虚拟机上执行在线安装, 熟悉安装流程后在实际部署到您的服务器上。在线部署详细文档:http://xrkmonitor.com/monitor...

离线部署

如果在线安装失败或者需要二次开发, 可以使用源码编译方式安装。

三部完成部署:

  1. 执行 make 完成源码编译
  2. 进入 tools_sh 目录,执行 make_all.sh 生成部署包
  3. 在安装目录解压部署包,执行 local_install.sh 完成安装

使用的技术方案

  1. apache + mysql(监控点数据、配置信息使用 mysql 存储, 支持分布式部署)
  2. 前端 web 控制台采用 dwz 开源框架
  3. 前端监控图表采用开源 echarts 绘制
  4. 后台 cgi 使用开源的cgi模板引擎 - clearsilver, 所有cgi支持以fastcgi方式部署
  5. 后台服务使用了开源的 socket 开发框架 - C++ Sockets

項目地址

官网地址:https://gitee.com/xrkmonitorc...

在线文档 :http://xrkmonitor.com/monitor...

API支持的语言

  1. c/c++ 开发接口
  2. php 开发接口
  3. linux shell 开发接口
  4. javascript 开发接口

image

查看原文

赞 4 收藏 2 评论 0

民工哥 发布了文章 · 10月20日

CI/CD 最佳实践的基本原则

持续集成和持续部署(CI/CD)是许多组织使用的敏捷方法。它正在帮助这些组织有效、安全地发行软件。

根据 GitLab 2020 DevSecOps 调查,几乎 83%的开发人员表示,他们正在比以前更快、更频繁地发布代码。59%的公司表示他们几乎每天都要发布多次。而这是因为采用了 DevOps 方法,并且主要归功于持续集成、自动化测试和持续部署。

每个组织都试图在建立 CI/CD 流水线时引入自己的方法,最终找到完美的平衡,我们通常将其称为“最佳实践”。本文就来谈一些有效且安全的 CI/CD 流水线的基本原则。

可靠性

在软件开发生命周期中拥有 CI/CD 流水线工具是组织能够快速构建和交付应用程序的一大福音,但与此同时,选择正确的 CI/CD 工具也相当重要,其应当能够随业务组织发展而扩展,并且运行准确无误。而且,它还应该足够灵活,可以处理多种用例和多种软件交付需求。

CI 流水线应当很快

使 CI/CD 流水线尽可能快是非常重要的。我们所有的自动化测试都运行在开发环境中的 CI 流水线上,而其最终会被部署到生产环境中。因此,涵盖所有边缘情况和潜在的致命失效非常重要,同时,我们需要确保所有这些更改不会在我们的代码中造成任何无法预料的错误。因此,同时保持 CI 流水线简单、快速和安全非常重要。

随着微服务架构的广泛采用,CI 流水线变得简单明了(不同于单体架构的情形)。但是如果流水线任务繁重,最好移除一些不会产生重大影响的测试,并且记录下这种取舍。我们还应该确定测试的优先顺序。运行较快的测试应首先执行。例如,单元测试比较快,而且是程序功能或模块的基础,因此应当首先执行,然后再进行功能测试和集成测试。这样,我们可以尽早发现错误并节省时间。开发者应该在推送代码之前在本地运行测试以尽早发现错误。

在独立环境中构建和运行

从 CI/CD 流水线的安全性以及确保它类似于预发布环境和生产环境的角度讲,在独立的环境中运行 CI/CD 流水线一直都很重要,这可以确保我们的测试结果更加准确。

我们可以使用 Docker 或其他任何容器化工具来运行我们的测试套件,也可以在 Docker 容器中为我们的应用程序安装其他依赖。这样,我们可以确保测试在完全隔离的环境中运行,并且不受底层主机的任何影响。由于我们的 CI/CD 平台可以完全访问我们的代码仓库,因此大多数组织也习惯于在自己的云平台基础设施中部署 CI/CD 工具以确保安全。

许多组织迈出了更大一步,他们还在隔离环境中渲染和测试 UI 组件。在将它们作为独立的构建块交付并集成到一个或多个项目中之前,此过程是一种验证它们确实独立的方法(这通常使用 Bit(Github)完成)。

预发布环境和生产环境等价

建议始终保持预发布环境和生产环境等价,以避免运行测试时发生意外错误导致发布暂停这种小概率事件。我们的 CI/CD 流水线首先经过运行测试和在预发布环境中部署的阶段。测试后,该应用会自动升级(或手动部署)到生产环境。

使开发和测试环境完全等价于生产环境非常困难,但我们可以在需要时做出决定保持他们尽可能相似,并且了解我们正在做出的取舍。大多数组织还使用“蓝绿部署”或“金丝雀发布”的部署策略,在该策略中,我们首先在生产环境中部署应用并处理大约 1% 的流量。然后将流量提高到 100%,或者也可以较为轻松的回滚到之前的版本。

总结

所有 CI/CD 工具都不相同,每个组织都尽可能以最有效和便捷的方式利用 CI/CD。但以上是一些最佳实践,每个人都应注意并遵循这些最佳实践,以避免将来出现问题。每个组织都应授权并仅通过 CI/CD 流水线来发布软件,以提高代码质量和组织的编码规范。

作者 | Ankit Jain      策划 | 田晓旭
原文:__https://blog.bitsrc.io/ci-cd-...

image

查看原文

赞 1 收藏 1 评论 0

民工哥 发布了文章 · 10月19日

Linux 上如何清除 RAM 内存高速缓存,缓存和交换空间

像任何其他的操作系统一样,GNU / Linux已经有效地实施了内存管理甚至更多。但是,如果有任何进程正在蚕食你的内存,你要清除它,Linux提供了一个方法来刷新或清除RAM缓存。

在Linux中如何清除缓存?

每一个Linux系统有三个选项来清除缓存而不中断任何进程或服务。

1,仅清除缓存页

sync; echo 1 > /proc/sys/vm/drop_caches

2,清除目录项和inodes

sync; echo 2 > /proc/sys/vm/drop_caches

3,清除,缓存页,目录项和inodes

sync; echo 3 > /proc/sys/vm/drop_caches

上述命令的说明:

sync将刷新文件系统缓存,命令通过“;”分隔,顺序执行,shell等待终止在序列中的每一个命令执行之前。正如内核文档中提到的,写到drop_cache将清空缓存而不杀死任何应用程序/服务,echo命令做写入文件的工作。

如果你必须清除磁盘高速缓存,第一个命令在企业和生产环境中是最安全,“…echo 1> …”只会清除页缓存。

不建议使用上面第三个选项在生产环境中“…echo 3 >” ,除非你明确自己在做什么,因为它会清除缓存页,目录项和inodes。

在Linux上释放Buffer和Cache要用到内核是否是个好主意?

当你请求许多设定想要检查时,如果它实际上是专门实施对I/O 广泛的基准测试,那么你可能需要清除缓存。你可以如上所示删除缓存,无需重新启动系统即无需停机。

Linux被设计成它在寻找磁盘之前到磁盘缓存寻找的方式。如果它发现该资源在缓存中,则该请求不到达磁盘。如果我们清理缓存,磁盘缓存将没有用处,系统会到磁盘上寻找资源。

此外,当清除缓存后它也将减慢系统运行速度,系统会重新加载每一个被请求的资源再次到磁盘缓存中。

现在,我们将通过一个cron任务调度器创建一个shell脚本在每天下午2点自动清除RAM缓存。

创建一个shell脚本clearcache.sh并在其中添加以下行:

#!/bin/bash
# Note, we are using "echo 3", but it is not recommended in production instead use "echo 1"
echo "echo 3 > /proc/sys/vm/drop_caches"

给clearcache.sh文件设置执行权限

# chmod 755 clearcache.sh

现在,当你需要清除RAM缓存时只需要调用脚本。

现在设置一个定时任务来清除RAM缓存每天在下午2点,打开crontab进行编辑。

# crontab -e

添加以下行,保存并退出。

0 3 * * * /path/to/clearcache.sh

有关如何创建一个定时任务,更多细节你可以查看我们的文章11 Cron Scheduling Jobs。

在生产环境的服务器上自动清除RAM是否是一个好主意?

不!它不是。想想一个情况,当你已经预定脚本来清除RAM缓存每天在下午2点。每天下午2点该脚本会执行并刷新你的RAM缓存。在一天中的任何时候,您网站用户的在线量可能会超过预期的,并从你的服务器请求资源。同时调度器运行着脚本,并在高速缓存中清除一切。当所有的用户都从磁盘读取数据时,这将导致服务器崩溃并损坏数据库。

因此,清除缓存仅在必要时并且在你的预料之中,否则你就是个Cargo Cult System Administrator。

如何清除Linux的交换空间?

如果你想清除交换空间,你可以运行下面的命令:

# swapoff -a && swapon -a

此外,了解有关风险后,您可能会将上面的命令添加到cron中。

现在,我们将上面两种命令结合成一个命令写出正确的脚本来同时清除RAM缓存和交换空间。

# echo 3 > /proc/sys/vm/drop_caches && swapoff -a && swapon -a && printf 'n%sn' 'Ram-cache and Swap Cleared'

su -c 'echo 3 >/proc/sys/vm/drop_caches' && swapoff -a && swapon -a && printf 'n%sn' 'Ram-cache and Swap Cleared'

在测试上面的命令之前,我们先运行“free -m” 然后执行脚本检查缓存。

就是现在,如果你喜欢这篇文章,不要忘记点个在看与转发分享支持一下。

原文:http://www.tecmint.com/clear-...

image

查看原文

赞 0 收藏 0 评论 0

民工哥 发布了文章 · 10月18日

MySQL 死锁产生原因及解决方法

image

一、Mysql 锁类型和加锁分析

1、锁类型介绍:

MySQL有三种锁的级别:页级、表级、行级。

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

算法:

  • next KeyLocks锁,同时锁住记录(数据),并且锁住记录前面的Gap
  • Gap锁,不锁记录,仅仅记录前面的Gap
  • Recordlock锁(锁数据,不锁Gap)

所以其实 Next-KeyLocks=Gap锁+ Recordlock锁

二、死锁产生原因和示例

1、产生原因:

所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。表级锁不会产生死锁.所以解决死锁主要还是针对于最常用的InnoDB。

  • 死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。
  • 那么对应的解决死锁问题的关键就是:让不同的session加锁有次序
2、产生示例:

案例一

需求:将投资的钱拆成几份随机分配给借款人。

起初业务程序思路是这样的:

投资人投资后,将金额随机分为几份,然后随机从借款人表里面选几个,然后通过一条条select for update 去更新借款人表里面的余额等。

例如两个用户同时投资,A用户金额随机分为2份,分给借款人1,2

B用户金额随机分为2份,分给借款人2,1

由于加锁的顺序不一样,死锁当然很快就出现了。

对于这个问题的改进很简单,直接把所有分配到的借款人直接一次锁住就行了。

Select * from xxx where id in (xx,xx,xx) for update

在in里面的列表值mysql是会自动从小到大排序,加锁也是一条条从小到大加的锁

例如(以下会话id为主键):

Session1:

mysql> select * from t3 where id in (8,9) for update;
+----+--------+------+---------------------+
| id | course | name | ctime               |
+----+--------+------+---------------------+
|  8 | WA     | f    | 2016-03-02 11:36:30 |
|  9 | JX     | f    | 2016-03-01 11:36:30 |
+----+--------+------+---------------------+
rows in set (0.04 sec)

Session2:

select * from t3 where id in (10,8,5) for update;

锁等待中……

其实这个时候id=10这条记录没有被锁住的,但id=5的记录已经被锁住了,锁的等待在id=8的这里,不信请看。

Session3:

mysql> select * from t3 where id=5 for update;

锁等待中

Session4:

mysql> select * from t3 where id=10 for update;
+----+--------+------+---------------------+
| id | course | name | ctime               |
+----+--------+------+---------------------+
| 10 | JB     | g    | 2016-03-10 11:45:05 |
+----+--------+------+---------------------+
row in set (0.00 sec)

在其它session中id=5是加不了锁的,但是id=10是可以加上锁的。

案例二

在开发中,经常会做这类的判断需求:根据字段值查询(有索引),如果不存在,则插入;否则更新。

以id为主键为例,目前还没有id=22的行

Session1:

select * from t3 where id=22 for update;
Empty set (0.00 sec)

session2:

select * from t3 where id=23  for update;
Empty set (0.00 sec)

Session1:

insert into t3 values(22,'ac','a',now());

锁等待中……

Session2:

insert into t3 values(23,'bc','b',now());
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

当对存在的行进行锁的时候(主键),mysql就只有行锁。

当对未存在的行进行锁的时候(即使条件为主键),mysql是会锁住一段范围(有gap锁)

锁住的范围为:

(无穷小或小于表中锁住id的最大值,无穷大或大于表中锁住id的最小值)

如:如果表中目前有已有的id为(11 , 12)

那么就锁住(12,无穷大)

如果表中目前已有的id为(11 , 30)

那么就锁住(11,30)

对于这种死锁的解决办法是:

insert into t3(xx,xx) on duplicate key update `xx`='XX';

用mysql特有的语法来解决此问题。因为insert语句对于主键来说,插入的行不管有没有存在,都会只有行锁

案例三

mysql> select * from t3 where id=9 for update;
+----+--------+------+---------------------+
| id | course | name | ctime               |
+----+--------+------+---------------------+
|  9 | JX     | f    | 2016-03-01 11:36:30 |
+----+--------+------+---------------------+
 
row in set (0.00 sec)

Session2:

mysql> select * from t3 where id<20 for update;

锁等待中

Session1:

mysql> insert into t3 values(7,'ae','a',now());
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

这个跟案例一其它是差不多的情况,只是session1不按常理出牌了,

Session2在等待Session1的id=9的锁,session2又持了1到8的锁(注意9到19的范围并没有被session2锁住),最后,session1在插入新行时又得等待session2,故死锁发生了。

这种一般是在业务需求中基本不会出现,因为你锁住了id=9,却又想插入id=7的行,这就有点跳了,当然肯定也有解决的方法,那就是重理业务需求,避免这样的写法。

案例四

一般的情况,两个session分别通过一个sql持有一把锁,然后互相访问对方加锁的数据产生死锁。

案例五

两个单条的sql语句涉及到的加锁数据相同,但是加锁顺序不同,导致了死锁。

死锁场景如下:

表结构:

CREATE TABLE dltask (
    id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT ‘auto id’,
    a varchar(30) NOT NULL COMMENT ‘uniq.a’,
    b varchar(30) NOT NULL COMMENT ‘uniq.b’,
    c varchar(30) NOT NULL COMMENT ‘uniq.c’,
    x varchar(30) NOT NULL COMMENT ‘data’,   
    PRIMARY KEY (id),
    UNIQUE KEY uniq_a_b_c (a, b, c)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’deadlock test’;
#a,b,c三列,组合成一个唯一索引,主键索引为id列。

事务隔离级别:

RR (Repeatable Read)

每个事务只有一条SQL:

delete from dltask where a=? and b=? and c=?;

SQL的执行计划:

死锁日志:

众所周知,InnoDB上删除一条记录,并不是真正意义上的物理删除,而是将记录标识为删除状态。(注:这些标识为删除状态的记录,后续会由后台的Purge操作进行回收,物理删除。但是,删除状态的记录会在索引中存放一段时间。) 在RR隔离级别下,唯一索引上满足查询条件,但是却是删除记录,如何加锁?InnoDB在此处的处理策略与前两种策略均不相同,或者说是前两种策略的组合:对于满足条件的删除记录,InnoDB会在记录上加next key lock X(对记录本身加X锁,同时锁住记录前的GAP,防止新的满足条件的记录插入。) Unique查询,三种情况,对应三种加锁策略,总结如下:

此处,我们看到了next key锁,是否很眼熟?对了,前面死锁中事务1,事务2处于等待状态的锁,均为next key锁。明白了这三个加锁策略,其实构造一定的并发场景,死锁的原因已经呼之欲出。但是,还有一个前提策略需要介绍,那就是InnoDB内部采用的死锁预防策略。

  • 找到满足条件的记录,并且记录有效,则对记录加X锁,No Gap锁(lock_mode X locks rec but not gap);
  • 找到满足条件的记录,但是记录无效(标识为删除的记录),则对记录加next key锁(同时锁住记录本身,以及记录之前的Gap:lock_mode X);
  • 未找到满足条件的记录,则对第一个不满足条件的记录加Gap锁,保证没有满足条件的记录插入(locks gap before rec);

死锁预防策略

InnoDB引擎内部(或者说是所有的数据库内部),有多种锁类型:事务锁(行锁、表锁),Mutex(保护内部的共享变量操作)、RWLock(又称之为Latch,保护内部的页面读取与修改)。

InnoDB每个页面为16K,读取一个页面时,需要对页面加S锁,更新一个页面时,需要对页面加上X锁。任何情况下,操作一个页面,都会对页面加锁,页面锁加上之后,页面内存储的索引记录才不会被并发修改。

因此,为了修改一条记录,InnoDB内部如何处理:

  • 根据给定的查询条件,找到对应的记录所在页面;
  • 对页面加上X锁(RWLock),然后在页面内寻找满足条件的记录;
  • 在持有页面锁的情况下,对满足条件的记录加事务锁(行锁:根据记录是否满足查询条件,记- 录是否已经被删除,分别对应于上面提到的3种加锁策略之一);

死锁预防策略:相对于事务锁,页面锁是一个短期持有的锁,而事务锁(行锁、表锁)是长期持有的锁。因此,为了防止页面锁与事务锁之间产生死锁。InnoDB做了死锁预防的策略:持有事务锁(行锁、表锁),可以等待获取页面锁;但反之,持有页面锁,不能等待持有事务锁。

根据死锁预防策略,在持有页面锁,加行锁的时候,如果行锁需要等待。则释放页面锁,然后等待行锁。此时,行锁获取没有任何锁保护,因此加上行锁之后,记录可能已经被并发修改。因此,此时要重新加回页面锁,重新判断记录的状态,重新在页面锁的保护下,对记录加锁。如果此时记录未被并发修改,那么第二次加锁能够很快完成,因为已经持有了相同模式的锁。但是,如果记录已经被并发修改,那么,就有可能导致本文前面提到的死锁问题。

以上的InnoDB死锁预防处理逻辑,对应的函数,是row0sel.c::row_search_for_mysql()。感兴趣的朋友,可以跟踪调试下这个函数的处理流程,很复杂,但是集中了InnoDB的精髓。

剖析死锁的成因

做了这么多铺垫,有了Delete操作的3种加锁逻辑、InnoDB的死锁预防策略等准备知识之后,再回过头来分析本文最初提到的死锁问题,就会手到拈来,事半而功倍。

首先,假设dltask中只有一条记录:(1, ‘a’, ‘b’, ‘c’, ‘data’)。三个并发事务,同时执行以下的这条SQL:

delete from dltask where a=’a’ and b=’b’ and c=’c’;

并且产生了以下的并发执行逻辑,就会产生死锁:

上面分析的这个并发流程,完整展现了死锁日志中的死锁产生的原因。其实,根据事务1步骤6,与事务0步骤3/4之间的顺序不同,死锁日志中还有可能产生另外一种情况,那就是事务1等待的锁模式为记录上的X锁 + No Gap锁(lock_mode X locks rec but not gap waiting)。这第二种情况,也是”润洁”同学给出的死锁用例中,使用MySQL 5.6.15版本测试出来的死锁产生的原因。

此类死锁,产生的几个前提:

  • Delete操作,针对的是唯一索引上的等值查询的删除;(范围下的删除,也会产生死锁,但是死锁的场景,跟本文分析的场景,有所不同)
  • 至少有3个(或以上)的并发删除操作;
  • 并发删除操作,有可能删除到同一条记录,并且保证删除的记录一定存在;
  • 事务的隔离级别设置为Repeatable Read,同时未设置innodb_locks_unsafe_for_binlog参数(此参数默认为FALSE);(Read Committed隔离级别,由于不会加Gap锁,不会有next key,因此也不会产生死锁) -使用的是InnoDB存储引擎;(废话!MyISAM引擎根本就没有行锁)

https://blog.csdn.net/tr1912/...

image

查看原文

赞 7 收藏 5 评论 2

民工哥 发布了文章 · 10月18日

10 个冷门但又非常实用的 Docker 使用技巧

在平时的工作中,docker 接触得很多,除了经常使用的 docker run ,docker stop 等命令,docker 还有很多十分有用但是却不经常使用的命令,下面就来总结一下:

1. docker top

这个命令是用来查看一个容器里面的进程信息的,比如你想查看一个 nginx 容器里面有几个 nginx 进程的时候,就可以这么做:

docker top 3b307a09d20d
UID      PID    PPID    C    STIME  TTY    TIME       CMD
root     805    787     0    Jul13   ?   00:00:00  nginx: master process nginx -g daemon off;
systemd+ 941     805     0   Jul13    ?   00:03:18  nginx: worker process

2. docker load && docker save

我一般使用这两个命令去下载打包 Kubernetes 的镜像,因为你知道的国内的网速并不像国外那么快。

docker save 可以把一个镜像保存到 tar 文件中,你可以这么做:

~ docker save registry:2.7.1 >registry-2.7.1.tar
#同时 docker load 可以把镜像从 tar 文件导入到 docker 中
~ docker load < registry-2.7.1.tar

3. docker search

这个命令可以帮助你在命令行中方便的搜索 DockerHub 中的镜像,比如:

~ docker search nginx
NAME                               DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
nginx                              Official build of Nginx.                        13519               [OK]
jwilder/nginx-proxy                Automated Nginx reverse proxy for docker con…   1846                                    [OK]
richarvey/nginx-php-fpm            Container running Nginx + PHP-FPM capable of…   780                                     [OK]
linuxserver/nginx                  An Nginx container, brought to you by LinuxS…   123
bitnami/nginx                      Bitnami nginx Docker Image                      87                                      [OK]
tiangolo/nginx-rtmp                Docker image with Nginx using the nginx-rtmp…   85                                      [OK]
jc21/nginx-proxy-manager           Docker container for managing Nginx proxy ho…   73
alfg/nginx-rtmp                    NGINX, nginx-rtmp-module and FFmpeg from sou…   71                                      [OK]
nginxdemos/hello                   NGINX webserver that serves a simple page co…   57                                      [OK]
jlesage/nginx-proxy-manager        Docker container for Nginx Proxy Manager        53                                      [OK]
nginx/nginx-ingress                NGINX Ingress Controller for Kubernetes         37
......

当然这个功能在国内可能不会特别好用,因为......

4. docker events

这个命令可以帮助你实时获取 docker 的各种事件信息,比如创建了一个容器什么的。

~ docker events
2020-07-28T21:28:46.000403018+08:00 image load sha256:432bf69f0427b52cad10897342eaf23521b7d973566354118e9a59c4d31b5fae (name=sha256:432bf69f0427b52cad10897342eaf23521b7d973566354118e9a59c4d31b5fae)

5. docker update

当你 docker run 了之后却发现里面有一些参数并不是你想要的状态比如你设置的 nginx 容器 cpu 或者内存太小,这个时候你就可以使用 docker update 去修改这些参数。

~ docker update nginx --cpus 2

6. docker history

当你修改了一个镜像,但是忘记了每一层的修改命令,或者你想查看一个镜像是怎么构建的时候就可以使用这个命令,比如:

~ docker history  traefik:v2.1.6
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
5212a87ddaba        5 months ago        /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
<missing>           5 months ago        /bin/sh -c #(nop)  CMD ["traefik"]              0B
<missing>           5 months ago        /bin/sh -c #(nop)  ENTRYPOINT ["/entrypoint.…   0B
<missing>           5 months ago        /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>           5 months ago        /bin/sh -c #(nop) COPY file:59a219a1fb7a9dc8…   419B
<missing>           5 months ago        /bin/sh -c set -ex;  apkArch="$(apk --print-…   52.9MB
<missing>           5 months ago        /bin/sh -c apk --no-cache add ca-certificate…   1.85MB
<missing>           6 months ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           6 months ago        /bin/sh -c #(nop) ADD file:a1906f14a4e217a49…   4.81MB

7. docker wait

这个命令可以查看容器的退出状态,比如:

~ docker wait 7f7f0522a7d0
0

这样你就可以知道这个容器是正常退出的还是异常退出的了。

8. docker pause && docker unpause

当你运行了一个容器但是想要暂停它运行的时候,你就可以使用这个命令。

~ docker pause 7f7f0522a7d0

9. docker diff

当你运行了一个容器,但是你不知道容器里修改了哪一些文件的时候可以使用这个命令,比如:

~ docker diff 38c59255bf6e
C /etc
A /etc/localtime
C /var
C /var/lib
A /var/lib/registry

10. docker stats

这个是 docker 内置的监控命令,当你想要查看当前主机下所有容器占用内存和 cpu 的情况的时候就可以使用这个命令。

~ docker stats
CONTAINER ID        NAME                        CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
1c5ade04e7f9        redis                        0.08%               17.53MiB / 47.01GiB   0.04%               10.9GB / 37GB       0B / 0B             4
afe6d4ebe409        kafka-exporter                0.09%               16.91MiB / 47.01GiB   0.04%               1.97GB / 1.53GB     752MB / 0B          23
f0c7c01a9c34        kafka-docker_zookeeper         0.01%               308.8MiB / 47.01GiB   0.64%               20.2MB / 12.2MB     971MB / 3.29MB      28
da8c5008955f        kafka-docker_kafka-manager     0.08%               393.2MiB / 47.01GiB   0.82%               1.56MB / 2.61MB     1.14GB / 0B         60
c8d51c583c49        kafka-docker_kafka            1.63%               1.256GiB / 47.01GiB   2.67%               30.4GB / 48.9GB     22.3GB / 5.77GB     85
......
原文:http://suo.im/6n2lLa

image

查看原文

赞 12 收藏 11 评论 0

民工哥 发布了文章 · 10月16日

一款免费的数据库工具,比Navicat还要好用,功能还很强大

DBeaver 是一个基于 Java 开发,免费开源的通用数据库管理和开发工具,使用非常友好的 ASL 协议。可以通过官方网站或者 Github 进行下载。

由于 DBeaver 基于 Java 开发,可以运行在各种操作系统上,包括:Windows、Linux、macOS 等。DBeaver 采用 Eclipse 框架开发,支持插件扩展,并且提供了许多数据库管理工具:ER 图、数据导入/导出、数据库比较、模拟数据生成等。

DBeaver 通过 JDBC 连接到数据库,可以支持几乎所有的数据库产品,包括:MySQL、PostgreSQL、MariaDB、SQLite、Oracle、Db2、SQL Server、Sybase、MS Access、Teradata、Firebird、Derby 等等。商业版本更是可以支持各种 NoSQL 和大数据平台:MongoDB、InfluxDB、Apache Cassandra、Redis、Apache Hive 等。

下载与安装

DBeaver 社区版可以通过官方网站或者 Github 进行下载。两者都为不同的操作系统提供了安装包或者解压版,可以选择是否需要同时安装 JRE。另外,官方网站还提供了 DBeaver 的 Eclipse 插件,可以在 Eclipse 中进行集成。

DBeaver 支持中文,安装过程非常简单,不多说,唯一需要注意的是 DBeaver 的运行依赖于 JRE。不出意外,安装完成后运行安装目录下的 dbeaver.exe 可以看到以下界面(Windows 10):


这个界面其实是新建数据库连接,我们可以看到它支持的各种数据平台;先点击“取消”按钮,进入主窗口界面。

此时,它会提示我们是否建立一个示例数据库。


如果点击“是(Y)”,它会创建一个默认的 SQLite 示例数据库。下图是它的主窗口界面。


DBeaver 和我们常用的软件类似,最上面是菜单项和快捷工具,左侧是已经建立的数据库连接和项目信息,右侧是主要的工作区域。

连接数据库

打开 DBeaver 之后,首先要做的就是创建数据库连接。可以通过菜单“数据库” -> “新建连接”打开新建连接向导窗口,也就是我们初次运行 DBeaver 时弹出的窗口。


我们以 PostgreSQL 为例,新建一个数据库连接。选择 PostgreSQL 图标,点击“下一步(N)”。


然后是设置数据库的连接信息:主机、端口、数据库、用户、密码。“Advanced settings”高级设置选项可以配置 SSH、SSL 以及代理等,也可以为连接指定自己的名称和连接类型(开发、测试、生产)。

搜索民工哥技术之路公众号,回复“实战宝典”,送你一份阿里内部实战手册。

点击最下面的“测试链接(T)”可以测试连接配置的正确性。初次创建某种数据库的连接时,会提示下载相应的 JDBC 驱动。


它已经为我们查找到了相应的驱动,只需要点击“下载”即可,非常方便。下载完成后,如果连接信息正确,可以看到连接成功的提示。


确认后完成连接配置即可。左侧的数据库导航中会增加一个新的数据库连接。

由于某些数据库(例如 Oracle、Db2)的 JDBC 驱动需要登录后才能下载,因此可以使用手动的方式进行配置。选择菜单“数据库” -> “驱动管理器”。


选择 Oracle ,点击“编辑(E)…”按钮。


通过界面提示的网址,手动下载 Oracle 数据库的 JDBC 驱动文件,例如 ojdbc8.jar。然后点击“添加文件(F)”按钮,选择并添加该文件。


下次建立 Oracle 数据库连接时即可使用该驱动。

新建连接之后,就可以通过这些连接访问相应的数据库,查看和编辑数据库中的对象,执行 SQL 语句,完成各种管理和开发工作。

生成 ER 图

最后介绍一下如何生成数据库对象的 ER 图。点击窗口左侧“数据库导航”旁边的“项目”视图。


其中有个“ER Diagrams”,就是实体关系图。右击该选项,点击“创建新的 ER 图”。


输入一个名称并选择数据库连接和需要展示的对象,然后点击“完成”,即可生成相应的 ER 图。

ER 图可以进行排版和显示设置,也支持打印为图片。DBeaver 目前还不支持自己创建 ER 图,只能从现有的数据库中生成。

对于图形工具,很多功能我们都可以自己去使用体会;当然,DBeaver 也提供了用户指南,自行参考。

作者:不剪发的Tony老师
来源:http://suo.im/5OBiUi

image

查看原文

赞 4 收藏 3 评论 3

民工哥 发布了文章 · 10月16日

Linux 服务器高并发调优实战

众所周知在默认参数情况下Linux对高并发支持并不好,主要受限于单进程最大打开文件数限制、内核TCP参数方面和IO事件分配机制等。下面就从几方面来调整使Linux系统能够支持高并发环境。

iptables相关

如非必须,关掉或卸载iptables防火墙,并阻止kernel加载iptables模块。这些模块会影响并发性能。

单进程最大打开文件数限制

一般的发行版,限制单进程最大可以打开1024个文件,这是远远不能满足高并发需求的,调整过程如下:在#号提示符下敲入:

# ulimit –n 65535

将root启动的单一进程的最大可以打开的文件数设置为65535个。如果系统回显类似于“Operationnotpermitted”之类的话,说明上述限制修改失败,实际上是因为在中指定的数值超过了Linux系统对该用户打开文件数的软限制或硬限制。因此,就需要修改Linux系统对用户的关于打开文件数的软限制和硬限制。

  • 第一步,修改limits.conf文件,并添加:
# vim /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535

其中'*'号表示修改所有用户的限制;soft或hard指定要修改软限制还是硬限制;65536则指定了想要修改的新的限制值,即最大打开文件数(请注意软限制值要小于或等于硬限制)。修改完后保存文件。

  • 第二步,修改/etc/pam.d/login文件,在文件中添加如下行:
# vim /etc/pam.d/login
sessionrequired /lib/security/pam_limits.so

这是告诉Linux在用户完成系统登录后,应该调用pam_limits.so模块来设置系统对该用户可使用的各种资源数量的最大限制(包括用户可打开的最大文件数限制),而pam_limits.so模块就会从/etc/security/limits.conf文件中读取配置来设置这些限制值。修改完后保存此文件。

  • 第三步,查看Linux系统级的最大打开文件数限制,使用如下命令:
# cat/proc/sys/fs/file-max
32568

这表明这台Linux系统最多允许同时打开(即包含所有用户打开文件数总和)32568个文件,是Linux系统级硬限制,所有用户级的打开文件数限制都不应超过这个数值。

通常这个系统级硬限制是Linux系统在启动时根据系统硬件资源状况计算出来的最佳的最大同时打开文件数限制,如果没有特殊需要,不应该修改此限制,除非想为用户级打开文件数限制设置超过此限制的值。修改此硬限制的方法是修改/etc/sysctl.conf文件内fs.file-max= 131072 这是让Linux在启动完成后强行将系统级打开文件数硬限制设置为131072。修改完后保存此文件。

完成上述步骤后重启系统,一般情况下就可以将Linux系统对指定用户的单一进程允许同时打开的最大文件数限制设为指定的数值。

如果重启后用ulimit-n命令查看用户可打开文件数限制仍然低于上述步骤中设置的最大值,这可能是因为在用户登录脚本/etc/profile中使用ulimit-n命令已经将用户可同时打开的文件数做了限制。

由于通过ulimit-n修改系统对用户可同时打开文件的最大数限制时,新修改的值只能小于或等于上次ulimit-n设置的值,因此想用此命令增大这个限制值是不可能的。

所以,如果有上述问题存在,就只能去打开/etc/profile脚本文件,在文件中查找是否使用了ulimit-n限制了用户可同时打开的最大文件数量,如果找到,则删除这行命令,或者将其设置的值改为合适的值,然后保存文件,用户退出并重新登录系统即可。

通过上述步骤,就为支持高并发TCP连接处理的通讯处理程序解除关于打开文件数量方面的系统限制。

内核TCP参数方面

Linux系统下,TCP连接断开后,会以TIME_WAIT状态保留一定的时间,然后才会释放端口。当并发请求过多的时候,就会产生大量的TIME_WAIT状态的连接,无法及时断开的话,会占用大量的端口资源和服务器资源。这个时候我们可以优化TCP的内核参数,来及时将TIME_WAIT状态的端口清理掉。

下面介绍的方法只对拥有大量TIME_WAIT状态的连接导致系统资源消耗有效,如果不是这种情况下,效果可能不明显。可以使用netstat命令去查TIME_WAIT状态的连接状态,输入下面的组合命令,查看当前TCP连接的状态和对应的连接数量:

# netstat-n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
#这个命令会输出类似下面的结果:
LAST_ACK16
SYN_RECV348
ESTABLISHED70
FIN_WAIT1229
FIN_WAIT230
CLOSING33
TIME_WAIT18098

我们只用关心TIME_WAIT的个数,在这里可以看到,有18000多个TIME_WAIT,这样就占用了18000多个端口。要知道端口的数量只有65535个,占用一个少一个,会严重的影响到后继的新连接。这种情况下,我们就有必要调整下Linux的TCP内核参数,让系统更快的释放TIME_WAIT连接。

编辑配置文件:/etc/sysctl.conf,在这个文件中,加入下面的几行内容:

# vim /etc/sysctl.conf
net.ipv4.tcp_syncookies= 1
net.ipv4.tcp_tw_reuse= 1
net.ipv4.tcp_tw_recycle= 1
net.ipv4.tcp_fin_timeout= 30

输入下面的命令,让内核参数生效:

# sysctl-p

简单的说明上面的参数的含义:

net.ipv4.tcp_syncookies= 1
#表示开启SYNCookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse= 1
#表示开启重用。允许将TIME-WAITsockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle= 1
#表示开启TCP连接中TIME-WAITsockets的快速回收,默认为0,表示关闭;
net.ipv4.tcp_fin_timeout
#修改系統默认的TIMEOUT 时间。

在经过这样的调整之后,除了会进一步提升服务器的负载能力之外,还能够防御小流量程度的DoS、CC和SYN攻击。

此外,如果你的连接数本身就很多,我们可以再优化一下TCP的可使用端口范围,进一步提升服务器的并发能力。依然是往上面的参数文件中,加入下面这些配置:

net.ipv4.tcp_keepalive_time= 1200
net.ipv4.ip_local_port_range= 1024 65535
net.ipv4.tcp_max_syn_backlog= 8192
net.ipv4.tcp_max_tw_buckets= 5000

这几个参数,建议只在流量非常大的服务器上开启,会有显著的效果。一般的流量小的服务器上,没有必要去设置这几个参数。

net.ipv4.tcp_keepalive_time= 1200

表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。

ip_local_port_range= 1024 65535

表示用于向外连接的端口范围。缺省情况下很小,改为1024到65535。

net.ipv4.tcp_max_syn_backlog= 8192

表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。

net.ipv4.tcp_max_tw_buckets= 5000

表示系统同时保持TIME_WAIT的最大数量,如果超过这个数字,TIME_WAIT将立刻被清除并打印警告信息。默认为180000,改为5000。此项参数可以控制TIME_WAIT的最大数量,只要超出了。

内核其他TCP参数说明

net.ipv4.tcp_max_syn_backlog= 65535
#记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。
net.core.netdev_max_backlog= 32768
#每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。
net.core.somaxconn= 32768

例如web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值。

  • net.core.wmem_default= 8388608
  • net.core.rmem_default= 8388608
  • net.core.rmem_max= 16777216 #最大socket读buffer,可参考的优化值:873200
  • net.core.wmem_max= 16777216 #最大socket写buffer,可参考的优化值:873200
net.ipv4.tcp_timestsmps= 0

时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉。

net.ipv4.tcp_synack_retries= 2

为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。

net.ipv4.tcp_syn_retries= 2

在内核放弃建立连接之前发送SYN包的数量。

#net.ipv4.tcp_tw_len= 1
net.ipv4.tcp_tw_reuse= 1

开启重用。允许将TIME-WAITsockets重新用于新的TCP连接。

net.ipv4.tcp_wmem= 8192 436600 873200

TCP写buffer,可参考的优化值:8192 436600 873200

net.ipv4.tcp_rmem = 32768 436600 873200

TCP读buffer,可参考的优化值:32768 436600 873200

net.ipv4.tcp_mem= 94500000 91500000 92700000

同样有3个值,意思是:

  • net.ipv4.tcp_mem[0]:低于此值,TCP没有内存压力。
  • net.ipv4.tcp_mem[1]:在此值下,进入内存压力阶段。
  • net.ipv4.tcp_mem[2]:高于此值,TCP拒绝分配socket。上述内存单位是页,而不是字节。可参考的优化值是:7864321048576 1572864
net.ipv4.tcp_max_orphans= 3276800
  • 系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。
  • 如果超过这个数字,连接将即刻被复位并打印出警告信息。
  • 这个限制仅仅是为了防止简单的DoS攻击,不能过分依靠它或者人为地减小这个值,
  • 更应该增加这个值(如果增加了内存之后)。
net.ipv4.tcp_fin_timeout= 30

如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60秒。2.2 内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN-WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。

同时还涉及到一个TCP 拥塞算法的问题,你可以用下面的命令查看本机提供的拥塞算法控制模块:

sysctlnet.ipv4.tcp_available_congestion_control

对于几种算法的分析,详情可以参考下:TCP拥塞控制算法的优缺点、适用环境、性能分析,比如高延时可以试用hybla,中等延时可以试用htcp算法等。

如果想设置TCP 拥塞算法为hybla

net.ipv4.tcp_congestion_control=hybla

额外的,对于内核版高于于3.7.1的,我们可以开启tcp_fastopen:

net.ipv4.tcp_fastopen= 3

IO事件分配机制

在Linux启用高并发TCP连接,必须确认应用程序是否使用了合适的网络I/O技术和I/O事件分派机制。可用的I/O技术有同步I/O,非阻塞式同步I/O,以及异步I/O。在高TCP并发的情形下,如果使用同步I/O,这会严重阻塞程序的运转,除非为每个TCP连接的I/O创建一个线程。但是,过多的线程又会因系统对线程的调度造成巨大开销。因此,在高TCP并发的情形下使用同步I/O是不可取的,这时可以考虑使用非阻塞式同步I/O或异步I/O。非阻塞式同步I/O的技术包括使用select(),poll(),epoll等机制。异步I/O的技术就是使用AIO。

从I/O事件分派机制来看,使用select()是不合适的,因为它所支持的并发连接数有限(通常在1024个以内)。如果考虑性能,poll()也是不合适的,尽管它可以支持的较高的TCP并发数,但是由于其采用“轮询”机制,当并发数较高时,其运行效率相当低,并可能存在I/O事件分派不均,导致部分TCP连接上的I/O出现“饥饿”现象。而如果使用epoll或AIO,则没有上述问题(早期Linux内核的AIO技术实现是通过在内核中为每个I/O请求创建一个线程来实现的,这种实现机制在高并发TCP连接的情形下使用其实也有严重的性能问题。但在最新的Linux内核中,AIO的实现已经得到改进)。

综上所述,在开发支持高并发TCP连接的Linux应用程序时,应尽量使用epoll或AIO技术来实现并发的TCP连接上的I/O控制,这将为提升程序对高并发TCP连接的支持提供有效的I/O保证。

经过这样的优化配置之后,服务器的TCP并发处理能力会显著提高。以上配置仅供参考,用于生产环境请根据自己的实际情况调整观察再调整。

原文:https://www.cnblogs.com/txlsz...

image

查看原文

赞 13 收藏 10 评论 0

民工哥 发布了文章 · 10月15日

建议收藏!超全的 Linux Shell 文本处理工具集锦

本文将介绍Linux下使用Shell处理文本时最常用的工具:find、grep、xargs、sort、uniq、tr、cut、paste、wc、sed、awk;提供的例子和参数都是最常用和最为实用的;对shell脚本使用的原则是命令单行书写,尽量不要超过2行;如果有更为复杂的任务需求,还是考虑python吧.

1、find 文件查找

查找txt和pdf文件

find . ( -name "*.txt" -o -name "*.pdf" ) -print

正则方式查找.txt和pdf

find . -regex  ".*(.txt|.pdf)$"
#-iregex:忽略大小写的正则

否定参数:查找所有非txt文本

find . ! -name "*.txt" -print

指定搜索深度:打印出当前目录的文件(深度为1)

find . -maxdepth 1 -type f

定制搜索

#按类型搜索:
find . -type d -print  //只列出所有目录
#按时间搜索:
-atime 访问时间 (单位是天,分钟单位则是-amin,以下类似)
-mtime 修改时间 (内容被修改)
-ctime 变化时间 (元数据或权限变化)
最近7天被访问过的所有文件:
find . -atime 7 -type f -print
#按大小搜索:
寻找大于2k的文件
find . -type f -size +2k
#按权限查找:
find . -type f -perm 644 -print //找具有可执行权限的所有文件
#按用户查找:
find . -type f -user weber -print// 找用户weber所拥有的文件

找到后的后续动作删除:

#删除当前目录下所有的swp文件:
find . -type f -name "*.swp" -delete
#执行动作(强大的exec)
find . -type f -user root -exec chown weber {} ; //将当前目录下的所有权变更为weber
注:{}是一个特殊的字符串,对于每一个匹配的文件,{}会被替换成相应的文件名;
eg:将找到的文件全都copy到另一个目录:
find . -type f -mtime +10 -name "*.txt" -exec cp {} OLD ;

结合多个命令tips: 如果需要后续执行多个命令,可以将多个命令写成一个脚本。然后 -exec 调用时执行脚本即可;

-exec ./commands.sh {} ;
#-print的定界符
  • 默认使用' '作为文件的定界符;
  • -print0 使用''作为文件的定界符,这样就可以搜索包含空格的文件;

2、grep 文本搜索

grep match_patten file // 默认访问匹配行

常用参数:

  • -o 只输出匹配的文本行 VS -v 只输出没有匹配的文本行
  • -c 统计文件中包含文本的次数

grep -c "text" filename

  • n 打印匹配的行号
  • i 搜索时忽略大小写
  • l 只打印文件名

在多级目录中对文本递归搜索(程序员搜代码的最爱):

grep "class" . -R -n

匹配多个模式

grep -e "class" -e "vitural" file

grep输出以作为结尾符的文件名:(-z)

grep "test" file* -lZ| xargs -0 rm

xargs 命令行参数转换

xargs 能够将输入数据转化为特定命令的命令行参数;这样,可以配合很多命令来组合使用。比如grep,比如find;

将多行输出转化为单行输出

cat file.txt| xargs
  • 是多行文本间的定界符
  • 将单行转化为多行输出
cat single.txt | xargs -n 3
#-n:指定每行显示的字段数

xargs参数说明

  • -d 定义定界符 (默认为空格 多行的定界符为 )
  • -n 指定输出为多行
  • -I {} 指定替换字符串,这个字符串在xargs扩展时会被替换掉,用于待执行的命令需要多个参数时
cat file.txt | xargs -I {} ./command.sh -p {} -1
#-0:指定为输入定界符
#统计程序行数
find source_dir/ -type f -name "*.cpp" -print0 |xargs -0 wc -l

3、sort 排序

字段说明:

  • -n 按数字进行排序 VS -d 按字典序进行排序
  • -r 逆序排序
  • -k N 指定按第N列排序
sort -nrk 1 data.txt
sort -bd data // 忽略像空格之类的前导空白字符

4、uniq 消除重复行

消除重复行

sort unsort.txt | uniq

统计各行在文件中出现的次数

sort unsort.txt | uniq -c

找出重复行

sort unsort.txt | uniq -d

可指定每行中需要比较的重复内容:-s 开始位置 -w 比较字符数

5、用 tr 进行转换

通用用法

echo 12345| tr '0-9''9876543210' //加解密转换,替换对应字符
cat text| tr '    '' '  //制表符转空格

tr删除字符

cat file | tr -d '0-9'   // 删除所有数字

-c 求补集

cat file | tr -c  '0-9'   //获取文件中所有数字
cat file | tr -d -c '0-9'  //删除非数字数据

tr压缩字符

tr -s 压缩文本中出现的重复字符;最常用于压缩多余的空格
cat file | tr -s ' '

字符类:tr中可用各种字符类

  • alnum:字母和数字
  • alpha:字母
  • digit:数字
  • space:空白字符
  • lower:小写
  • upper:大写
  • cntrl:控制(非可打印)字符
  • print:可打印字符
使用方法:tr [:class:] [:class:]
eg: tr 
'[:lower:]''[:upper:]'

6、cut 按列切分文本

#截取文件的第2列和第4列:
cut -f2,4 filename
#去文件除第3列的所有列:
cut -f3 --complement filename
#-d 指定定界符:
cat -f2 -d ";" filename

cut 取的范围

  • N- 第N个字段到结尾
  • -M 第1个字段为M
  • N-M N到M个字段cut 取的单位
  • -b 以字节为单位
  • -c 以字符为单位
  • -f 以字段为单位(使用定界符)
cut -c1-5 file //打印第一到5个字符
cut -c-2 file  //打印前2个字符

7、paste 按列拼接文本

将两个文本按列拼接到一起

cat file1
1
2
cat file2
colin
book
paste file1 file2
1colin
2 book

默认的定界符是制表符,可以用-d指明定界符

paste file1 file2 -d ","
1,colin
2,book

8、wc 统计行和字符的工具

wc -l file // 统计行数
wc -w file // 统计单词数
wc -c file // 统计字符数

9、sed 文本替换利器

首处替换

sed 's/text/replace_text/'file   //替换每一行的第一处匹配的text

全局替换

sed 's/text/replace_text/g' file

默认替换后,输出替换后的内容,如果需要直接替换原文件,使用-i:

sed -i 's/text/repalce_text/g' file

移除空白行:

sed '/^$/d' file

变量转换

已匹配的字符串通过标记&来引用.

echo this is en example | seg 's/w+/[&]/g'
$>[this]  [is] [en] [example]

子串匹配标记

第一个匹配的括号内容使用标记 来引用

sed 's/hello([0-9])//'

双引号求值

  • sed通常用单引号来引用;也可使用双引号,使用双引号后,双引号会对表达式求值:
  • sed 's/$var/HLLOE/'当使用双引号时,我们可以在sed样式和替换字符串中指定变量;
p=patten
r=replaced
echo "line con a patten"| sed "s/$p/$r/g"
$>line con a replaced

字符串插入字符:将文本中每行内容(PEKSHA) 转换为 PEK/SHA

sed 's/^.{3}/&//g' file

10、awk 数据流处理工具

awk脚本结构

awk ' BEGIN{ statements } statements2 END{ statements } '

工作方式

  • 1.执行begin中语句块;
  • 2.从文件或stdin中读入一行,然后执行statements2,重复这个过程,直到文件全部被读取完毕;
  • 3.执行end语句块;

print 打印当前行,使用不带参数的print时,会打印当前行;

echo -e "line1 line2"| awk 'BEGIN{print "start"} {print } END{ print "End" }'
#print 以逗号分割时,参数以空格定界;
echo | awk ' {var1 = "v1" ; var2 = "V2"; var3="v3";
print var1, var2 , var3; }'
$>v1 V2 v3
#使用-拼接符的方式(""作为拼接符);
echo | awk ' {var1 = "v1" ; var2 = "V2"; var3="v3";
print var1"-"var2"-"var3; }'
$>v1-V2-v3

特殊变量:NR NF 1 $2

  • NR:表示记录数量,在执行过程中对应当前行号;
  • NF:表示字段数量,在执行过程总对应当前行的字段数;
  • $0:这个变量包含执行过程中当前行的文本内容;
  • $1:第一个字段的文本内容;
  • $2:第二个字段的文本内容;
echo -e "line1 f2 f3 line2 line 3"| awk '{print NR":"$0"-"$1"-"$2}' 
#打印每一行的第二和第三个字段
awk '{print $2, $3}' file 
#统计文件的行数:
awk ' END {print NR}' file 
#累加每一行的第一个字段:
echo -e "1 2 3 4 "| awk 'BEGIN{num = 0 ;print "begin";} {sum += $1;}END {print "=="; print sum }'   
#传递外部变量
var=1000
echo | awk '{print vara}' vara=$var #输入来自stdin
awk '{print vara}' vara=$var file # 输入来自文件

用样式对awk处理的行进行过滤

awk 'NR < 5' #行号小于5
awk 'NR==1,NR==4 {print}' file #行号等于1和4的打印出来
awk '/linux/'  #包含linux文本的行(可以用正则表达式来指定,超级强大)
awk '!/linux/'  #不包含linux文本的行

设置定界符

使用-F来设置定界符(默认为空格)

awk -F: '{print $NF}'/etc/passwd

读取命令输出

使用getline,将外部shell命令的输出读入到变量cmdout中;

echo | awk '{"grep root /etc/passwd" | getline cmdout; print cmdout }'

在awk中使用循环

for (i=0;i<10;i++){print $i;}
for (i in array){print array[i];}

以逆序的形式打印行:(tac命令的实现)

seq 9|awk '{lifo[NR] = $0; lno=NR}END{ for(;lno>-1;lno--){print lifo[lno];}} '

awk实现head、tail命令

head:
 awk 'NR<=10{print}' filename
tail:
  awk '{buffer[NR%10] = $0;} END{for(i=0;i<11;i++){
  print buffer[i %10]} } ' filename

打印指定列

#awk方式实现:
ls -lrt | awk 
'{print $6}'
#cut方式实现
ls -lrt | cut -f6

打印指定文本区域

#确定行号
seq 100| awk 'NR==4,NR==6{print}'
#确定文本
打印处于startpattern 和endpattern之间的文本;
awk '/start_pattern/, /end_pattern/' filename
seq 100| awk '/13/,/15/'
cat /etc/passwd| awk '/mai.*mail/,/news.*news/'

awk常用内建函数

  • index(string,search_string):返回search_string在string中出现的位置 sub(regex,replacement_str,string):将正则匹配到的第一处内容替换为replacement_str;
  • match(regex,string):检查正则表达式是否能够匹配字符串;
  • length(string):返回字符串长度
echo | awk '{"grep root /etc/passwd" | getline cmdout; print length(cmdout) }'
#printf 类似c语言中的printf,对输出进行格式化
seq 10| awk '{printf "->%4s ", $1}'  #迭代文件中的行、单词和字符
  1. 迭代文件中的每一行
while 循环法
while read line;
do
echo $line;
done < file.txt

改成子shell:

cat file.txt | (
while read line;
do
 echo $line;
done
)

awk法:

cat file.txt| awk '{print}'

2.迭代一行中的每一个单词

for word in $line;
do
echo $word;
done
  1. 迭代每一个字符{#word}:返回变量word的长度
for ((i=0;i<${#word};i++))
do
echo ${word:i:1);
done

作者 | 大CC
来源 | www.cnblogs.com/me15/p/3427319.html

image

查看原文

赞 29 收藏 24 评论 4

民工哥 发布了文章 · 10月14日

我敢打赌!这是全网最全的 Git 分支开发规范手册

Git 是目前最流行的源代码管理工具。为规范开发,保持代码提交记录以及 git 分支结构清晰,方便后续维护,现规范 git 的相关操作。

分支命名

1、master 分支

master 为主分支,也是用于部署生产环境的分支,确保master分支稳定性, master 分支一般由develop以及hotfix分支合并,任何时间都不能直接修改代码

2、develop 分支

develop 为开发分支,始终保持最新完成以及bug修复后的代码,一般开发的新功能时,feature分支都是基于develop分支下创建的。

feature 分支

  • 开发新功能时,以develop为基础创建feature分支。
  • 分支命名: feature/ 开头的为特性分支, 命名规则: feature/user_module、 feature/cart_module

release分支

release 为预上线分支,发布提测阶段,会release分支代码为基准提测。当有一组feature开发完成,首先会合并到develop分支,进入提测时会创建release分支。如果测试过程中若存在bug需要修复,则直接由开发者在release分支修复并提交。当测试完成之后,合并release分支到master和develop分支,此时master为最新代码,用作上线。

hotfix 分支

分支命名: hotfix/ 开头的为修复分支,它的命名规则与feature分支类似。线上出现紧急问题时,需要及时修复,以master分支为基线,创建hotfix分支,修复完成后,需要合并到master分支和develop分支

常见任务

增加新功能

(dev)$: git checkout -b feature/xxx            # 从dev建立特性分支
(feature/xxx)$: blabla                         # 开发
(feature/xxx)$: git add xxx
(feature/xxx)$: git commit -m 'commit comment'
(dev)$: git merge feature/xxx --no-ff          # 把特性分支合并到dev

修复紧急bug

(master)$: git checkout -b hotfix/xxx         # 从master建立hotfix分支
(hotfix/xxx)$: blabla                         # 开发
(hotfix/xxx)$: git add xxx
(hotfix/xxx)$: git commit -m 'commit comment'
(master)$: git merge hotfix/xxx --no-ff       # 把hotfix分支合并到master,并上线到生产环境
(dev)$: git merge hotfix/xxx --no-ff          # 把hotfix分支合并到dev,同步代码

测试环境代码

(release)$: git merge dev --no-ff             # 把dev分支合并到release,然后在测试环境拉取并测试

生产环境上线

(master)$: git merge release --no-ff          # 把release测试好的代码合并到master,运维人员操作
(master)$: git tag -a v0.1 -m '部署包版本名'  #给版本命名,打Tag

日志规范

在一个团队协作的项目中,开发人员需要经常提交一些代码去修复bug或者实现新的feature。

而项目中的文件和实现什么功能、解决什么问题都会渐渐淡忘,最后需要浪费时间去阅读代码。但是好的日志规范commit messages编写有帮助到我们,它也反映了一个开发人员是否是良好的协作者。

编写良好的Commit messages可以达到3个重要的目的:

  • 加快review的流程
  • 帮助我们编写良好的版本发布日志
  • 让之后的维护者了解代码里出现特定变化和feature被添加的原因

目前,社区有多种 Commit message 的写法规范。来自Angular 规范是目前使用最广的写法,比较合理和系统化。如下图:

Commit messages的基本语法

当前业界应用的比较广泛的是 Angular Git Commit Guidelines

https://github.com/angular/an...

具体格式为:

<type>: <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
  • type: 本次 commit 的类型,诸如 bugfix docs style 等
  • scope: 本次 commit 波及的范围
  • subject: 简明扼要的阐述下本次 commit 的主旨,在原文中特意强调了几点:
  • 使用祈使句,是不是很熟悉又陌生的一个词
  • 首字母不要大写
  • 结尾无需添加标点

body: 同样使用祈使句,在主体内容中我们需要把本次 commit 详细的描述一下,比如此次变更的动机,如需换行,则使用 |

footer: 描述下与之关联的 issue 或 break change

Type的类别说明:

  • feat: 添加新特性
  • fix: 修复bug
  • docs: 仅仅修改了文档
  • style: 仅仅修改了空格、格式缩进、都好等等,不改变代码逻辑
  • refactor: 代码重构,没有加新功能或者修复bug
  • perf: 增加代码进行性能测试
  • test: 增加测试用例
  • chore: 改变构建流程、或者增加依赖库、工具等

Commit messages格式要求

# 标题行:50个字符以内,描述主要变更内容
#
# 主体内容:更详细的说明文本,建议72个字符以内。需要描述的信息包括:
#
# * 为什么这个变更是必须的? 它可能是用来修复一个bug,增加一个feature,提升性能、可靠性、稳定性等等
# * 他如何解决这个问题? 具体描述解决问题的步骤
# * 是否存在副作用、风险?
#
# 如果需要的化可以添加一个链接到issue地址或者其它文档

来源:https://juejin.im/post/684490...

image

查看原文

赞 33 收藏 26 评论 3

民工哥 发布了文章 · 10月13日

Github 星标 8K+ 这款国人开源的 Redis 可视化管理工具,真香...

做程序员就少不了与一些工具打交道,比如:监控工具、管理工具等,有些工具是命令行界面,有些工具是可视化界面,反正都是可以能够满足日常使用的功能需求。

对于redis管理工具来说,也有不少可能的产品,比如:

  • 1、Redis Desktop Manager
  • 2、RedisStudio
  • 3、phpRedisAdmin
  • 4、Go-Redis
  • 5、RedisClient

有开源、免费的,也有商业收费的版本,基本上功能都还可以,不过开源、免费还是最大的需求点之一哈,老司机都懂得的。

所以,今天,民工哥就给大家安利一款由国人开源的Redis可视化管理工具,名字很有意思,叫:Another Redis Desktop Manager。

Github:https://github.com/qishibo/An...

Another Redis Desktop Manager简述

Another Redis Desktop Manager是一个更快,更好,更稳定的Redis桌面管理器,与Linux,Windows和Mac兼容。而且,加载大量密钥时,它不会崩溃。

Another Redis Desktop Manager安装

这类的软件、工具安装都非常的简单,易操作。

1、Mac或Linux安装

# clone code
git clone https://github.com/qishibo/AnotherRedisDesktopManager.git
cd AnotherRedisDesktopManager
# install dependencies
npm install
# if download electron failed during installing, use this command
# ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/" npm install
# serve with hot reload at localhost:9988
npm start
# after the previous step is completed, open another tab, build up a desktop client
npm run electron

如果出现下面的报错信息,官方也比较贴心给出了解决方案。

# if error like this
../src/FontManagerLinux.cc:1:35: fatal error: fontconfig/fontconfig.h: No such file or directory
# then try this
yum install libfontconfig1-dev -y

2、Windows安装

# install build tools for the first time, just execute once
npm install -g windows-build-tools
# clone code
git clone https://github.com/qishibo/AnotherRedisDesktopManager.git
cd AnotherRedisDesktopManager
# install dependencies, 32-bit or 64-bit all use win32
npm install --platform=win32
# if download electron failed during installing, use this command
# npm config set ELECTRON_MIRROR http://npm.taobao.org/mirrors/electron/
# npm install --platform=win32
# serve with hot reload at localhost:9988
npm start
# after the previous step is completed to 100%, open another tab, build up a desktop client
npm run electron

当然,WIN系统你也可以直接下载.exe的软件包,双击运行安装更方便。

https://github.com/qishibo/An...

Another Redis Desktop Manager功能展示

1、主界面

image.png

支持:SSH、SSL、Cluster等几种模式。集群可以输入任意集群中的节点都可以自动识别出来。

2、操作界面

image.png

更多实用的功能,大家感兴趣的可以下载下来,亲自使用体验一下。

image

查看原文

赞 4 收藏 2 评论 5

民工哥 发布了文章 · 10月13日

推荐 22 款好用的 CLI 工具

image.png

_作者:switowski
策划:万佳
原文链接:https://switowski.com/blog/fa...

作者根据多年的终端使用经验,详细介绍了一些实用的 CLI 工具,希望它们能帮读者提高生产力。

我大部分的时间都花费在终端的使用上,我觉得有必要给大家推荐一下比较好用的终端工具。先给大家列个推荐清单,如下图。

image.png

高频 CLI 工具推荐

1、fish shell

Shell- 毋庸置疑,在终端中,Shell 是使用最频繁也最重要的工具。过去,我曾经使用过 Bash 和 Z Shell,而如今,我正在使用的是 Fish Shell。这是一个非常优秀的终端 Shell 工具,拥有许多开箱即用的功能,例如语法自动推荐补全、语法高亮显示或使用快捷键在最近访问的文件夹之间来回切换。

image.png

一方面,它非常适合初学者使用,因为使用者无需进行任何设置。另一方面,由于它使用的脚本语法与其他 Shell 有所差异,因此通常用户不能把拷贝自网上的脚本直接粘贴使用。你必须将不兼容的命令更改为合法的 Fish 脚本,或者启动一个 Bash 会话以运行 Bash 脚本。

https://fishshell.com/docs/cu...

我能理解这种更改背后的原因(毕竟 Bash 脚本不是易于用户使用的语言),但这种不兼容丝毫没有给我带来任何好处。我平时很少编写 Bash / Fish 脚本,所以经常遗忘这些语法,因此每次要使用这些脚本时我总是必须从头开始重新学习它。与 Bash 脚本相比,Fish 脚本的资源相对更少。我通常不会去阅读文档,重复造轮子,而是从 StackOverflow 复制粘贴现成的脚本拿来即用。

虽然前面我提到了 Fish Shell 的几个缺点,但是我还是会推荐你去用一下,因为切换 Shell 工具十分简单,所以很值得你去尝试一下。特别是当你懒得自己去配置 Shell,并希望通过最少的配置就能获得很好的使用效果的时候,那就更不要错过它了。

Fish插件

你可以自己添加相关插件来扩展 Fish Shell 的功能。最简单的安装插件的方法就是使用插件管理工具,比如 Fisher、Oh My Fish 或者 fundle。现在,我使用的插件管理工具是 Fisher,我用它安装管理了三个插件:

  1. franciscolourenco/done ——在长时间运行的脚本完成后发送通知。
  2. evanlucas/fish-kubectl-completions——1个自动补全 kubectl(Kubernetes command line tool) 命令的插件。
  3. fzf——将 fzf 工具与 Fish 集成在一起的插件。

过去,我有使用很多的插件(比如 rbenv、pyenv、nodenv、fzf、z),但是我改用其他工具以避免影响我的 Shell 的运行速度(这是我过去使用 Z shell 所得到的一个教训)。

下载地址:https://fishshell.com/

2、Starship

如果必须要从本篇文章中选择一个我最喜欢的终端工具——那非 Starship 莫属。Starship 可以适用于任何 Shell。你只需要安装它,然后在相应的配置文件.bashrc/.zshrc/config.fish添加一行配置,剩下的工作交给它来完成就好了。
image.png

它可以做到:

  • 根据你是否在代码仓库中添加了新文件、是否修改了文件、是否暂存了文件等情况,用相应的符号表示 git 仓库的状态。
  • 根据你所在的 Python 项目目录,展示 Python 的版本号,这也适用于 Go/Node/Rust/Elm 等其他编程语言环境。
  • 展示上一个命令执行所用的时间,指令运行时间必须在毫秒级别。
  • 如果上一个命令执行失败,会展示相应的错误提示符。

image.png

还有不计其数的其他信息可以展示。但是,它能以更加友好的形式智能地给你呈现!比如,如果你不在 git 存储库中,它将隐藏 git 信息。如果您不在 Python 项目中,则不会有 Python 版本信息,因为显示它没有什么意义。它永远不会给你展示多余信息,始终保持终端的美观,优雅和简约。

Starship 的运行速度怎么样呢?它是用 Rust 编写的,尽管功能如此之多,但仍然比我以前使用的所有提示工具都要快!我对提示信息非常洁癖,因此我经常破解自己的版本。我会根据现有的提示找到对应的功能代码,然后将其粘组合在一起,以确保 Starship 只有我需要的功能以保持其快速运行。“外部工具永远无法比我精心制作的提示工具更快!” 这就是我对 Starship 持怀疑态度的原因。

下载地址:https://starship.rs/

3、z

“z”可以让你快速地在文件目录之间跳转。它会记住你访问的历史文件夹,经过短暂的学习后,你就可以使用z path_of_the_folder_name命令在目录之间跳转了。

比如,如果我经常访问 ~/work/src/projects,我只需要运行 z pro ,就可以立马跳转到那里。z 的原理参考了 frecency 算法——一个基于统计 frequency 和 recency 进行分析的算法。如果它存储了你不想使用的路径文件夹,你随时可以手动将其删除。它提高了我在常用的不同文件路径之间频繁切换的效率,帮我节省了键盘击键次数以及大量的路径记忆。

下载地址:https://github.com/rupa/z

4、fzf

fzf— fuzzy finder,即模糊查找器。它是一种通用工具,可让你使用模糊搜索来查找文件、历史命令、进程、git 提交等。你键入一些字母,它会尝试匹配结果列表中任何位置的字母。输入的字母越多,结果也就越准确。你可能在其他的代码编辑器中有过这种类型的搜索使用体验——当你想打开某个文件时,只键入文件名的一部分而不用输入完整路径就能进行查找——这就是模糊搜索。

image.png

我通过 fish fzf 插件插件使用它,因此我可以搜索命令历史记录或快速打开文件。这是可以每天为我节省不少时间的一个非常棒的工具。

https://github.com/jethrokuan...

下载地址:https://github.com/junegunn/fzf

5、fd

上面动图是 find 命令(左)和 fd 命令(右)的使用对比。

类似于系统自带的 find 命令,但使用起来更简单,查找速度更快,并且具有良好的默认设置。

不管你想找到一个名为“invoice”的文件,但是不确定文件的扩展名,还是查找一个存放所有 invoice 的目录,而不单是一个文件。你可以撸起袖子,开始为 find 命令编写那些复杂的正则表达式,也可以直接命令行运行 fd invoice。反正对我来说,我只选择最简单的那个。

默认情况下,fd 会忽略隐藏的以及在.gitignore列出的文件和目录。大多数时候,这也是我们想要的,但是在极少数特殊情况下,如果需要禁用此功能时,我会给该命令设置一个别名:fda='fd -IH'

你会发现,fd 命令输出的颜色配置很漂亮,而且根据基准测试(上述 GIF),它的执行速度甚至比find 命令的还要快。

下载地址:https://github.com/sharkdp/fd

6、ripgrep

上图为 grep(左)与 rg(右)命令执行时的对比。

与上述fd指令类似,ripgrepgrep命令的替代方法, 不过ripgrep的执行速度更快,而且具有健全的默认配置以及丰富的彩色输出。

它同样会跳过被.gitignore忽略以及隐藏的文件,因此如果有特殊需要,我们可以设置指令别名:rga ='rg -uuu'。它会禁用所有智能筛选,并使ripgrep的表现与标准的 grep 指令一致。

下载地址:https://github.com/BurntSushi...

7、htop 和 glances

在 Linux 或 Mac 上显示进程运行状态信息最常用工具是我们熟悉的top,它是每位系统管理员的好帮手。而且,即使是像我一样主要从事网络开发,查看计算机的运行状况也很有用。你知道,只是看一下当前到底是 Docker 进程还是 Chrome 进程吃掉了你所有的 RAM,应该如何做吗?
image.png

htop工具是top工具的绝佳替代品。

top工具是非常基础的监控工具,提供的功能有限,因此很多人转去使用 htop。htop比起top,优势很明显——除了功能更加完善以外,它的色彩搭配也很丰富,整体上使用起来更加友好。

image.png

借助 glances,还可以让你一目了然地快速了解系统当前状态。

glances 是htop的补充工具。除了列出所有进程及其 CPU 和内存使用情况之外,它还可以显示有关系统的其他信息,比如:

  • 网络及磁盘使用情况
  • 文件系统已使用的空间和总空间
  • 来自不同传感器(例如电池)的数据
  • 以及最近消耗过多资源的进程列表

我选择使用htop来筛选和终止进程,因为对我来讲,效率提高了不少,我也使用 glances可以快速浏览一下计算机的运行状况。它提供 API 接口、Web UI 以及支持各种导出格式,因此你可以将系统监视提高到一个新 Level。因此我在这里强烈推荐一波!

htop 下载地址:https://hisham.hm/htop/

glances 下载地址:

https://nicolargo.github.io/g...

8、virtualenv 和 virtualfish

Virtualenv 是用于在 Python 中创建虚拟环境的工具(比起内置的venv模块,我更喜欢 Virtualenv)。

image.png

VirtualFish 是 Fish Shell 的虚拟环境管理器(如果你不使用 Fish Shell,请查看 virtualenvwrapper)。它提供了许多命令来执行快速创建、列出或删除虚拟环境等操作。

virtualenv 下载地址:

https://pypi.org/project/virt...

virtualfish 下载地址:

https://github.com/justinmaye...

9、pyenv、nodenv 和 rbenv

pyenv 可以轻松实现 Python 版本的切换。

image.png

Pyenv、nodenv 和 rubyenv 是用于管理计算机上不同版本的 Python、Node 和 Ruby 的工具。

假设你要在计算机上安装两个版本的 Python。比如,你正在从事两个不同的 Python 项目,或者因为特殊情况仍然需要使用 Python2。不同 Python 版本在电脑上管理很复杂。你需要确保不同的项目具有正确版本的软件依赖包。如果你不小心的话,很容易弄乱这种脆弱的配置并被其他软件包使用的二进制文件所覆盖。

该工具为版本管理提供了很多帮助,并将这一噩梦变得易于管理。它可以全局或“按文件夹”切换 Python 版本,而且每个版本都是相互隔离的。

我最近找到了一种名为 asdf 的工具,该工具可以将 pyenv、nodenv、rbenv 及其他 env 进行统一管理。它提供了几乎所有编程语言的版本管理,下次我需要为编程语言设置版本管理器时,一定会尝试使用一下。

pyenv 下载地址:https://github.com/pyenv/pyenv

nodenv 下载地址:https://github.com/nodenv/nodenv

rbenv 下载地址:https://github.com/rbenv/rbenv

10、pipx

Virtualenv 解决了 Python 程序包管理中的许多问题,但是还有一个方案可以解决。如果我想在全局环境下安装 Python 软件包(比如它是一个独立的工具,正如前面提到的glances 工具),那么我会遇到全局安装带来的问题。在虚拟环境之外安装软件包不是一个好主意,将来可能会导致意想不到的问题。另一方面,如果我决定使用虚拟环境,那么每次我要运行程序时都需要激活该虚拟环境。这也不是最方便的解决方案。

image.png

事实证明,pipx工具可以解决上面提到的问题。它将 Python 软件依赖包安装到单独的环境中(因此不会存在依赖项冲突的问题)。与此同时,这些工具提供的 CLI 命令在全局环境内也可用。因此,我无需激活任何环境——pipx会帮我完成这个操作!

如果你想了解有关 Python 工具的更多信息并想了解如何使用它们,我为 PyCon 2020 会议制作了一个名为“现代 Python 开发人员工具包”的视频。

这是一个长达两个小时的视频教程,内容涉及如何设置 Python 开发环境,要使用的工具以及如何从头开始制作 TODO 应用程序(包括测试和文档)。你可以在 YouTube 上进行观看。

https://www.youtube.com/watch...

pipx 下载地址:

https://github.com/pipxprojec...

11、ctop 和 lazydocker

ctop 的实时监控示例

当你使用 Docker 并对其监控时,这两个工具会很有帮助。ctop是 Docker 容器的顶级接口。它可以为你:

  • 展示正在运行和已停止的容器列表。
  • 展示统计信息,例如内存、CPU 使用率以及针对每个容器的其他详细信息窗口(例如绑定的端口等其他信息)。
  • 提供快捷菜单,方便快速停止、杀掉指定容器进程或显示给定容器的日志。

这比你尝试从docker ps命令中找出所有这些信息要方便多了。

lazydocker是我最喜欢的 Docker 工具

如果你认为ctop很酷,请你尝试使用 lazydocker 后再做决定!它是一个非常成熟的拥有终端 UI 界面的工具,提供了非常丰富的功能用于管理 Docker。这是我最喜欢的 Docker 管理工具!

ctop 下载地址:https://github.com/bcicen/ctop

lazydocker 下载地址:

https://github.com/jesseduffi...

低频 CLI 工具推荐

除了几乎每天都在使用的工具以外,我多年来还收集了一些给力的工具,这些工具对于一些特定需求非常好用。比如有的终端工具可以用来将终端操作记录成 GIF(并且可以让你在 GIF 中暂停和复制文本!),还有的终端工具可以用于列出目录结构、连接数据库等,下面我会一一介绍。

12、Homebrew

image.png

如果你使用的是 Mac,那我就无需再介绍 Homebrew 了。它是 macOS 上被业界普遍认可的软件包管理器。对了,它还有一个称为 Cakebrew 的 GUI 版本软件,如果感兴趣你可以尝试一下。

下载地址:https://brew.sh/

13、asciinema

image.png

asciinema是可用于记录终端会话的工具。但是,与录制 GIF 不同,它可以让用户选择并复制这些录制中的代码!

这对于录制编码教程来说十分好用。你应该遇到那种尴尬的情况——当你准备跟着视频教程在终端中敲巨长的命令,但是讲师并为你提供这个代码段,你不得不花费很长的时间去整理这些冗长的命令。asciinema录制的内容,支持直接复制,十分给力。

下载地址:https://asciinema.org/

14、colordiff 和 diff-so-fancy

image.png

我很少在终端中使用diff操作(比较两个文件之间的差异),但是如果你需要执行这个操作,可以放弃使用diff命令,而是使用 colordiffcolordiff输出可以高亮显示,因此在查看文件差异内容时要方便得多,而不是在diff命令输出内容下,费力地查看所有的“ <”和“>”符号来对比文件差异。

如果你觉得还不够,那么我推荐给你 diff-so-fancy。它是比colordiff更友好的一个差异对比工具。

image.png

它通过以下方式进一步改善了文件内容差异展示的外观:

  • 突出显示每一行中差异的单词,而不是整行
  • 简化变更文件的标题
  • 去除 + 和 - 符号(颜色差异展示就够了)
  • 清楚地指出新行和删除的空行

colordiff 下载地址:https://www.colordiff.org/

diff-so-fancy 下载地址:https://github.com/so-fancy/d...

15、tree

你可以通过brew install tree安装该工具。如果要查看给定目录的内容,那么 tree 是执行此操作的必备工具。它能以漂亮的树状结构显示所有子目录及文件:

$ tree .
.
├── recovery.md
├── README.md
├── archive
├── automator
│   ├── Open Iterm2.workflow
│   │   └── Contents
│   │       ├── Info.plist
│   │       ├── QuickLook
│   │       │   └── Thumbnail.png
│   │       └── document.wflow
│   └── Start Screen Saver.workflow
├── brew-cask.sh

16、bat

image.png

类似于在终端中常用的用于显示文件内容的cat命令,但是bat效果更佳。

它增加了语法高亮显示,git gutter 标记(如果适用),自动分页(如果文件很大)等功能,并且使得输出的内容阅读起来更加友好。

bat 下载地址:https://github.com/sharkdp/bat

17、httpie

如果你需要发送一些 HTTP 请求,但发现使用curl不够直观,那么请尝试一下httpie。这是一款非常好用的curl替代工具。合理的默认配置以及简洁的语法使它更易于使用,命令返回也是彩色输出,甚至支持为不同类型的身份验证安装相应的插件。

httpie 下载地址:https://httpie.org/

18、tldr

简化版的命令帮助手册。“man pages” 包含了 Linux 软件的手册,这些手册解释了如何使用给定的命令。你可以尝试运行man catman grep来查看相关命令的帮助手册。它们描述的非常详细,有时可能难以掌握。因此,tldr社区的目的,就是将每个命令的帮助手册进行简化,方便用户查阅。

tldr适用于几乎所有的受欢迎的软件。正如我提到的,这是社区的努力和功劳,虽然不太可能包含所有的软件的简化帮助手册。但是当某个帮助手册被纳入管理并起作用时,它提供的信息通常就是你要查找的内容。

比如,如果你要创建一些文件的 gzip 压缩存档,man tar可以为你提供可能的参数选择。而tldr tar会列出一些我们常见的示例——如图所示,第二个示例正是你要执行的操作:

image.png

“man pages”展示的信息太全面了,但是很多时候使用tldr可以更快地帮你找到特定信息,这才是用户真正想要的。

tldr 下载地址:https://tldr.sh/

19、exa

image.png

exals命令的一个可替代方案。

它色彩艳丽,还可以显示 git 状态等其他信息,自动将文件大小转换为方便人们阅读的单位,并且所有这些都保持与ls几乎相同的执行速度。虽然我很喜欢这个工具并推荐给你们,但由于某种原因,我仍然坚持使用 ls。

exa 下载地址:https://the.exa.website/

20、litecli 和 pgcli

这是我首选的 SQLite 和 PostgreSQL CLI 的解决方案。借助自动提示和语法突出显示,它们比默认的sqlite3psql工具要好用很多。

litecli 下载地址:https://litecli.com/

pgcli 下载地址:https://www.pgcli.com/

21、mas

image.png

mas是一个用于从 App Store 安装软件的 CLI 工具。我目前为止,我仅仅使用过它一次——设置我的 Macbook 电脑软件。将来,我也将使用它来设置我的下一台 Macbook。mas可让你自动在 macOS 中安装软件。它解放了你大量的点击操作。而且,鉴于你正在阅读这篇有关 CLI 工具的文章,所以我大胆地认为,大家都和我一样,不喜欢无聊的单击操作。

我在“灾难修复”脚本中保留了从 App Store 安装的应用程序列表。如果我的电脑真的发生了什么意外情况,我希望能够以最小的代价重新安装所有内容。

mas 下载地址:https://github.com/mas-cli/mas

22、ncdu

image.png

这是在终端进行磁盘分析时使用的工具,它使用起来简单快捷。当我需要释放一些硬盘空间时,会默认使用这款工具。

ncdu 下载地址:https://dev.yorhel.nl/ncdu

23、总结

以上推荐工具清单确实很长,但是我希望有一些工具真的能够带给你方便,提高你的生产力。fdripgrephttpie等工具可能是你以前熟悉的工具的改进版本。这些工具的改进版本除了更易于使用之外,它们还提供更友好的输出,执行速度甚至更快。所以,我们要多多尝试并接受新的事物,不要仅仅因为大家都在使用旧工具而只局限在旧工具的使用上。事物都是在向前发展的,穷则变,变则通,通则久。大家一起共勉。

image

查看原文

赞 7 收藏 6 评论 0