月度归档: 2021 年 5 月

centos7上的图形化界面svn客户端_把文件上传到Linux服务器有多少种方法

1. Window上传文件到Linux

1.1 图形化界面winscp

适用于传送文件和目录,但要安装额外的软件winscp

1.2 lrzsz套件

适用于传送文件,使用ssh远程登录管理软件xshell或者securecrt即可。

使用xshell或者securecrt通过ssh连接到Linux服务器。

以CentOS7.5为例,安装lrzsz套件

[root@zcwyou ~]# yum -y install lrzsz

Linux服务器接收远程文件:

[root@zcwyou ~]# rz

以下图片是Windows使用xshell向Linux上传文件

0f3e4b373668e9dc168314639e746147.png

Windows使用xshell向Linux上传文件

以下图片是Windows使用securecrt向Linux上传文件

b415cdd8e93223b80841ef27fd33562e.png

Windows使用securecrt向Linux上传文件

2. Linux服务器发送文件到Windows

以发送1.pcap为例

[root@zcwyou ~]# sz 1.pcap 

以下图片是Windows使用securecrt下载Linux文件

7b17d9f1a0033f957a179a6c391870cd.png

Windows使用securecrt与Linux传输文件

以下图片是Windows使用xshell下载Linux文件

fd83f2a74ebb534f0576b7603dbb7bcd.png

Windows使用xshell与Linux传输文件

2.1 通过FTP协议上传文件到Linux服务器

需要在Linux服务器上先搭建好FTP服务:

请查看本站相关教程

然后再下载filezilla,安装在Windows系统上。

官网地址:

https://filezilla-project.org/

Windows FTP客户端下载地址:

https://filezilla-project.org/download.php?type=client

65be5272dd4c87bcd5bf23d63a028595.png

Windows使用ftp协议上传文件到Linux

2.2 通过wget或者curl下载HTTP、HTTPS、FTP文件

以wget为例,下载一个文件

安装wget软件

[root@zcwyou ~]# yum -y install wget

下载Logo.png

[root@zcwyou ~]# wget https://www.linuxrumen.com/wp-content/uploads/2018/11/Logo.png

3. 两台Linux服务器互传文件

通常情况下,我使用scp,方便、简单,快速。

以CentOS7.5为例:

3.1 先安装openssh-clients套件

[root@zcwyou ~]# yum -y install openssh-clients

3.2 从本机复制文件到远程服务器

复制文件/var/log/messages到远程服务器10.10.10.10目录/home/abc/

[root@zcwyou ~]# scp /var/log/messages root@10.10.10.10:/home/abc/

3.3 从远程服务器复制文件到本机

从远程服务器10.10.10.10复制文件abc.txt到本机/tmp

[root@zcwyou ~]# scp root@10.10.10.10:/home/zcwyou/abc.txt /tmp

3.4 从本机复制目录到远程服务器

添加选项-r

复制本机目录/var/log/到服务器10.10.10.10的目录/home/abc/

[root@zcwyou ~]# scp -r /var/log/ root@10.10.10.10:/home/abc/

3.5 复制远程服务器目录到本机

复制远程服务器10.10.10.10的/home/abc/到本地目录/var/backup

[root@zcwyou ~]# scp -r root@10.10.10.10:/home/abc/ /var/backup

按提示输入10.10.10.10的密码。

Windows上传文件到Linux服务器

Windows上传文件到Linux服务器
脚本编写
@echo off
echo ===开始上传文件===
set file=%1

set host=123.57.83.80
set user=root
set base=/root/

echo 上传文件
dir /B %file%
echo 到服务器 %user%@%host%

scp %file% %user%@%host%:%base%
echo 上传成功
pause
脚本使用
在任何地方建立个后缀为.bat的文件,复制上面脚本到这个文件,然后修改脚本参数保存

上传方式 将要上传的文件拖到这个脚本,会提示你输入服务器密码,然后等待上传成功

脚本参数说明

演示
提示输入密码:

这里输入密码不会显示,输入密码回车就行

上传成功:

怎样上传文件到linux服务器?

我们知道,云主机文件传输是一件相对复杂的事情,经常需要搭建FTP服务器或者是借助其他工具来完成。下面为大家介绍一种简单易操作的传输文件到Linux服务器的方法。
对于行云管家来说,Linux文件传输同Windows文件传输一样,我们为每一台Linux主机配置了一个1G的网盘作为中间介质来实现客户端和主机之间的文件传输。
那么如何传输文件到Linux服务器?
一、什么是主机网盘?
为了实现用户更快、更安全、更省钱的文件传输需求,我们在用户将主机导入行云管家平台时,自动为每台主机分配了一个主机网盘,该网盘将在远程连接主机过程中被挂载成主机的逻辑磁盘,方便用户传输文件。
二、什么是内网文件传输?
在文件传输过程中,如果我们通过公网传输,很显然会受到公网带宽的限制,占用云主机公网带宽资源,甚至影响到主机的正常访问和使用。如果是按流量付费的用户,还将产生额外的费用。此外FTP文件传输方式还需要开放服务器21/22端口,内网文件传输的方式无需开启21/22端口,杜*此安全隐患。
1、进入远程桌面
为了获得*流畅的文件传输效果,建议用户以内网IP访问形式创建云主机远程桌面。通过行云管家登录云主机之后,用户在右侧面板,默认为“文件传输”页签,在文件传输面板您就可以直接查看到主机的文件目录(注意:面板中显示的是云主机的文件系统,而不是主机网盘)
怎样上传文件到linux服务器?
2、上传文件
Linux主机上传文件有三种方法:其一是上传本地文件至Linux云主机,其二是上传本地目录到Linux云主机上,其三是上传网盘文件到Linux云主机;
怎样上传文件到linux服务器?
在上传网盘文件之前,您需要先将本地文件上传到网盘上,再使用内网进行文件传输,上传到Linux云主机。
怎样上传文件到linux服务器?
3、下载文件
同样的,您在云主机上增删的文件,也能同步体现在文件传输面板上。另外,Linux主机下载文件有两种方法:其一是将Linux云主机上的文件下载到本地,其二是将Linux云主机的文件下载到网盘。
怎样上传文件到linux服务器?
值得注意的是,行云管家Linux文件传输功能在一定程度上更加简单明了,无需安装任何客户端,充分利用云厂商内网千兆带宽的优势,获得更快的文件传输速度;并且行云管家文件传输过程中占用的带宽和流量费用将由行云管家负载,省钱省心;与此同时,行云管家Linux文件传输也无需用户开放Linux服务器端口,安全放心。

分布式消息队列差异化总结

本文将对Kafka、RabbitMQ、ZeroMQ、RocketMQ、ActiveMQ从17 个方面综合对比作为消息队列使用时的差异。

 

一、资料文档

 

  • Kafka:资料数量中等。有Kafka作者自己写的书,网上资料也有一些。
  • RabbitMQ:资料数量多。有一些不错的书,网上资料多。
  • ZeroMQ:资料数量少。专门写ZeroMQ的书较少,网上的资料多是一些代码的实现和简单介绍。
  • RocketMQ:资料数量少。专门写RocketMQ的书目前有了两本;网上的资料良莠不齐,官方文档很简洁,但是对技术细节没有过多的描述。
  • ActiveMQ:资料数量多。没有专门写ActiveMQ的书,网上资料多。

 

二、开发语言

 

  • Kafka:Scala
  • RabbitMQ:Erlang
  • ZeroMQ:C
  • RocketMQ:Java
  • ActiveMQ:Java

 

三、支持的协议

 

  • Kafka:自己定义的一套…(基于TCP)
  • RabbitMQ:AMQP
  • ZeroMQ:TCP、UDP
  • RocketMQ:自己定义的一套…
  • ActiveMQ:OpenWire、STOMP、REST、XMPP、AMQP

 

四、消息存储

 

1、Kafka

 

内存、磁盘、数据库。支持大量堆积。

 

Kafka的*小存储单元是分区,一个topic包含多个分区,Kafka创建主题时,这些分区会被分配在多个服务器上,通常一个broker一台服务器。

 

分区首领会均匀地分布在不同的服务器上,分区副本也会均匀的分布在不同的服务器上,确保负载均衡和高可用性,当新的broker加入集群的时候,部分副本会被移动到新的broker上。

 

根据配置文件中的目录清单,Kafka会把新的分区分配给目录清单里分区数*少的目录。

 

默认情况下,分区器使用轮询算法把消息均衡地分布在同一个主题的不同分区中,对于发送时指定了key的情况,会根据key的hashcode取模后的值存到对应的分区中。

 

2、RabbitMQ

 

内存、磁盘。支持少量堆积。

 

RabbitMQ的消息分为持久化的消息和非持久化消息,不管是持久化的消息还是非持久化的消息都可以写入到磁盘。

 

持久化的消息在到达队列时就写入到磁盘,并且如果可以,持久化的消息也会在内存中保存一份备份,这样可以提高一定的性能,当内存吃紧的时候会从内存中清除。

 

