docker容器调试利器nicolaka/netshoot

背景

在日常工作中,我们一般会将容器进行精简,将其大小压缩到最小,以此来提高容器部署效率,参考小米云技术 – Docker最佳实践:5个方法精简镜像。但有一个比较尴尬的问题就是对容器排障,由于容器里没有了我们日常工作中用到许多排障命令,如top、ps、netstat等,所以想排除故障的话,常用的做法是安装对应的命令,如果容器过多的话,再这样搞就有些麻烦了,特别是在一些安装包源速度很慢的情况。

解决方案

今天发现一篇文章(简化 Pod 故障诊断:kubectl-debug 介绍)介绍针对此类问题的解决方案的,这里介绍的是一个叫做 kubectl-debug 的命令,主要由国内知名的PingCAP公司出品的,主要是用在k8s环境中的。我们知道容器里主要两大技术,一个是用cgroup来实现容器资源的限制,一个是用Namespace来实现容器的资源隔离的)。(kubectl-debug 命令是基于一个工具包(https://github.com/nicolaka/netshoot) 来实现的,其原理是利用将一个工具包容器添加到目标容器所在的Pod里,实现和目标容器的Namespace一致,从而达到对新容器进程可见性,这样我们就可以直接在目标容器里操作这些命令。所以在平时开发环境中,可以很方便的直接使用这个工具包来实现对容器的排障。

netshoot包含一组强大的工具,如图所示

工具包清单

apache2-utils
bash
bind-tools
bird
bridge-utils
busybox-extras
calicoctl
conntrack-tools
ctop
curl
dhcping
drill
ethtool
file
fping
iftop
iperf
iproute2
iptables
iptraf-ng
iputils
ipvsadm
libc6-compat
liboping
mtr
net-snmp-tools
netcat-openbsd
netgen
nftables
ngrep
nmap
nmap-nping
openssl
py-crypto
py2-virtualenv
python2
scapy
socat
strace
tcpdump
tcptraceroute
util-linux
vim

看了上图不得不说几乎包含了所有的调度命令。

使用也很方便,只需要一条命令即可

$ docker run -it --net container:<container_name> nicolaka/netshoot

参数 --net--network 的缩写,有四种值,用法参考:https://docs.docker.com/engine/reference/run/#network-settings

执行命令后,会自动创建一个镜像为 nicolaka/netshoot 的容器,并共享一个网络栈,即Namespace 和目标容器的一样,并自动进入到目标容器 里。如果执行命令 hostname 命令的话,结果显示的是目标 “容器ID” 。此时就可以在容器里执行常用的一些ps、 top、netstat、iftop 之类的命令。

➜  ~ docker run -it --rm --network container:mysql80 nicolaka/netshoot
                    dP            dP                           dP
                    88            88                           88
88d888b. .d8888b. d8888P .d8888b. 88d888b. .d8888b. .d8888b. d8888P
88'  `88 88ooood8   88   Y8ooooo. 88'  `88 88'  `88 88'  `88   88
88    88 88.  ...   88         88 88    88 88.  .88 88.  .88   88
dP    dP `88888P'   dP   `88888P' dP    dP `88888P' `88888P'   dP

Welcome to Netshoot! (github.com/nicolaka/netshoot)
root @ /
 [1] 🐳  → ls
bin    dev    etc    home   lib    lib64  media  mnt    opt    proc   root   run    sbin   srv    sys    tmp    usr    var

root @ /
 [2] 🐳  → hostname
7ff422d3f75d

root @ /
 [3] 🐳  → ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/bash -l
   15 root      0:00 ps

root @ /
 [4] 🐳  → netstat -an | grep LISTEN
tcp        0      0 :::33060                :::*                    LISTEN
tcp        0      0 :::3306                 :::*                    LISTEN
unix  2      [ ACC ]     STREAM     LISTENING      18007 /var/run/mysqld/mysqld.sock
unix  2      [ ACC ]     STREAM     LISTENING      19694 /var/run/mysqld/mysqlx.sock

root @ /
 [5] 🐳  →

上面我们添加了--rm 参数,主要是为了使用完毕后,及时清除临时容器相关的资源。这里我们将临时容器的Namespace和mysql80 容器相同

mysql80容器的ID为 7ff422d3f75d,即hostname 命令的输出结果。

现在我们先不要从这个容器里退出,再开启一个新终端进入到临时容器(bb47226c955e)里

➜  ~ docker exec -it bb4 /bin/bash
bash-5.0# hostname
7ff422d3f75d
bash-5.0# ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/bash -l
   22 root      0:00 /bin/bash
   28 root      0:00 ps
bash-5.0# netstat -an | grep LISTEN
tcp        0      0 :::33060                :::*                    LISTEN
tcp        0      0 :::3306                 :::*                    LISTEN
unix  2      [ ACC ]     STREAM     LISTENING      18007 /var/run/mysqld/mysqld.sock
unix  2      [ ACC ]     STREAM     LISTENING      19694 /var/run/mysqld/mysqlx.sock
bash-5.0#

从hostname和进程ID的结果来看,进入的还是msyql80容器。

工具包容器的作用就像将里面的内容合并到了目标容器里一样。使用到了--net 参数。

最后执行 exit 或者 logout 命令退出容器,此时临时容器及其volume将自动被删除(参数–rm)。

总结

这里主要考察了容器的Namespace隔离性的原理,通过将不同容器的Namespace和其它容器的Namespace保持一致,从而实现了对进程的可见性。

问题延伸

这里抛出另一个问题,如果两个容器都存在一个一模一样的进程,这个时候会出错吗?如果不会的话,为什么(应用存储路径不一样?或者进程PID不一样)?我们可见的是哪个容器的里程,临时容器还是目标容器呢?

参考:
https://blog.csdn.net/xbw_linux123/article/details/81873490

基于docker环境实现Elasticsearch 集群环境

最近搭建了es集群的时候,现在需要测试添加一个新的数据节点,项目是使用docker-compose命令来搭建的。

以下基于最新版本 es7.2.0进行, 配置文件目录为 es, 所以docker 在创建网络的时候,网络名称会以 es_ 前缀开始,如本例中我们在docker-composer.yaml文件中指定了网络名称为esnet,但docker生成的实例名称为 es_esnet,至于网络相关的信息可以通过 docker network --help 查看。

搭建es集群

// docker-compose.yaml 集群配置文件

version: '2.2'
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
    container_name: es01
    environment:
      - node.name=es01
      - node.master=true
      - node.data=true
      - discovery.seed_hosts=es02
      - cluster.initial_master_nodes=es01,es02
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - esnet
  es02:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
    container_name: es02
    environment:
      - node.name=es02
      - discovery.seed_hosts=es01
      - cluster.initial_master_nodes=es01,es02
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata02:/usr/share/elasticsearch/data
    networks:
      - esnet
  es03:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
    container_name: es03
    environment:
      - node.name=es03
      - discovery.seed_hosts=es01
      - cluster.initial_master_nodes=es01,es02
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata03:/usr/share/elasticsearch/data
    networks:
      - esnet
volumes:
  esdata01:
    driver: local
  esdata02:
    driver: local
  esdata03:
    driver: local

networks:
  esnet:

集群配置了3个master节点,并同时作为数据节点使用,当节点未指定 node.master和node.data的时候,默认值为 true 。执行命令

$ docker-compose up

启动集群。

验证集群环境是否搭建成功,在浏览器里访问 http://localhost:9200 http://localhost:9200/_cat/nodes?v 显示正常。三个节点角色均为mdi

ip           heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.16.3           28          74   3    0.14    0.94     4.97 mdi       *      es02
192.168.16.4           28          74   3    0.14    0.94     4.97 mdi       -      es01
192.168.16.2           25          74   3    0.14    0.94     4.97 mdi       -      es03

添加集群新的节点

添加es数据节点文件 join-docker-compose.yaml

version: '2.2'
services:
  es04:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
    container_name: es04
    environment:
      - node.name=es04
      - node.master=false
      - node.data=true
      - cluster.initial_master_nodes=es01,es02
      - cluster.name=docker-cluster
      - discovery.seed_hosts=es01
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata04:/usr/share/elasticsearch/data
    networks:
      - esnet

  
volumes:
  esdata04:
    driver: local

networks:
  esnet:
    external:
      name: es_esnet

这里指定了 node.master=false,执行命令

$ docker-compose -f join-docker-compose.yaml up

注意这里手动指定了 yaml 文件,两个配置文件都在同一个es目录里。

再次使用上面的 http://localhost:9200/_cat/nodes?v 进行验证

ip           heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.16.3           26          93  19    0.44    0.28     1.30 mdi       *      es02
192.168.16.5           22          93  14    0.44    0.28     1.30 di        -      es04
192.168.16.4           15          93  18    0.44    0.28     1.30 mdi       -      es01
192.168.16.2           37          93  20    0.44    0.28     1.30 mdi       -      es03

这里我们可以看到新增加的节点 es04,节点角色为di, 这个节点由于指定了 node.master=false 说明此节点并不参与master leader节点的选举。

集群节点使用的docker网络为 es_esnet

测试es集群master的选举(高可用)

上面我们可以看到当前es02这个master节点为leader,我们现在手动停止这个master容器,让其它的两个master中选举一个leader,执行命令

$ docker stop es02

此时,再用上面的方法查看一下集群节点情况

ip           heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.16.5           40          74   2    0.19    0.27     0.94 di        -      es04
192.168.16.4           15          74   2    0.19    0.27     0.94 mdi       *      es01
192.168.16.2           39          74   2    0.19    0.27     0.94 mdi       -      es03

可以看到es02节点消失了,现在的master leader 节点为 es01。如果我们再把容器启动起来的话,发现es02作为了一个普通的master节点加入到了集群。

注意事项:

其实es集群的环境搭建挺容易的,我在搭建过程中遇到了一此坑,花费了好久才算爬出来,下面记录下来供大家参考。

一、确认分配给 docker 软件的内存是否足够

在上一篇文章( https://blog.haohtml.com/archives/18981) 里已经写过了,由于docker 分配的内存不足,导致启动一个新的es节点,会直接killed掉原来的es节点,发生OOM的现象,并且这个问题通过直接查看容器日志根本排查不到,容器内并未有相关的日志信息

二、保证环境的干净

使用 docker-compose 命令启动es节点容器成功后,如果需要对旧节点的配置内容进行修改的话,则强烈建议执行以下命令进行容器的清理工作

$ docker-compose -f 配置文件.yaml down -v

以上命令会将容器及容器关联的数据卷 volume 信息进行一并删除。否则容易出现新启动的节点又单独变成了一个集群,这时会出现跨集群节点加入被拒绝的错误。我在搭建环境的时候,创建用的 docker-compose up 命令,但修改配置文件后,手动执行 “docker rm 容器ID” 将容器删除,再次执行了 docker-compose up命令时,会出现上面说的这个问题,在这个坑里呆了好久才算出来。如果一定要想用docker rm 命令删除容器的话,添加添加-f参数,将volume 一并删除,如 docker rm -f es03

三、参数 discovery.zen.minimum_master_nodes

这里用的是es7.2.0的版本,服务启动时提示参数项discovery.zen.minimum_master_nodes 在下一个版本中即将废除的,但在官方文档里没有找到说明信息,这一点待确认。

golang中的内存对齐(进阶必看)

先看一个结构体

// 写法一
type T1 struct {
	a int8
	b int64
	c int16
}

// 写法二
type T2 struct {
	a int8
	c int16
	b int64
}

对于这两个结构体,都有a、b、c三个定义完全一样的字段,只是在定义结构体的时候字段顺序不一样而已,那么两种写法有什么影响吗?

对于新手来说,感觉着没有什么区别的,只是一个书写顺序不同而已,但对于go编译器来说,则有着很大的区别,特别是在不同架构上(32位/64位)的编译器,在一定程序上对内存的使用大小和执行效率有着一定的不同。这里的主要知识点就是golang语言中的内存对齐概念(alignment guarantee),https://gfw.go101.org/article/memory-layout.html

类型的尺寸和结构体字节填充(structure padding)

Go白皮书只对以下种类的类型的尺寸进行了明确规定

类型种类                  尺寸(字节数)
------                   ------
byte, uint8, int8        1
uint16, int16            2
uint32, int32, float32   4
uint64, int64            8
float64, complex64       8
complex128               16
uint, int                取决于编译器实现。通常在
                         32位架构上为4,在64位
                         架构上为8。
uintptr                  取决于编译器实现。但必须
                         能够存下任一个内存地址。

Go白皮书没有对其它种类的类型的尺寸最初明确规定。 请阅读值复制成本一文来获取标准编译器使用的各种其它类型的尺寸。

标准编译器(和gccgo编译器)将确保一个类型的尺寸为此类型的对齐保证的倍数。

为了满足上一节中规定的地址对齐保证要求,Go编译器可能会在结构体的相邻字段之间填充一些字节。 这使得一个结构体类型的尺寸并非等于它的各个字段类型尺寸的简单相加之和。

下面是一个展示了一些字节是如何填充到一个结构体中的例子。 首先,从上面的描述中,我们已得知(对于标准编译器来说):

  • 内置类型int8的对齐保证和尺寸均为1个字节; 内置类型int16的对齐保证和尺寸均为2个字节; 内置类型int64的尺寸为8个字节,但它的对齐保证在32位架构上为4个字节,在64位架构上为8个字节。
  • 下例中的类型T1T2的对齐保证均为它们的各个字段的最大对齐保证。 所以它们的对齐保证和内置类型int64相同,即在32位架构上为4个字节,在64位架构上为8个字节。
  • 类型T1T2尺寸需为它们的对齐保证的倍数,即在32位架构上为4n个字节,在64位架构上为8n个字节。
type T1 struct {
	a int8

	// 在64位架构上,为了让下一个字段b的地址为8字节对齐,
	// 需在在字段a这里填充7个字节。在32位架构上,为了让
	// 字段b的地址为4字节对齐,需在这里填充3个字节。

	b int64
	c int16

	// 为了让类型T1的尺寸为T1的对齐保证的倍数,
	// 在64位架构上需在这里填充6个字节,在32架构
	// 上需在这里填充2个字节。
}
// 类型T1的尺寸在64位架构上位24个字节(1+7+8+2+6),
// 在32位架构上为16个字节(1+3+8+2+2)。
// 以保存每个字段都是8(64位架构)或者4(32位架构)的的整数倍

type T2 struct {
	a int8

	// 为了让下一个字段c的地址为2字节对齐,
	// 需在字段a这里填充1个字节。

	c int16

	// 在64位架构上,为了让下一个字段b的地址为8字节对齐,
	// 需在字段c这里填充4个字节。在32位架构上,不需填充
	// 字节即可保证字段b的地址为4字节对齐的。

	b int64
}
// 类型T2的尺寸在64位架构上位16个字节(1+1+2+4+8),
// 在32位架构上为12个字节(1+1+2+8)。

从这个例子可以看出,尽管类型T1T2拥有相同的字段集,但是它们的尺寸并不相等。每个字段的大小都要受下一个字段大小的影响,以方便下个字段对齐。所以建议在开发中,字段占用空间小的放在前面。

一个有趣的事实是有时候一个结构体类型中零尺寸类型的字段可能会影响到此结构体类型的尺寸。 请阅读此问答获取详情。

如果还有些模糊的话可以看一下这篇文章:https://blog.csdn.net/Lazyboy_/article/details/88579966

kafka常用术语

官方网站:http://kafka.apache.org/,中文: http://kafka.apachecn.org/

注意它和其它消息系统(消息队列)在定义上的区别,以便更好的理解它的应用场景。Apache Kafka 是一款分布式流处理平台(Distributed Streaming Platform)

术语(注意加粗部分的定义):

消息:Record。消息实体,是通信的基本单位。
主题:Topic。主题是承载消息的逻辑容器,在实际使用中多用来区分具体的业务。
分区:Partition。一个有序不变的消息序列。每个主题Topic下可以有多个分区。
消息位移:Offset。表示分区中每条消息的位置信息,是一个单调递增且不变的值。
缓存代理,Broker。Kafka集群中的一台或多台服务器统称broker。
副本:Replica。Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的副本。副本还分为领导者副本和追随者副本,各自有不同的角色划分。副本是在分区层级下的,即每个分区可配置多个副本实现高可用。
生产者:Producer。向主题发布新消息的应用程序。
消费者:Consumer。从主题订阅新消息的应用程序。
消费者位移:Consumer Offset。表示消费者消费进度,每个消费者都有自己的消费者位移。
消费者组:Consumer Group。多个消费者实例共同组成的一个组,同时消费多个分区以实现高吞吐。
重平衡:Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。

kafka

转自极客时间

想一下,为什么kafka比其它消息系统有着很高的吞吐量??提示 “零拷贝“https://www.cnblogs.com/f-ck-need-u/p/7615914.html

ES集群的高可用性之节点

为了防止ES集群中单点问题,一般都需要对集群节点做高可用性,当发生单点问题时,也可以向外正常提供服务。这里主要记录一下节点的加入、离开和主节点选举。

集群安装教程请参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html

集群是由多个节点组成的,每个节点都扮演着不同的角色,一般常用的有 Master、Data 和 client。节点角色介绍:https://www.jianshu.com/p/7c4818dda91a

节点角色配置参数:
node.master: true
node.data: false
node.ingest: false 

在一个集群中,可以通过 http://localhost:9200/_cat/nodes?v 查看到每个节点在集群中扮演的角色,一个节点可同时拥有多个角色,如值MDI,同时也是每个节点的默认值,其中的 Ingest 节点也称作预处理节点,不过在生产环境中一般将master 和 data节点分开的。所有节点默认都是支持 Ingest 操作的。节点组合参考:https://blog.51cto.com/michaelkang/2061712

新节点的加入

随着数量大的增加,有时候我们不得进行机器的扩容,这时间就需要加入一些新的机器节点,用来提高访问速度。

当一个新节点加入的时候,它通过读取 discovery.zen.ping.unicast.hosts 配置的节点获取集群状态,然后找到 master 节点,并向其发送一个join request(discovery.zen.join_timeout)。主节点接收到请求后,同步集群状态到新节点。

节点离开

这里指非 mastr 节点。
当master主节点定期ping(ping_interval 默认为1s)一个节点出现3次ping不通的情况时(ping_timeout 默认为30s),主节点会认为该节点已宕机,将该节点踢出集群。

主节点的选举

我们知道集群中master节点角色的重要性,如果此节点出现问题的话,服务基本处于不可用状态了,所以保证master节点的高可用性至关重要。

当主节点发生故障时,集群中的其他节点将会ping当前的 master eligible 节点,并从中选出一个新的主节点。
节点可以通过设置 node.master=true 来设置自己的角色为主节点。
通过配置discovery.zen.minimum_master_nodes防止集群出现脑裂。该配置通过检查集群中 master eligible 的个数来判断是否选举出一个主节点。其个数最好设置为(number_master eligible/2)+1,防止当主节点出问题时,一个集群分裂为两个集群。
具有最小编号的active master eligible node将会被选举为master节点。参考:https://www.elastic.co/guide/cn/elasticsearch/guide/current/important-configuration-changes.html

脑裂的概念:
如果你有2个Master候选节点,并设置最小Master节点数为1,当网络抖动或偶然断开时,2个Master都会认为另一个Master挂掉了,他们都被选举为主Master,则此时集群中存在两个主Master,即物理上1个集群变成了逻辑上的2个集群,而当其中一个Master再次挂掉时,即便它恢复后回到了原有的集群,在它作为主Master期间写入的数据都会丢失,因为它上面维护了Index信息。类似我们平时的投票选举一下,10个人如果赞成与返对的比例为5:5就不太好了,所以必须保证双方的票数无法完全一样才可以。

为了防止脑裂,常常设置参数为discovery.zen.minimum_master_nodes=N/2+1,其中N为集群中Master节点的个数。建议集群中Master节点的个数为奇数个,如3个或者5个。

在版本 6 和更早的版本中,还有一些其他以 discovery.zen.* 开头的选项,允许你配置 Zen Discovery 的行为。其中一些设置不再有效,已被删除。其他的已经改名。如果一个参数已经被改名,那么它的旧名称在版本 7 中就被弃用,你需要调整配置来使用新名称。

新的集群协调子系统包括一个新的故障检测机制。这意味着 discovery.zen.fd.* 开头的 Zen Discovery 错误检测设置不再有效。大多数用户应该在版本 7 或更高版本中使用默认的故障检测配置,如果需要进行任何更改,可以使用cluster.fault_detection.*

elasticsearch.yml 配置详解:https://www.cnblogs.com/zlslch/p/6419948.html

参考文档:
https://www.jianshu.com/p/80b030a5b500
https://www.jianshu.com/p/7c4818dda91a

docker容器 Exited (137)错误代码

最近要搭建es集群,由于刚接触es不久,直接使用的docker构建,发现当用两个容器搭建好集群时,再添加新的es容器节点时,总是出现其它容器被kill的现象,查看容器日志未发现任何错误信息,一段时间段非常的迷茫。

由于对es也是刚刚接触,起始认为是配置不当引起了,于是一直在配置这一块找问题,网上的有些教程是直接在物理机器上或者虚拟机上进行部署,而自己的环境是docker, 通过 docker-compose 来部署的,环境有些差异。

有网友提醒有可能是由 OOM 引起的问题,因为代码是137, 使用命令 “docker inspect 容器ID” 查看了一下容器, status列显示”OOMKilled”: false” ,所以从这里查看的话,并非是 OOM 引起的,再加上首次遇到这个问题的时候,个人分析容器的启动过程,新启动一个容器时,dockerd应该先检查内存是否足够,如果不足够的话,则启动新窗口失败才对,而不是将已存在的窗口killed,再启动新容器,所以仍将OOM的原因排除掉了。

后来发现一篇文章https://www.petefreitag.com/item/848.cfm 介绍到这个和 docker for mac 分配的内存大小有关系,试着将给 docker 分配的内存调整为4G,重新docker-compose up 竟然解决了。

由此可见docker软件对容器启动时处理逻辑与自己分析的还是有些差异的。 可惜浪费了好几天的时间,很值的好好反思一下!

总结一下,还是基础不牢固,解决思路,分析问题不清晰,一直在配置这一块的周旋,虽然首次怀疑是OOM引起的,但在容器日志里并没有OOM的日志信息,再加上集群编排时有“ – “ES_JAVA_OPTS=-Xms512m -Xmx512m” 配置项,给es分配512M的内存大小,也忽略掉物理机器给docker分配内存大小这个因素了,所以直接将OOM的原因排除了,而未想到OOM发生时,并不保证一定在容器日志里记录的。

mac手动停止 php-fpm 服务

由于要安装一个docker服务,对外提供端口用的是9000, 和php-fpm的监听端口冲突,所以需要先停止一下php-fpm服务。

多次执行

sudo killall php-fpm

发现过一会php-fpm会自动启动,就算一个一个的进程kill -9 也一样的效果。经过分析这个应该是和php-fpm配置文件 ~/Library/LaunchAgents/homebrew.mxcl.php@7.1.plist 有关。

我们知道 ~/Library/LaunchAgents 针对当前用户的启动项目录,针对这个项目里的一些配置服务有一个 launchctl 命令可以操作,其中有几个命令我们需要知道他的意思

launchctl load 启动plist运行
launchctl unload  卸载
launchctl list 查看所有启动任务

默认当用户登录后,mac系统会对当前目录 ~/Library/LaunchAgents 里的每个配置服务文件自动执行launchctl load 命令。如果我们想停止一个服务的话,则需要执行 launchctl unload 命令即可。

$ LaunchAgents launchctl list | grep php
66054 0 homebrew.mxcl.php@7.1
$ LaunchAgents launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.php@7.1.plist
$ LaunchAgents launchctl list | grep php

然后再用ps 查看确认php-fpm已停止。

MySQL8.0中的几项新特性(整理)

新特性解读 | MySQL 8.0 窗口函数详解

新特性解读 | MySQL 8.0 json到表的转换

新特性解读 | MySQL 8.0.16 在组复制中启用成员自动重新加入

新特性解读 | MySQL 8.0 直方图

新特性解读 | MySQL 8.0 索引特性1-函数索引

新特性解读 | MySQL 8.0 索引特性2-索引跳跃扫描

新特性解读 | MySQL 8.0 索引特性3 -倒序索引

新特性解读 | MySQL 8.0 索引特性4-不可见索引

新特性解读 | MySQL 8.0.16 组复制通讯协议的设置

新特性解读 | MySQL 8.0.16 组复制新功能:消息碎片化

新特性解读 | MySQL 8.0 新增 HINT 模式

社区投稿 | MySQL 8.0.16 开始支持 check 完整性

新特性解读 | MySQL 8.0 通用表达式

新特性解读 | MySQL 最新的release notes

新特性解读 | MySQL 5.7升级到MySQL 8.0的注意事项

新特性解读 | 自动验证 MySQL 配置正确性的新选项

一文读懂 MySQL 的隔离级别和锁的关系

MySQL 中的隔离四种隔离级别与锁的关系一直挺模糊的,看了好多文章感觉着都不是很好理解,今天在“爱可生开源社区”看到一篇文章,感觉着挺容易理解的。

READ UNCOMMITTED 未提交读,可以读取未提交的数据。

READ COMMITTED 已提交读,对于锁定读(select with for update 或者 for share)、update 和 delete 语句, InnoDB 仅锁定索引记录,而不锁定它们之间的间隙,因此允许在锁定的记录旁边自由插入新记录。Gap locking 仅用于外键约束检查和重复键检查。

REPEATABLE READ 可重复读,事务中的一致性读取读取的是事务第一次读取所建立的快照。

SERIALIZABLE 序列化

文中主要对 RR 和 RC 两种常用的隔离级别做了不同情况的说明,对于 SERIALIZABLE 序列化 和 READ UNCOMMITTED 未提交读,由于很好理解所以未在文中体现。对于 RR 和 RC 主要区别是 RR 存在 Gap Lock间隙锁,而RC则没有Gap Lock间隙锁,所以在互联网中绝大部分是采用了RC 隔离级别,而未使用MySQL中默认的RR级别。对于锁的介绍请参考:https://blog.haohtml.com/archives/17758

MySQL5.7中Undo回收收缩相关参数

在MySQL5.7以前,ibdata1文件会逐渐增大(ibdata1文件包含哪些信息?),非常占用系统空间,特别是一些云数据来说,磁盘非常的贵,想要回收空间,只能进行一次导出和导入操作,来重新生成undo 表空间,从MySQL5.7开始,有了在线回收undo表空间的功能,主要由以下几个参数设置。

innodb_undo_directory = .
为undo文件存储路径。如果没有指定默认值(NULL),则undo表空间则存放到mysql的data目录里(datadir选项)。配置此项可以用undo从ibdata文件里分离出来。单独存储。

innodb_undo_logs = 128
(默认值 128)undo rollback segment 回滚段个数,为 innodb_rollback_segments 参数选项的别名,最大值为128,其中32个为使用临时表空间 ibtmp1 保留,1个为系统表空间使用,剩余的95个为 undo tablespaces 使用。
当 innodb_rollback_segments<=32的时候,系统将自动分配1个rollback segment给系统表空间,32个分给临时表空间。
此选项以后版本将移除!

innodb_undo_tablespaces = 0
(MySQL5.7默认值为0,MySQL8默认值为2)undo文件个数,此值需要在MySQL Server 初始化的时候指定,一经设定,以后将无法修改,否则重启后会提示部分Undo 文件找不到。默认值为0, 此时无法进行Undo回收操作,回收undo表空间至少需要为2个才可以, 需保证其中一个进行回收收缩时,另一个为可用状态。保存路径为 innodb_undo_directory选项设置,undo文件名规则为 undoN。文件大小受innodb_page_size选项影响。
此选项以后版本将移除!

innodb_undo_log_truncate = OFF
(默认值NO)参数设置为ON,即开启在线回收undo日志文件,支持动态设置,当超过 innodb_max_undo_log_size 时被进行收缩,至少需要两个undo文件,即innodb_undo_tablespace>=2

innodb_max_undo_log_size = 1073741824
(默认1GB)当超过阈值时,会触发truncate回收动作,truncate后空间缩小到10MB

innodb_purge_rseg_truncate_frequency = 128
(默认值128), 控制回收undo log的频率。 指定purge操作被唤起多少次之后才释放rollback segments。当undo表空间里面的rollback segments被释放时,undo表空间才会被truncate。由此可见,该参数越小,undo表空间被尝试truncate的频率越高。 。

联想思考:
ibdata文件包含哪些信息?
undo logs、change buffer、doublewrite buffer、表数据、索引数据,如果启用了 innodb_file_per_table 选项的话,则表数据和索引数据则存储到相应表的.ibd 文件里),参考:
https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_ibdata_file

ibdata文件爆增原因有哪些?
事务未提交、大事务、启用了共享表空间、磁盘io过慢导致check point远远落后。等等

如何避免ibdata文件一直爆增的问题?
尽量短事务、增加 purge 线程、加速purge频率(innodb_purge_truncate_frequency)、监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill

参考:
https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_max_undo_log_size

undo表空间回收https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-tablespaces.html