非持久化的消息一般只存在于内存中,在内存吃紧的时候会被换入到磁盘中,以节省内存。

 

引入镜像队列机制,可将重要队列“复制”到集群中的其他broker上,保证这些队列的消息不会丢失。

 

配置镜像的队列,都包含一个主节点master和多个从节点slave,如果master失效,加入时间*长的slave会被提升为新的master,除发送消息外的所有动作都向master发送,然后由master将命令执行结果广播给各个slave,RabbitMQ会让master均匀地分布在不同的服务器上,而同一个队列的slave也会均匀地分布在不同的服务器上,保证负载均衡和高可用性。

 

3、ZeroMQ

 

消息发送端的内存或者磁盘中。不支持持久化。

 

4、RocketMQ

 

磁盘。支持大量堆积。

 

commitLog文件存放实际的消息数据,每个commitLog上限是1G,满了之后会自动新建一个commitLog文件保存数据。

 

ConsumeQueue队列只存放offset、size、tagcode,非常小,分布在多个broker上。

 

ConsumeQueue相当于CommitLog的索引文件,消费者消费时会从consumeQueue中查找消息在commitLog中的offset,再去commitLog中查找元数据。

 

ConsumeQueue存储格式的特性,保证了写过程的顺序写盘(写CommitLog文件),大量数据IO都在顺序写同一个commitLog,满1G了再写新的。

 

加上RocketMQ是累计4K才强制从PageCache中刷到磁盘(缓存),所以高并发写性能突出。

 

5、ActiveMQ

 

内存、磁盘、数据库。支持少量堆积。

 

五、消息事务

 

  • Kafka:支持
  • RabbitMQ:支持。

    客户端将信道设置为事务模式,只有当消息被RabbitMQ接收,事务才能提交成功,否则在捕获异常后进行回滚。使用事务会使得性能有所下降

  • ZeroMQ:不支持
  • RocketMQ:支持
  • ActiveMQ:支持

 

六、负载均衡

 

1、Kafka

 

支持负载均衡。

 

1)一个broker通常就是一台服务器节点。

 

对于同一个Topic的不同分区,Kafka会尽力将这些分区分布到不同的Broker服务器上,zookeeper保存了broker、主题和分区的元数据信息。

 

分区首领会处理来自客户端的生产请求,Kafka分区首领会被分配到不同的broker服务器上,让不同的broker服务器共同分担任务。

 

每一个broker都缓存了元数据信息,客户端可以从任意一个broker获取元数据信息并缓存起来,根据元数据信息知道要往哪里发送请求。

 

2)Kafka的消费者组订阅同一个topic,会尽可能地使得每一个消费者分配到相同数量的分区,分摊负载。

 

3)当消费者加入或者退出消费者组的时候,还会触发再均衡,为每一个消费者重新分配分区,分摊负载。

 

Kafka的负载均衡大部分是自动完成的,分区的创建也是Kafka完成的,隐藏了很多细节,避免了繁琐的配置和人为疏忽造成的负载问题。

 

4)发送端由topic和key来决定消息发往哪个分区,如果key为null,那么会使用轮询算法将消息均衡地发送到同一个topic的不同分区中。如果key不为null,那么会根据key的hashcode取模计算出要发往的分区。

 

2、RabbitMQ

 

对负载均衡的支持不好。

 

1)消息被投递到哪个队列是由交换器和key决定的,交换器、路由键、队列都需要手动创建。

 

RabbitMQ客户端发送消息要和broker建立连接,需要事先知道broker上有哪些交换器,有哪些队列。

 

通常要声明要发送的目标队列,如果没有目标队列,会在broker上创建一个队列,如果有,就什么都不处理,接着往这个队列发送消息。假设大部分繁重任务的队列都创建在同一个broker上,那么这个broker的负载就会过大。

 

可以在上线前预先创建队列,无需声明要发送的队列,但是发送时不会尝试创建队列,可能出现找不到队列的问题,RabbitMQ的备份交换器会把找不到队列的消息保存到一个专门的队列中,以便以后查询使用。

 

使用镜像队列机制建立RabbitMQ集群可以解决这个问题,形成master-slave的架构,master节点会均匀分布在不同的服务器上,让每一台服务器分摊负载。slave节点只是负责转发,在master失效时会选择加入时间*长的slave成为master。

 

当新节点加入镜像队列的时候,队列中的消息不会同步到新的slave中,除非调用同步命令,但是调用命令后,队列会阻塞,不能在生产环境中调用同步命令。

 

2)当RabbitMQ队列拥有多个消费者的时候,队列收到的消息将以轮询的分发方式发送给消费者。每条消息只会发送给订阅列表里的一个消费者,不会重复。

 

这种方式非常适合扩展,而且是专门为并发程序设计的。

 

如果某些消费者的任务比较繁重,那么可以设置basicQos限制信道上消费者能保持的*大未确认消息的数量,在达到上限时,RabbitMQ不再向这个消费者发送任何消息。

 

3)对于RabbitMQ而言,客户端与集群建立的TCP连接不是与集群中所有的节点建立连接,而是挑选其中一个节点建立连接。

 

但是RabbitMQ集群可以借助HAProxy、LVS技术,或者在客户端使用算法实现负载均衡,引入负载均衡之后,各个客户端的连接可以分摊到集群的各个节点之中。

 

客户端均衡算法:

 

  • 轮询法。按顺序返回下一个服务器的连接地址。
  • 加权轮询法。给配置高、负载低的机器配置更高的权重,让其处理更多的请求;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载。
  • 随机法。随机选取一个服务器的连接地址。
  • 加权随机法。按照概率随机选取连接地址。
  • 源地址哈希法。通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算。
  • *小连接数法。动态选择当前连接数*少的一台服务器的连接地址。

 

3、ZeroMQ

 

去中心化,不支持负载均衡。本身只是一个多线程网络库。

 

4、RocketMQ

 

支持负载均衡。

 

一个broker通常是一个服务器节点,broker分为master和slave,master和slave存储的数据一样,slave从master同步数据。

 

1)nameserver与每个集群成员保持心跳,保存着Topic-Broker路由信息,同一个topic的队列会分布在不同的服务器上。

 

2)发送消息通过轮询队列的方式发送,每个队列接收平均的消息量。发送消息指定topic、tags、keys,无法指定投递到哪个队列(没有意义,集群消费和广播消费跟消息存放在哪个队列没有关系)。

 

  • tags选填,类似于 Gmail 为每封邮件设置的标签,方便服务器过滤使用。目前只支 持每个消息设置一个 tag,所以也可以类比为 Notify 的 MessageType 概念。
  • keys选填,代表这条消息的业务关键词,服务器会根据 keys 创建哈希索引,设置后, 可以在 Console 系统根据 Topic、Keys 来查询消息,由于是哈希索引,请尽可能 保证 key 唯一,例如订单号,商品 Id 等。

 

3)RocketMQ的负载均衡策略规定:

 

Consumer数量应该小于等于Queue数量,如果Consumer超过Queue数量,那么多余的Consumer 将不能消费消息。

 

这一点和Kafka是一致的,RocketMQ会尽可能地为每一个Consumer分配相同数量的队列,分摊负载。

 

5、ActiveMQ

 

支持负载均衡。可以基于zookeeper实现负载均衡。

 

七、集群方式

 

1、Kafka

 

天然的‘Leader-Slave’无状态集群,每台服务器既是Master也是Slave。

 

分区首领均匀地分布在不同的Kafka服务器上,分区副本也均匀地分布在不同的Kafka服务器上,所以每一台Kafka服务器既含有分区首领,同时又含有分区副本。

 

每一台Kafka服务器是某一台Kafka服务器的Slave,同时也是某一台Kafka服务器的leader。

 

Kafka的集群依赖于zookeeper,zookeeper支持热扩展,所有的broker、消费者、分区都可以动态加入移除,而无需关闭服务,与不依靠zookeeper集群的mq相比,这是*大的优势。

 

2、RabbitMQ

 

支持简单集群,’复制’模式,对高级集群模式支持不好。

 

RabbitMQ的每一个节点,不管是单一节点系统或者是集群中的一部分,要么是内存节点,要么是磁盘节点,集群中至少要有一个是磁盘节点。

 

在RabbitMQ集群中创建队列,集群只会在单个节点创建队列进程和完整的队列信息(元数据、状态、内容),而不是在所有节点上创建。

 

引入镜像队列,可以避免单点故障,确保服务的可用性,但是需要人为地为某些重要的队列配置镜像。

 

3、ZeroMQ

 

去中心化,不支持集群。

 

4、RocketMQ

 

常用 多对’Master-Slave’ 模式,开源版本需手动切换Slave变成Master。

 

Name Server是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。

 

Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master。

 

Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。

 

Master也可以部署多个。每个Broker与Name Server集群中的所有节点建立长连接,定时注册Topic信息到所有Name Server。

 

Producer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。

 

Consumer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。

 

Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。

 

客户端先找到NameServer, 然后通过NameServer再找到 Broker。

 

一个topic有多个队列,这些队列会均匀地分布在不同的broker服务器上。RocketMQ队列的概念和Kafka的分区概念是基本一致的,Kafka同一个topic的分区尽可能地分布在不同的broker上,分区副本也会分布在不同的broker上。

 

RocketMQ集群的slave会从master拉取数据备份,master分布在不同的broker上。

 

5、ActiveMQ

 

支持简单集群模式,比如’主-备’,对高级集群模式支持不好。

 

八、管理界面

 

  • Kafka:一般
  • RabbitMQ:
  • ZeroMQ:
  • RocketMQ:有管理后台, 但不是项目里自带, 需要自己启动一个单独的管理后台实例
  • ActiveMQ:一般

 

九、可用性

 

  • Kafka:非常高(分布式)
  • RabbitMQ:高(主从)
  • ZeroMQ:
  • RocketMQ:非常高(分布式)
  • ActiveMQ:高(主从)

 

十、消息重复

 

  • Kafka:支持at least once、at most once
  • RabbitMQ:支持at least once、at most once
  • ZeroMQ:只有重传机制,但是没有持久化,消息丢了重传也没有用。既不是at least once、也不是at most once、更不是exactly only once
  • RocketMQ:支持at least once
  • ActiveMQ:支持at least once

 

十一、吞吐量TPS

 

  • Kafka:*大

    Kafka按批次发送消息和消费消息。发送端将多个小消息合并,批量发向Broker,消费端每次取出一个批次的消息批量处理。

  • RabbitMQ:比较大
  • ZeroMQ:*大
  • RocketMQ:

    RocketMQ接收端可以批量消费消息,可以配置每次消费的消息数,但是发送端不是批量发送。

  • ActiveMQ:比较大

 

十二、订阅形式和消息分发

 

1、Kafka

 

基于topic以及按照topic进行正则匹配的发布订阅模式。

 

1)发送

 

发送端由topic和key来决定消息发往哪个分区,如果key为null,那么会使用轮询算法将消息均衡地发送到同一个topic的不同分区中。如果key不为null,那么会根据key的hashcode取模计算出要发往的分区。

 

2)接收

 

  • consumer向群组协调器broker发送心跳来维持他们和群组的从属关系以及他们对分区的所有权关系,所有权关系一旦被分配就不会改变除非发生再均衡(比如有一个consumer加入或者离开consumer group),consumer只会从对应的分区读取消息。
  • Kafka限制consumer个数要少于分区个数,每个消息只会被同一个 Consumer Group的一个consumer消费(非广播)。
  • Kafka的 Consumer Group订阅同一个topic,会尽可能地使得每一个consumer分配到相同数量的分区,不同 Consumer Group订阅同一个主题相互独立,同一个消息会被不同的 Consumer Group处理。

 

2、RabbitMQ

 

提供了4种:direct、topic、Headers和fanout。

 

1)发送

 

先要声明一个队列,这个队列会被创建或者已经被创建,队列是基本存储单元。

 

  • 由exchange和key决定消息存储在哪个队列。
  • direct>发送到和bindingKey完全匹配的队列。
  • topic>路由key是含有”.”的字符串,会发送到含有“*”、“#”进行模糊匹配的bingKey对应的队列。
  • fanout>与key无关,会发送到所有和exchange绑定的队列
  • headers>与key无关,消息内容的headers属性(一个键值对)和绑定键值对完全匹配时,会发送到此队列。此方式性能低一般不用

 

2)接收

 

RabbitMQ的队列是基本存储单元,不再被分区或者分片,对于我们已经创建了的队列,消费端要指定从哪一个队列接收消息。

 

当RabbitMQ队列拥有多个消费者的时候,队列收到的消息将以轮询的分发方式发送给消费者。每条消息只会发送给订阅列表里的一个消费者,不会重复。

 

这种方式非常适合扩展,而且是专门为并发程序设计的。

 

如果某些消费者的任务比较繁重,那么可以设置basicQos限制信道上消费者能保持的*大未确认消息的数量,在达到上限时,RabbitMQ不再向这个消费者发送任何消息。

 

3、ZeroMQ

 

点对点(p2p)。

 

4、RocketMQ

 

基于topic/messageTag以及按照消息类型、属性进行正则匹配的发布订阅模式。

 

1)发送

 

发送消息通过轮询队列的方式发送,每个队列接收平均的消息量。发送消息指定topic、tags、keys,无法指定投递到哪个队列(没有意义,集群消费和广播消费跟消息存放在哪个队列没有关系)。

 

  • tags选填,类似于 Gmail 为每封邮件设置的标签,方便服务器过滤使用。目前只支 持每个消息设置一个 tag,所以也可以类比为 Notify 的 MessageType 概念。
  • keys选填,代表这条消息的业务关键词,服务器会根据 keys 创建哈希索引,设置后, 可以在 Console 系统根据 Topic、Keys 来查询消息,由于是哈希索引,请尽可能 保证 key 唯一,例如订单号,商品 Id 等。

 

2)接收

 

  • 广播消费。一条消息被多个Consumer消费,即使Consumer属于同一个ConsumerGroup,消息也会被ConsumerGroup中的每个Consumer都消费一次。
  • 集群消费。一个 Consumer Group中的Consumer实例平均分摊消费消息。例如某个Topic有 9 条消息,其中一个Consumer Group有3个实例,那么每个实例只消费其中的 3 条消息。即每一个队列都把消息轮流分发给每个consumer。

 

5、ActiveMQ

 

点对点(p2p)、广播(发布-订阅)

 

  • 点对点模式,每个消息只有1个消费者;
  • 发布/订阅模式,每个消息可以有多个消费者。

 

1)发送

 

  • 点对点模式:先要指定一个队列,这个队列会被创建或者已经被创建。
  • 发布/订阅模式:先要指定一个topic,这个topic会被创建或者已经被创建。

 

2)接收

 

  • 点对点模式:对于已经创建了的队列,消费端要指定从哪一个队列接收消息。
  • 发布/订阅模式:对于已经创建了的topic,消费端要指定订阅哪一个topic的消息。

 

十三、顺序消息

 

  • Kafka:支持。

    设置生产者的max.in.flight.requests.per.connection为1,可以保证消息是按照发送顺序写入服务器的,即使发生了重试。

    Kafka保证同一个分区里的消息是有序的,但是这种有序分两种情况:

    ①key为null,消息逐个被写入不同主机的分区中,但是对于每个分区依然是有序的

    ②key不为null , 消息被写入到同一个分区,这个分区的消息都是有序。

  • RabbitMQ:不支持
  • ZeroMQ:不支持
  • RocketMQ:支持
  • ActiveMQ:不支持

 

十四、消息确认

 

1、Kafka

 

支持。

 

1)发送方确认机制

 

  • ack=0,不管消息是否成功写入分区
  • ack=1,消息成功写入首领分区后,返回成功
  • ack=all,消息成功写入所有分区后,返回成功。

 

2)接收方确认机制

 

自动或者手动提交分区偏移量,早期版本的Kafka偏移量是提交给Zookeeper的,这样使得zookeeper的压力比较大,更新版本的Kafka的偏移量是提交给Kafka服务器的,不再依赖于zookeeper群组,集群的性能更加稳定。

 

2、RabbitMQ

 

支持。

 

1)发送方确认机制,消息被投递到所有匹配的队列后,返回成功。如果消息和队列是可持久化的,那么在写入磁盘后,返回成功。支持批量确认和异步确认。

 

2)接收方确认机制,设置autoAck为false,需要显式确认,设置autoAck为true,自动确认。

 

当autoAck为false的时候,RabbitMQ队列会分成两部分,一部分是等待投递给consumer的消息,一部分是已经投递但是没收到确认的消息。

 

如果一直没有收到确认信号,并且consumer已经断开连接,RabbitMQ会安排这个消息重新进入队列,投递给原来的消费者或者下一个消费者。

 

未确认的消息不会有过期时间,如果一直没有确认,并且没有断开连接,RabbitMQ会一直等待,RabbitMQ允许一条消息处理的时间可以很久很久。

 

3、ZeroMQ

 

支持。

 

4、RocketMQ

 

支持。

 

5、ActiveMQ

 

支持。

 

十五、消息回溯

 

  • Kafka:支持指定分区offset位置的回溯
  • RabbitMQ:不支持
  • ZeroMQ:不支持
  • RocketMQ:支持指定时间点的回溯
  • ActiveMQ:不支持

 

十六、消息重试

 

1、Kafka

 

不支持,但是可以实现。

 

Kafka支持指定分区offset位置的回溯,可以实现消息重试。

 

2、RabbitMQ

 

不支持,但是可以利用消息确认机制实现。

 

RabbitMQ接收方确认机制,设置autoAck为false。

 

当autoAck为false的时候,RabbitMQ队列会分成两部分,一部分是等待投递给consumer的消息,一部分是已经投递但是没收到确认的消息。

 

如果一直没有收到确认信号,并且consumer已经断开连接,RabbitMQ会安排这个消息重新进入队列,投递给原来的消费者或者下一个消费者。

 

3、ZeroMQ

 

不支持。

 

4、RocketMQ

 

支持。

 

消息消费失败的大部分场景下,立即重试99%都会失败,所以RocketMQ的策略是在消费失败时定时重试,每次时间间隔相同。

 

1)发送端的 send 方法本身支持内部重试,重试逻辑如下:

 

  • 至多重试3次;
  • 如果发送失败,则轮转到下一个broker;
  • 这个方法的总耗时不超过sendMsgTimeout 设置的值,默认 10s,超过时间不在重试。

 

2)接收端。

 

Consumer 消费消息失败后,要提供一种重试机制,令消息再消费一次。Consumer 消费消息失败通常可以分为以下两种情况:

 

  • 由于消息本身的原因,例如反序列化失败,消息数据本身无法处理(例如话费充值,当前消息的手机号被注销,无法充值)等。定时重试机制,比如过 10s 秒后再重试。
  • 由于依赖的下游应用服务不可用,例如 db 连接不可用,外系统网络不可达等。即使跳过当前失败的消息,消费其他消息同样也会报错。这种情况可以 sleep 30s,再消费下一条消息,减轻 Broker 重试消息的压力。

 

5、ActiveMQ

 

不支持。

 

十七、并发度

 

1、Kafka

 

并发度高。

 

一个线程一个消费者,Kafka限制消费者的个数要小于等于分区数,如果要提高并行度,可以在消费者中再开启多线程,或者增加consumer实例数量。

 

2、RabbitMQ

 

并发度*高。本身是用Erlang语言写的,并发性能高。

 

可在消费者中开启多线程,*常用的做法是一个channel对应一个消费者,每一个线程把持一个channel,多个线程复用connection的tcp连接,减少性能开销。

 

当RabbitMQ队列拥有多个消费者的时候,队列收到的消息将以轮询的分发方式发送给消费者。每条消息只会发送给订阅列表里的一个消费者,不会重复。

 

这种方式非常适合扩展,而且是专门为并发程序设计的。

 

如果某些消费者的任务比较繁重,那么可以设置basicQos限制信道上消费者能保持的*大未确认消息的数量,在达到上限时,RabbitMQ不再向这个消费者发送任何消息。

 

3、ZeroMQ

 

并发度高。

 

4、RocketMQ

 

并发度高。

 

1)RocketMQ限制消费者的个数少于等于队列数,但是可以在消费者中再开启多线程,这一点和Kafka是一致的,提高并行度的方法相同。

 

修改消费并行度方法

 

  • 同一个 ConsumerGroup 下,通过增加 Consumer 实例数量来提高并行度,超过订阅队列数的 Consumer实例无效。
  • 提高单个 Consumer 的消费并行线程,通过修改参数consumeThreadMin、consumeThreadMax

 

2)同一个网络连接connection,客户端多个线程可以同时发送请求,连接会被复用,减少性能开销。

 

5、ActiveMQ

 

并发度高。

 

单个ActiveMQ的接收和消费消息的速度在1万笔/秒(持久化 一般为1-2万, 非持久化 2 万以上),在生产环境中部署10个ActiveMQ就能达到10万笔/秒以上的性能,部署越多的ActiveMQ broker 在MQ上latency也就越低,系统吞吐量也就越高。

springboot中SPI机制

一、从java类加载机制说起

  • java中的类加载器负载加载来自文件系统、网络或者其他来源的类文件。jvm的类加载器默认使用的是双亲委派模式。三种默认的类加载器Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader(Application ClassLoader)每一个中类加载器都确定了从哪一些位置加载文件。于此同时我们也可以通过继承java.lang.classloader实现自己的类加载器。

Bootstrap ClassLoader:负责加载JDK自带的rt.jar包中的类文件,是所有类加载的父类
Extension ClassLoader:负责加载java的扩展类库从jre/lib/ect目录或者java.ext.dirs系统属性指定的目录下加载类,是System ClassLoader的父类加载器
System ClassLoader:负责从classpath环境变量中加载类文件

java类加载结构

1、双亲委派模型

  • 原理:当一个类加载器收到类加载任务时,会先交给自己的父加载器去完成,因此*终加载任务都会传递到*顶层的BootstrapClassLoader,只有当父加载器无法完成加载任务时,才会尝试自己来加载。

具体:根据双亲委派模式,在加载类文件的时候,子类加载器首先将加载请求委托给它的父加载器,父加载器会检测自己是否已经加载过类,如果已经加载则加载过程结束,如果没有加载的话则请求继续向上传递直Bootstrap ClassLoader。如果请求向上委托过程中,如果始终没有检测到该类已经加载,则Bootstrap ClassLoader开始尝试从其对应路劲中加载该类文件,如果失败则由子类加载器继续尝试加载,直至发起加载请求的子加载器为止。

  • 采用双亲委派模式可以保证类型加载的安全性,不管是哪个加载器加载这个类,*终都是委托给顶层的BootstrapClassLoader来加载的,只有父类无法加载自己猜尝试加载,这样就可以保证任何的类加载器*终得到的都是同样一个Object对象。
  1. protected Class<?> loadClass(String name, boolean resolve) {
  2. synchronized (getClassLoadingLock(name)) {
  3. // 首先,检查该类是否已经被加载,如果从JVM缓存中找到该类,则直接返回
  4. Class<?> c = findLoadedClass(name);
  5. if (c == null) {
  6. try {
  7. // 遵循双亲委派的模型,首先会通过递归从父加载器开始找,
  8. // 直到父类加载器是BootstrapClassLoader为止
  9. if (parent != null) {
  10. c = parent.loadClass(name, false);
  11. } else {
  12. c = findBootstrapClassOrNull(name);
  13. }
  14. } catch (ClassNotFoundException e) {}
  15. if (c == null) {
  16. // 如果还找不到,尝试通过findClass方法去寻找
  17. // findClass是留给开发者自己实现的,也就是说
  18. // 自定义类加载器时,重写此方法即可
  19. c = findClass(name);
  20. }
  21. }
  22. if (resolve) {
  23. resolveClass(c);
  24. }
  25. return c;
  26. }
  27. }

2.双亲委派模型缺陷

  • 在双亲委派模型中,子类加载器可以使用父类加载器已经加载的类,而父类加载器无法使用子类加载器已经加载的。这就导致了双亲委派模型并不能解决所有的类加载器问题。
  • 案例:Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JNDI、JAXP 等,这些SPI的接口由核心类库提供,却由第三方实现,这样就存在一个问题:SPI 的接口是 Java 核心库的一部分,是由BootstrapClassLoader加载的;SPI实现的Java类一般是由AppClassLoader来加载的。BootstrapClassLoader是无法找到 SPI 的实现类的,因为它只加载Java的核心库。它也不能代理给AppClassLoader,因为它是*顶层的类加载器。也就是说,双亲委派模型并不能解决这个问题

3.使用线程上下文类加载器(ContextClassLoader)加载

  • 如果不做任何的设置,Java应用的线程的上下文类加载器默认就是AppClassLoader。在核心类库使用SPI接口时,传递的类加载器使用线程上下文类加载器,就可以成功的加载到SPI实现的类。线程上下文类加载器在很多SPI的实现中都会用到。
  • 通常我们可以通过Thread.currentThread().getClassLoader()和Thread.currentThread().getContextClassLoader()获取线程上下文类加载器。

4、使用类加载器加载资源文件,比如jar包

类加载器除了加载class外,还有一个非常重要功能,就是加载资源,它可以从jar包中读取任何资源文件,比如,ClassLoader.getResources(String name)方法就是用于读取jar包中的资源文件

  1. //获取资源的方法
  2. public Enumeration<URL> getResources(String name) throws IOException {
  3. Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
  4. if (parent != null) {
  5. tmp[0] = parent.getResources(name);
  6. } else {
  7. tmp[0] = getBootstrapResources(name);
  8. }
  9. tmp[1] = findResources(name);
  10. return new CompoundEnumeration<>(tmp);
  11. }

它的逻辑其实跟类加载的逻辑是一样的,首先判断父类加载器是否为空,不为空则委托父类加载器执行资源查找任务,直到BootstrapClassLoader,*后才轮到自己查找。而不同的类加载器负责扫描不同路径下的jar包,就如同加载class一样,*后会扫描所有的jar包,找到符合条件的资源文件。

  1. // 使用线程上下文类加载器加载资源
  2. public static void main(String[] args) throws Exception{
  3. // Array.class的完整路径
  4. String name = “java/sql/Array.class”;
  5. Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(name);
  6. while (urls.hasMoreElements()) {
  7. URL url = urls.nextElement();
  8. System.out.println(url.toString());
  9. }
  10. }

二、spring中SPI机制实现

1.SPI机制

(1)SPI思想

  • SPI的全名为Service Provider Interface.这个是针对厂商或者插件的。
  • SPI的思想:系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制

(2)SPI约定

  • 当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。通过这个约定,就不需要把服务放在代码中了,通过模块被装配的时候就可以发现服务类了。

2、SPI使用案例

  • common-logging apache*早提供的日志的门面接口。只有接口,没有实现。具体方案由各提供商实现, 发现日志提供商是通过扫描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通过读取该文件的内容找到日志提工商实现类。只要我们的日志实现里包含了这个文件,并在文件里制定 LogFactory工厂接口的实现类即可。

3、springboot中的类SPI扩展机制

  • 在springboot的自动装配过程中,*终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。
  1. public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;
  2. // spring.factories文件的格式为:key=value1,value2,value3
  3. // 从所有的jar包中找到META-INF/spring.factories文件
  4. // 然后从文件中解析出key=factoryClass类名称的所有value值
  5. public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
  6. String factoryClassName = factoryClass.getName();
  7. // 取得资源文件的URL
  8. Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  9. List<String> result = new ArrayList<String>();
  10. // 遍历所有的URL
  11. while (urls.hasMoreElements()) {
  12. URL url = urls.nextElement();
  13. // 根据资源文件URL解析properties文件,得到对应的一组@Configuration类
  14. Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
  15. String factoryClassNames = properties.getProperty(factoryClassName);
  16. // 组装数据,并返回
  17. result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
  18. }
  19. return result;
  20. }

转载自:https://www.jianshu.com/p/0d196ad23915

深入拆解Tomcat,Endpoint

一、NioEndpoint 组件:Tomcat 如何实现非阻塞 I/O

UNIX 系统下的 I/O 模型有 5 种:同步阻塞 I/O、同步非阻塞 I/O、I/O 多路复用、信号驱动 I/O 和异步 I/O。

I/O 就是计算机内存与外部设备之间拷贝数据的过程。

1,同步阻塞 I/O

用户线程发起 read 调用后就阻塞了,让出 CPU。内核等待网卡数据到来,把数据从网卡拷贝到内核空间,接着把数据拷贝到用户空间,再把用户线程叫醒。

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

2,同步非阻塞 I/O

用户线程不断的发起 read 调用,数据没到内核空间时,每次都返回失败,直到数据到了内核空间,这一次 read 调用后,在等待数据从内核空间拷贝到用户空间这段时间里,线程还是阻塞的,等数据到了用户空间再把线程叫醒。

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

3,I/O 多路复用

用户线程的读取操作分成两步了,线程先发起 select 调用,目的是问内核数据准备好了吗?等内核把数据准备好了,用户线程再发起 read 调用。在等待数据从内核空间拷贝到用户空间这段时间里,线程还是阻塞的。那为什么叫 I/O 多路复用呢?因为一次 select 调用可以向内核查多个数据通道(Channel)的状态,所以叫多路复用。

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

4,异步 I/O

用户线程发起 read 调用的同时注册一个回调函数,read 立即返回,等内核将数据准备好后,再调用指定的回调函数完成处理。在这个过程中,用户线程一直没有阻塞。

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

Tomcat 的 NioEndPoint 组件实现了 I/O 多路复用模型,它一共包含 LimitLatch、Acceptor、Poller、SocketProcessor 和 Executor 共 5 个组件。

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

LimitLatch 是连接控制器,它负责控制*大连接数,NIO 模式下默认是 10000,达到这个阈值后,连接请求被拒*。

Acceptor 跑在一个单独的线程里,它在一个死循环里调用 accept 方法来接收新连接,一旦有新的连接请求到来,accept 方法返回一个 Channel 对象,接着把 Channel 对象交给 Poller 去处理。

Poller 的本质是一个 Selector,也跑在单独线程里。Poller 在内部维护一个 Channel 数组,它在一个死循环里不断检测 Channel 的数据就绪状态,一旦有 Channel 可读,就生成一个 SocketProcessor 任务对象扔给 Executor 去处理。每个 Poller 线程都有自己的 Queue。每个 Poller 线程可能同时被多个 Acceptor 线程调用来注册 PollerEvent。Poller 不断的通过内部的 Selector 对象向内核查询 Channel 的状态,一旦可读就生成任务类 SocketProcessor 交给 Executor 去处理。Poller 的另一个重要任务是循环遍历检查自己所管理的 SocketChannel 是否已经超时,如果有超时就关闭这个 SocketChannel。

Executor 就是线程池,负责运行 SocketProcessor 任务类,SocketProcessor 的 run 方法会调用 Http11Processor 来读取和解析请求数据。ServerSocketChannel 通过 accept() 接受新的连接,accept() 方法返回获得 SocketChannel 对象,然后将 SocketChannel 对象封装在一个 PollerEvent 对象中,并将 PollerEvent 对象压入 Poller 的 Queue 里,这是个典型的生产者 – 消费者模式,Acceptor 与 Poller 线程之间通过 Queue 通信。

高并发就是能快速地处理大量的请求,需要合理设计线程模型让 CPU 忙起来,尽量不要让线程阻塞,因为一阻塞,CPU 就闲下来了。另外就是有多少任务,就用相应规模的线程数去处理。我们注意到 NioEndpoint 要完成三件事情:接收连接、检测 I/O 事件以及处理请求,那么*核心的就是把这三件事情分开,用不同规模的线程去处理,

I/O 模型是为了解决内存和外部设备速度差异的问题。我们平时说的阻塞或非阻塞是指应用程序在发起 I/O 操作时,是立即返回还是等待。而同步和异步,是指应用程序在与内核通信时,数据从内核空间到应用空间的拷贝,是由内核主动发起还是由应用程序来触发。

二、Nio2Endpoint 组件:Tomcat 如何实现异步 I/O

1,Nio2Endpoint

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

Nio2Acceptor 扩展了 Acceptor,用异步 I/O 的方式来接收连接,跑在一个单独的线程里,也是一个线程组。Nio2Acceptor 接收新的连接后,得到一个 AsynchronousSocketChannel,Nio2Acceptor 把 AsynchronousSocketChannel 封装成一个 Nio2SocketWrapper,并创建一个 SocketProcessor 任务类交给线程池处理,并且 SocketProcessor 持有 Nio2SocketWrapper 对象。

Executor 在执行 SocketProcessor 时,SocketProcessor 的 run 方法会调用 Http11Processor 来处理请求,Http11Processor 会通过 Nio2SocketWrapper 读取和解析请求数据,请求经过容器处理后,再把响应通过 Nio2SocketWrapper 写出。

Nio2Endpoint 中没有 Poller 组件,也就是没有 Selector。因为在异步 I/O 模式下,Selector 的工作交给内核来做了。

2,Nio2Acceptor

Nio2Acceptor 的监听连接的过程不是在一个死循环里不断的调 accept 方法,而是通过回调函数来完成的。

三、AprEndPoint 组件:Tomcat APR 提高 I/O 性能的秘密

APR(Apache Portable Runtime Libraries)是 Apache 可移植运行时库,它是用 C 语言实现的,其目的是向上层应用程序提供一个跨平台的操作系统接口库。Tomcat 可以用它来处理包括文件和网络 I/O,从而提升性能。

跟 NioEndpoint 一样,AprEndpoint 也实现了非阻塞 I/O,它们的区别是:NioEndpoint 通过调用 Java 的 NIO API 来实现非阻塞 I/O,而 AprEndpoint 是通过 JNI 调用 APR 本地库而实现非阻塞 I/O 的。

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

1,APR 提升性能的秘密

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

Tomcat 的 Endpoint 组件在接收网络数据时需要预先分配好一块 Buffer,所谓的 Buffer 就是字节数组byte[],Java 通过 JNI 调用把这块 Buffer 的地址传给 C 代码,C 代码通过操作系统 API 读取 Socket 并把数据填充到这块 Buffer。Java NIO API 提供了两种 Buffer 来接收数据:HeapByteBuffer 和 DirectByteBuffer。

HeapByteBuffer 对象本身在 JVM 堆上分配,并且它持有的字节数组byte[]也是在 JVM 堆上分配。但是如果用HeapByteBuffer来接收网络数据,需要把数据从内核先拷贝到一个临时的本地内存,再从临时本地内存拷贝到 JVM 堆,而不是直接从内核拷贝到 JVM 堆上。这是为什么呢?这是因为数据从内核拷贝到 JVM 堆的过程中,JVM 可能会发生 GC,GC 过程中对象可能会被移动,也就是说 JVM 堆上的字节数组可能会被移动,这样的话 Buffer 地址就失效了。如果这中间经过本地内存中转,从本地内存到 JVM 堆的拷贝过程中 JVM 可以保证不做 GC。

DirectByteBuffer 对象本身在 JVM 堆上,但是它持有的字节数组不是从 JVM 堆上分配的,而是从本地内存分配的。

DirectByteBuffer 对象本身在 JVM 堆上,但是它持有的字节数组不是从 JVM 堆上分配的,而是从本地内存分配的。

2,sendfile

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

Tomcat 的 AprEndpoint 通过操作系统层面的 sendfile 特性解决了这个问题,sendfile 系统调用方式非常简洁。

京东年薪60W架构师带你深入拆解Tomcat,Endpoint

 

除此之外,APR 提升性能的秘密还有:通过 DirectByteBuffer 避免了 JVM 堆与本地内存之间的内存拷贝;通过 sendfile 特性避免了内核与应用之间的内存拷贝以及用户态和内核态的切换。

一次线上Redis性能优化

记录一次线上Redis优化:

一、 现象描述

线上环境Redis采用哨兵模式,版本为3.0.7,服务器采用4C8G,TPS在20000左右,当设备(NB)在线用户数超过12万后,主节点单核CPU从30%左右直线上升至将近60%, CPU负荷高居不下且不稳定间接影响业务量的提升,亟待优化。

由于redis单线程的设计特性,任何一条阻塞的命令都会引起redis整个实例的阻塞,所以在使用过程中,我们首先从Linux环境情况、命令分布,key情况,客户端情况等方面检查。

二、 检查指标

  1. 1. 内存使用情况

redis使用内存为800M,linux内存使用率85%,未使用SWAP。(因为有其他java进程共用服务器),属于正常情况。

  1. 2. 单核CPU使用情况

 

(线上问题系列三)记录一次线上Redis性能优化

 

 

  1. 3. 整体CPU使用情况

整体25%左右,正常范围。

  1. 4. IO,磁盘IO和网络IO使用情况

我们发现磁盘IO比较低,而网络出口流量达到15M,说明读数据量比较大。

  1. 5. 网络延迟情况ICMP response time

检查网络响应时间在5ms以内,属于正常范围。

  1. 6. 命令执行分布和时间,info commandstats命令。

检查发现,在短时间内有大量的hgetall调用,hgetall方法是将hash table里面的所有内容一次性全部读出来,如果哈希值的内容比较大,*容易导致堵塞。是一个优化点。

  1. 7. TPS,检查TPS的方法可以统计total_commands_processed增长量

当前TPS不到2.5万,未达到瓶颈点。

  1. 8. 检查慢查询slowlog get 128,这个可以定位优化。

可以看到慢查询命令是主要是hmset,hgetall和scan等,不过整体慢查询时间能接受。

  1. 9. 检查客户端列表,检查连接时间和内存消耗omem的客户端。

redis-cli -p 6379 -h xxxx –a xxxx client list |grep -v “omem=0”

发现有较多长期连接的客户端,总的客户端数量650。

  1. 10. bigkey检查,优化比较大的key

redis-cli -p 6379 -h xxxx –a xxxx –-bigkeys

发现较多bigkey,后期需要进行优化。

 

(线上问题系列三)记录一次线上Redis性能优化

 

 

三、 检查服务器内核参数(Redis官方建议设置,服务器默认未设置,需要手工设置)

  1. 1. 内存分配

Redis是内存操作,需要优先使用内存。设置overcommit 为1。

  1. 2. THP禁用

THP开启后,*大内存页为2M,因为fork 调用的copy-on-write机制是基于操作系统页这个单位的,也就是只有有写入的脏页会被复制,但是一般系统不会在短时间内所有的页都发生了写入而导致复制,所以每次写命令引起的内存复制从4K变成了2M,从而拖慢写操作的执行时间,导致大量写操作慢查询。所以确保禁用Linux内核特性transparent huge pages,通过命令echo never >sys/kernel/mm/transparent_hugepage/enabled来设置。

  1. 3. swap使用概率

防止内存溢出OOM,故设置允许使用swap。(Redis本身不建议使用SWAP,OOM的情况万不得已)。echo {bestvalue} > /proc/sys/vm/swappiness

  1. 4. ulimit设置

需要设置比*大客户端连接数要大至少32。因为此服务器还有java应用程序,所以需要设置得更大。

  1. 5. TCPbacklog 设置

设置TCP等待连接队列长度为*大值。

echo 511 > /proc/sys/net/core/somaxconn

四、 检查配置文件

  1. 1. 内存设置

若是启用了Redis快照功能,应该设置“maxmemory”值为系统可使用内存的45%,设置一个明确的maxmemory参数来限制你的实例,以便确保实例会报告错误而不是当接近系统内存限制时失败。此Redis服务器内存空间较多,暂未设置。

  1. 2. 绑定本机网卡

检查日志会发现陆陆续续有较多错误:Error condition on socketfor SYNC: Connection refused。发现是连接问题,需要在主节点配置文件绑定本机IP,增加bind x.x.x.x

  1. 3. 检查数据持久化策略

数据落磁盘尽可能减少性能损坏,以空间换时间。设置如下命令:

rdbcompression no

rdbchecksum no

  1. 4. 客户端timeout

设置一个超时时间,防止无用的连接占用资源。设置如下命令:

timeout 150

tcp-keepalive 150

  1. 5. 优化AOF和RDB,减少占用CPU时间

主库可以不进行dump操作或者降低dump频率。

取消AOF持久化。命令如下:

appendonly no

五、 优化后情况

经过优化后观察,业务量已经增加了30%,TPS达到了3.5万,网络流量已经增加了30%,从15M增加到了20M多,而单核cpu平均值反而降到了50%以下。所有CPU平均值在25%左右。Redis用户连接数减少了60%,当前连接在260左右。观察日志,未发现有错误信息。

 

(线上问题系列三)记录一次线上Redis性能优化

 

 

六、 后续考虑改进方案

1. 考虑读写分离。

2. 减少时间复杂度比较高的操作,比如hgetall。

3. bigkey优化。

4. 其他无相关业务迁移。

浅析MySQL事务中的redo与undo

我们都知道事务有4种特性:原子性、一致性、隔离性和持久性,在事务中的操作,要么全部执行,要么全部不做,这就是事务的目的。事务的隔离性由锁机制实现,原子性、一致性和持久性由事务的redo 日志和undo 日志来保证。所以本篇文章将讨论关于事务中的redo和undo的几个问题:

  • redo 日志与undo日志分别是什么?
  • redo 如何保证事务的持久性?
  • undo log 是否是redo log的逆过程?

redo log

Redo 的类型

重做日志(redo log)用来保证事务的持久性,即事务ACID中的D。实际上它可以分为以下两种类型:

  • 物理Redo日志
  • 逻辑Redo日志

在InnoDB存储引擎中,大部分情况下 Redo是物理日志,记录的是数据页的物理变化。而逻辑Redo日志,不是记录页面的实际修改,而是记录修改页面的一类操作,比如新建数据页时,需要记录逻辑日志。关于逻辑Redo日志涉及更加底层的内容,这里我们只需要记住*大数情况下,Redo是物理日志即可,DML对页的修改操作,均需要记录Redo.

Redo 的作用

Redo log的主要作用是用于数据库的崩溃恢复

Redo 的组成

Redo log可以简单分为以下两个部分:

  • 一是内存中重做日志缓冲 (redo log buffer),是易失的,在内存中
  • 二是重做日志文件 (redo log file),是持久的,保存在磁盘中

什么时候写Redo?

上面那张图简单地体现了Redo的写入流程,这里再细说下写入Redo的时机:

  • 在数据页修改完成之后,在脏页刷出磁盘之前,写入redo日志。注意的是先修改数据,后写日志
  • redo日志比数据页先写回磁盘
  • 聚集索引、二级索引、undo页面的修改,均需要记录Redo日志。

Redo的整体流程

下面以一个更新事务为例,宏观上把握redo log 流转过程,如下图所示:

浅析MySQL事务中的redo与undo

 

  • *步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
  • 第二步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
  • 第三步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式
  • 第四步:定期将内存中修改的数据刷新到磁盘中

redo如何保证 事务的持久性?

InnoDB是事务的存储引擎,其通过Force Log at Commit 机制实现事务的持久性,即当事务提交时,先将 redo log buffer 写入到 redo log file 进行持久化,待事务的commit操作完成时才算完成。这种做法也被称为 Write-Ahead Log(预先日志持久化),在持久化一个数据页之前,先将内存中相应的日志页持久化。

为了保证每次日志都写入redo log file,在每次将redo buffer写入redo log file之后,默认情况下,InnoDB存储引擎都需要调用一次 fsync操作,因为重做日志打开并没有 O_DIRECT选项,所以重做日志先写入到文件系统缓存。为了确保重做日志写入到磁盘,必须进行一次 fsync操作。fsync是一种系统调用操作,其fsync的效率取决于磁盘的性能,因此磁盘的性能也影响了事务提交的性能,也就是数据库的性能。

(O_DIRECT选项是在Linux系统中的选项,使用该选项后,对文件进行直接IO操作,不经过文件系统缓存,直接写入磁盘)

上面提到的Force Log at Commit机制就是靠InnoDB存储引擎提供的参数 innodb_flush_log_at_trx_commit来控制的,该参数可以控制 redo log刷新到磁盘的策略,设置该参数值也可以允许用户设置非持久性的情况发生,具体如下:

  • 当设置参数为1时,(默认为1),表示事务提交时必须调用一次 fsync 操作,*安全的配置,保障持久性
  • 当设置参数为2时,则在事务提交时只做 write 操作,只保证将redo log buffer写到系统的页面缓存中,不进行fsync操作,因此如果MySQL数据库宕机时 不会丢失事务,但操作系统宕机则可能丢失事务
  • 当设置参数为0时,表示事务提交时不进行写入redo log操作,这个操作仅在master thread 中完成,而在master thread中每1秒进行一次重做日志的fsync操作,因此实例 crash *多丢失1秒钟内的事务。(master thread是负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性)

fsync和write操作实际上是系统调用函数,在很多持久化场景都有使用到,比如 Redis 的AOF持久化中也使用到两个函数。fsync操作 将数据提交到硬盘中,强制硬盘同步,将一直阻塞到写入硬盘完成后返回,大量进行fsync操作就有性能瓶颈,而write操作将数据写到系统的页面缓存后立即返回,后面依靠系统的调度机制将缓存数据刷到磁盘中去,其顺序是user buffer——> page cache——>disk。

浅析MySQL事务中的redo与undo

 

除了上面谈到的Force Log at Commit机制保证事务的持久性,实际上重做日志的实现还要依赖于mini-transaction。

Redo在InnoDB中是如何实现的?与mini-transaction的联系?

Redo的实现实则跟mini-transaction紧密相关,mini-transaction是一种InnoDB内部使用的机制,通过mini-transaction来保证并发事务操作下以及数据库异常时数据页中数据的一致性,但它不属于事务。

为了使得mini-transaction保证数据页数据的一致性,mini-transaction必须遵循以下三种协议

  • The FIX Rules
  • Write-Ahead Log
  • Force-log-at-commit

The FIX Rules

修改一个数据页时需要获得该页的x-latch(排他锁),获取一个数据页时需要该页的s-latch(读锁或者称为共享锁) 或者是 x-latch,持有该页的锁直到修改或访问该页的操作完成。

Write-Ahead Log

在前面阐述中就提到了Write-Ahead Log(预先写日志)。在持久化一个数据页之前,必须先将内存中相应的日志页持久化。每个页都有一个LSN(log sequence number),代表日志序列号,(LSN占用8字节,单调递增), 当一个数据页需要写入到持久化设备之前,要求内存中小于该页LSN的日志先写入持久化设备

那为什么必须要先写日志呢?可不可以不写日志,直接将数据写入磁盘?原则上是可以的,只不过会产生一些问题,数据修改会产生随机IO,但日志是顺序IO,append方式顺序写,是一种串行的方式,这样才能充分利用磁盘的性能。

Force-log-at-commit

这一点也就是前文提到的如何保证事务的持久性的内容,这里再次总结一下,与上面的内容相呼应。在一个事务中可以修改多个页,Write-Ahead Log 可以保证单个数据页的一致性,但是无法保证事务的持久性,Force-log-at-commit 要求当一个事务提交时,其产生所有的mini-transaction 日志必须刷新到磁盘中,若日志刷新完成后,在缓冲池中的页刷新到持久化存储设备前数据库发生了宕机,那么数据库重启时,可以通过日志来保证数据的完整性。

重做日志的写入流程

浅析MySQL事务中的redo与undo

 

上图表示了重做日志的写入流程,每个mini-transaction对应每一条DML操作,比如一条update语句,其由一个mini-transaction来保证,对数据修改后,产生redo1,首先将其写入mini-transaction私有的Buffer中,update语句结束后,将redo1从私有Buffer拷贝到公有的Log Buffer中。当整个外部事务提交时,将redo log buffer再刷入到redo log file中。

undo log

undo log的定义

undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。

undo log的作用

undo是一种逻辑日志,有两个作用:

  • 用于事务的回滚
  • MVCC

关于MVCC(多版本并发控制)的内容这里就不多说了,本文重点关注undo log用于事务的回滚。

undo日志,只将数据库逻辑地恢复到原来的样子,在回滚的时候,它实际上是做的相反的工作,比如一条INSERT ,对应一条 DELETE,对于每个UPDATE,对应一条相反的 UPDATE,将修改前的行放回去。undo日志用于事务的回滚操作进而保障了事务的原子性。

undo log的写入时机

  • DML操作修改聚簇索引前,记录undo日志
  • 二级索引记录的修改,不记录undo日志

需要注意的是,undo页面的修改,同样需要记录redo日志。

undo的存储位置

在InnoDB存储引擎中,undo存储在回滚段(Rollback Segment)中,每个回滚段记录了1024个undo log segment,而在每个undo log segment段中进行undo 页的申请,在5.6以前,Rollback Segment是在共享表空间里的,5.6.3之后,可通过 innodb_undo_tablespace设置undo存储的位置。

undo的类型

在InnoDB存储引擎中,undo log分为:

  • insert undo log
  • update undo log

insert undo log是指在insert 操作中产生的undo log,因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除,不需要进行purge操作。

而update undo log记录的是对delete 和update操作产生的undo log,该undo log可能需要提供MVCC机制,因此不能再事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行*后的删除。

补充:purge线程两个主要作用是:清理undo页和清除page里面带有Delete_Bit标识的数据行。在InnoDB中,事务中的Delete操作实际上并不是真正的删除掉数据行,而是一种Delete Mark操作,在记录上标识Delete_Bit,而不删除记录。是一种”假删除”,只是做了个标记,真正的删除工作需要后台purge线程去完成。

undo log 是否是redo log的逆过程?

undo log 是否是redo log的逆过程?其实从前文就可以得出答案了,undo log是逻辑日志,对事务回滚时,只是将数据库逻辑地恢复到原来的样子,而redo log是物理日志,记录的是数据页的物理变化,显然undo log不是redo log的逆过程。

redo & undo总结

下面是redo log + undo log的简化过程,便于理解两种日志的过程:

假设有A、B两个数据,值分别为1,2.
1. 事务开始
2. 记录A=1到undo log
3. 修改A=3
4. 记录A=3到 redo log
5. 记录B=2到 undo log
6. 修改B=4
7. 记录B=4到redo log
8. 将redo log写入磁盘
9. 事务提交

实际上,在insert/update/delete操作中,redo和undo分别记录的内容都不一样,量也不一样。在InnoDB内存中,一般的顺序如下:

  • 写undo的redo
  • 写undo
  • 修改数据页
  • 写Redo

深入解析MySQL索引原理

转载自:https://www.toutiao.com/a6706740989316301323/?app=news_article_lite&is_hit_share_recommend=0&tt_from=copy_link&utm_source=copy_link&utm_medium=toutiao_ios&utm_campaign=client_share

概述

*近一段时间重新深入研究了一遍MySQL的内容,今天主要分享分析MySQL索引原理,后续会输出一些关于MySQL方面的干货,希望各位小伙伴喜欢。

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

一、什么是索引、为什么要建立索引?

关于索引的理解,个人更加喜欢将其比喻为字典里面的目录,根据字典来进行查询的速度远大于每一页逐个逐个字排查的速度。

索引主要用于快速找出在某个列中有特定值的行,倘若不使用索引,MySQL必须从*条记录开始读完整个表,直到找出相关的行,表越大,查询数据所花费的时间就越多。如果表中查询的列有一个索引,MySQL能够快速到达一个位置去搜索数据,而不必查找所有数据,那么将会节省很大一部分时间。

(一)、索引的存储文件是如何的?

首先我们来看看mysql索引数据的存储位置:

mysql的数据存储是存放在mysql的data文件夹底下,截图如下所示:

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

查看该文件夹底下,我们通常可以看到相关的信息内容:

.frm表结构文件,和所选用的存储引擎无关。

(二)、MyISAM引擎的文件格式

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

1、myd 数据文件后缀

2、myi 索引文件后缀

(三)、InnoDB引擎的文件格式

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

1、ibd 将索引信息和数据一起存储起来

二、索引的结构是什么?为何不采用别的结构?

索引在起初做设计的时候其实是有一定数据结构选型的,对于不同的数据结构基础,我做了以下的相关总结:

(一)、使用二叉树作为索引结构缺陷

容易导致二叉树出现结构偏移,*端情况容易变成一条链表的形状。

例如下方情况:

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

(二)、使用红黑树数据结构的缺陷

红黑树虽然有自动进行树节点的二叉平衡功能。虽然相对于二叉树而言,不会有太严重的单边偏移情况,但还是避免不了*端情况下树的重心出现偏移的现象。(数据量变大的情况下,深度会变大)

使用红黑树数据结构容易在*端的情况下发生红黑树失重情况,如下图所示,随着数据量的增大,失重情况愈发严重:

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

 

(三)、hash索引的缺陷

虽然说hash查询的速度很快,但是依然有以下缺陷:

1、无法解决查询范围导致的问题

2、无法解决hash冲突的问题

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

 

三、如何理解BTree

在学习索引的真正结构之前,首先我们需要来了解一下什么是BTree。

(一)、BTree的基本结构如下图所示:

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

 

(二)、BTree里面包含有几个重要的特点

1、度(Degree)-节点的数据存储个数,一旦存储数据超过度值就要进行节点分裂

2、所有叶子节点具有相同的深度

3、叶子节点的指针为空

4、叶子节点中的数据key从左到右递增排列

btree数据结构通过节点的横向扩展,从而压低整个Btree的高度,减少了节点io读取的次数。通常百万级别的数据会被压到3-5层的高度。

四、节点数据读取过程发生了什么?

我在初学索引的时候,觉得io次数读取越少,因此获取数据的速度会越快。后来重新回看了一遍操作系统原理的讲解之后,对于这块的内容有了更加深入的认识。

(一)、磁盘数据读取

指针在同一磁道上边读取数据的时候如果采用的是顺序读取方式,那么数据的读取效率就会比较高效。倘若数据在进行读取的时候采用了随机读取的方式,则需要进行磁道的变换,这种磁道变换读取所带来的性能消耗会是巨大的。每个节点存放的地址通常都不会是连续的,不可避免的会有一些内存碎片存在,因此每一次进行io读取都会是比较耗费性能的。

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

 

btree里面的节点采用了key-value的基本存储结构,key是索引的数值,value是存储的data数值。由于我们对于btree进行节点比较的时候是基于内存进行数据比较,先从磁盘进行io读取数据,读取到cpu缓存中进行比对。

在了解了磁道读取问题之后,我们再来思考一下索引的遍历。

如下图所示:

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

如图所示,假设我们没有采用索引,要读取数据第七行(23),则需要进行7次的io读取操作。假若使用了索引,则可以通过走索引的方式,34–>22–>23一共三次io读取操作即可查找到所要的节点23。

(二)、度值无限扩增会如何?

如果我们将节点的度设置到*致,例如说将度设置到100W,那么BTree的高度就会降低,查询的次数就会大大减少,是否可行?

这种想法是不可行的,节点会变得过大。每次进行节点数据读取的时候都需要将磁盘的数据加载到操作系统自身的缓存中。假设将度值设置过大,io一次读取的大小还是有限,过大的节点还是需要进行多次的io读取。

计算机里面的内存和磁盘之间进行数据读取是按照计算机自身标准来进行设置的,每次读取的大小空间基本单位通常称之为,假设每一页的数据读取通常是8k(不同硬件设备大小不同),假若说一个节点的大小为10M,那么读取该节点里面的数据则需要进行多次的io操作。(通常DBA在进行数据库的度值设置的时候默认是将一个页单位的大小设置为度的大小。)

(三)、磁盘 IO

一个数据库必须保证其中存储的所有数据都是可以随时读写的,同时因为MySQL 中所有的数据其实都是以文件的形式存储在磁盘上的,而从磁盘上随机访问对应的数据非常耗时,所以数据库程序和操作系统提供了缓冲池内存以提高数据的访问速度。

除此之外,我们知道数据库对数据的读取并不是以行为单位进行的,无论是读取一行还是多行,都会将该行或者多行所在的页全部加载进来,然后再读取对应的数据记录;也就是说,读取所耗费的时间与行数无关,只与页数有关。(这也就是数据库的预读取机制)

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

页大小可以如下操作显示:

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

在 MySQL 中,页的大小一般为 16KB,不过也可能是 8KB、32KB 或者其他值,这跟 MySQL 的存储引擎对数据的存储方式有很大的关系。

关于数据库的缓存池原理剖析日后在后续我会进行解析,这里暂时先略过。

五、如何进行BTree的优化?

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

如果每个节点的大小越小,则操作系统读取一页数据的内容则越多,内存中可容纳的数据量增加,处理的效率也会有所提升。因此B+Tree就此诞生了。

(一)、如何理解B+Tree?

B+Tree的基本结构如下图所示:

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

(二)、B+Tree几个比较重要的特点:

1、非叶子节点不存储data,只存储key,可以增大度的值。

2、叶子节点不存储指针。

3、顺序访问指针,提高区间访问的性能。

仔细观察图中树结构的同学不知道是否有发现,B+Tree里面对于索引数据进行了适当的冗余存储,但是这一点相比于度大小的增加而言,并不会带来太多的性能影响。由于非叶子结点只存储key,并没有存储data数据,因此所有的非叶子结点的度可以增加地更大,使得一次io读取的数据更多,从磁盘读取到操作系统内存中的数据也大大增加。

仔细观察B+Tree里面的数据结构,会发现叶子节点里面有相应的顺序访问指针。

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

B+Tree的叶子节点之间的顺序访问指针的作用可以提高范围查询的效率。

例如说在一定范围内进行数据查询的时候不需要像BTree那样,每次在进行节点数据读取的时候都需要重新从顶部开始读取,可以根据相应的顺序指针直接进行连续读取,提高了范围性读取数据的效率。

六、Myisam非聚簇索引

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

之前我们有说过mysql里面数据存储的位置和文件信息。myisam存储引擎里面,mysql读取数据的时候会有一个文件指针,根据指针来读取数据文件的顺序为:.myi—>.myd(读取数据文件里面的内容然后查找数据)

存储引擎里面的索引通常会被划分为主键索引和非主键索引(一般是指除了主键索引之外的那些索引)

(一)、主键索引:

主键索引比较好理解,B+Tree里面的叶子节点的key存储的是索引值,value存储的是相应data数据的指针。

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

(二)、辅助索引:

Myisam里面的辅助索引结构和主键索引基本一致,结构如下图所示

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

(三)、Innodb聚簇索引

在上边我们提及过innodb存储中,是将数据和索引放在了同一份文件里面的。

因此innodb里面的非主键索引(辅助索引)和主键索引有一定的区别,下边我们来用图例进行详细对比说明:

innodb里面的主键索引和非主键索引:

(四)、主键索引

主键索引里面的存储情况如下图所示,叶子节点里面的key是索引的信息,value是数据表里面该索引对应行的数据信息。

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

(五)、非主键索引

这种索引在进行存储的时候,虽然说节点依然是key-value的结构,key对应的是相应的索引,但是value对应的却是主键索引的值。如下图所示:

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

 

如果索引里面存储的值不是数字类型而是字符串类型,那么在进行索引插入或者索引查找的时候都需要通过计算每个字符的ASCII码来判断索引的大小。

七、为什么InnoDB表必须有主键?

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

在innodb存储引擎里面,数据的默认的存储结构默认是以B+Tree的结构来进行存储的,因此必须要有主键存在。

即使手动没有设置主键,innodb也会自动选取一列数据进行设置,或者默认生成一列作为主键。

(一)、使用uuid和自增id作为主键的区别?

在了解了索引原理之后,我们不妨从底层来思考一下使用uuid替代自增id带来的缺陷。

1、由于uuid的长度普遍要比自增id的长度要长,因此uuid更加占用磁盘空间。

2、uuid通常都是以字符串的形式存在的,因此在进行比较的时候大多都是基于字符串的形式进行比较的(使用ack||码),而自增主键使用的是integer的比较方式。效率方面差异较大。

3、uuid在进行数据插入的时候可能会插入到某个度已经满了的节点里面,这个时候原有的旧节点就需要进行节点的分裂操作了,需要进行数据的移动,降低性能效率。

(二)、联合索引的底层存储结构是如何的?

在实际项目开发中,联合索引是我们经常会用到的优化技巧之一,本文暂时先不讲优化,先从底层去理解联合索引的存储方式,后边会展开优化的讲解。例如一个联合索引里面用到了id,职位名称,出生日期三个字段,那么在存储的时候会将三个字段一起存入节点里面的key位置。如下图所示:

 

干货篇:一篇文章让你——《深入解析MySQL索引原理》

 

在索引节点里面的key进行比对的时候,会按照id,职位名称,出生日期的顺序(由左到右),比较的时候是逐一比对,一旦没有匹配到指定字段,则不会继续匹配,这也是我们常说的复合索引里面的*左前缀匹配原则。

MySQL中interactive_timeout和wait_timeout的区别

在用mysql客户端对数据库进行操作时,打开终端窗口,如果一段时间没有操作,再次操作时,常常会报如下错误:

ERROR 2013 (HY000): Lost connection to MySQL server during query
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...

 

这个报错信息就意味着当前的连接已经断开,需要重新建立连接。

 

那么,连接的时长是如何确认的?

其实,这个与interactive_timeout和wait_timeout的设置有关。

 

 

首先,看看官方文档对于这两个参数的定义

interactive_timeout

默认是28800,单位秒,即8个小时

The number of seconds the server waits for activity on an interactive connection before closing it. An interactive client is defined as a client that uses the CLIENT_INTERACTIVE option to mysql_real_connect(). See also wait_timeout.

 

wait_timeout

默认同样是28800s

The number of seconds the server waits for activity on a noninteractive connection before closing it.

On thread startup, the session wait_timeout value is initialized from the global wait_timeout value or from the global interactive_timeout value, depending on the type of client (as defined by the CLIENT_INTERACTIVE connect option to mysql_real_connect()). See also interactive_timeout.

 

根据上述定义,两者的区别显而易见

1> interactive_timeout针对交互式连接,wait_timeout针对非交互式连接。所谓的交互式连接,即在mysql_real_connect()函数中使用了CLIENT_INTERACTIVE选项。

说得直白一点,通过mysql客户端连接数据库是交互式连接,通过jdbc连接数据库是非交互式连接。

2> 在连接启动的时候,根据连接的类型,来确认会话变量wait_timeout的值是继承于全局变量wait_timeout,还是interactive_timeout。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速