PHP使用ffmpeg实现视频截图(Linux系统和windows系统)

环境:php5.6,apache2.2

windows7:

ffmpeg-windows下载地址:https://download.csdn.net/download/qq_39545346/10312836

说明:

将ffmpeg中的所有dll文件和ext文件扔到C盘下的system32文件夹。

执行下面的代码:
$name = md5(date(‘YmdHis’)).”.png”;  
        $from = “E:\1.mp4”;  
        $to = “E:\cover_images\”;  
        $str = “ffmpeg -i “.$from.” -y -f mjpeg -ss 3 -t 1 -s 740×500 “.$to.$name;  
        system($str);

Linux(centos6.8):

根据安装教程在linux上安装完ffmpeg
直接使用exec函数,在php代码中执行linux命令,即可进行截图:
exec(‘/usr/local/bin/ffmpeg -ss 00:00:01  -i ./1.mp4 ./pic/423.jpg  -r 1 -vframes 1 -an -f mjpeg 1>/dev/null’);
注意,运行时应保证以下几点:
1.保存截图文件的文件夹有相关权限,
2.截取的视频文件有相关权限,
3.php没有禁用exec()函数,在php.ini中可以查看disabled_function
4.web访问用户,即apache服务默认用户有执行ffmepg的权限,
apache默认用户在httpd.conf中查看。
5.修改etc下的sudoers文件,新增加
Defaults visiblepw
%apache ALL=(ALL) NOPASSWD:/usr/bin/sudo, /usr/local/bin/MP4Box, /usr/local/bin/ffmpeg
给予apache用户相关权限。

centos7下javac:未找到命令的问题

在linux下编译java程序,执行javac编译生成class文件时,在centos7终端输入如,javac hello.java    会提示未找到指令,但用java -verison测试环境变量是没问题的

百度了好久,说的很复杂,重新再linux配置环境变量,输入 vi /etc/profile进入,添加以下代码:

export JAVA_HOME=/usr/local/jdk1.8.0_144
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

再测试,最后也没有成功

后来在stackoverflow上看到了这个

84 down vote accepted
You installed the Java Runtime Environment (JRE) only, which does not contain javac. For javac, you have to install the OpenJDK Development Environment. You can install java-devel or java-1.6.0-openjdk-devel, which both include javac.

By the way: you can find out which package provides javac with a yum search, e.g.

su -c ‘yum provides javac’
Another note: using yum and openjdk is only one possibility to install the JDK. Many people prefer Sun/Oracle’s “original” SDK. See How to install Java SDK on CentOS? and links for alternatives.

大意就是我们用yum来装原生的就行了

在终端输入

yum install java-devel

执行安装

再测试就行了


补充:Vi编辑常用快捷键

复制:ctrl+insert

粘贴:shift+insert

按Esc保存退出编译,shift+zz退出

mysql 替换某个字段中的某个字符

数据库中的数据 需要批量的替换  , 替换某个词,或特殊符号

sql语句:

update 表名 set 字段名=REPLACE (字段名,'原来的值','要修改的值')

update  zhexuezhongguo as a set a.`正文`=REPLACE (a.`正文`,'摘  要:',' ') ;

update user_item set addr=REPLACE (addr,'成都','天府')

添加条件:
update user_item set addr=REPLACE (addr,'成都','天府') where time<'2013-11--5';

SQL数据库,如何把一张表从一个数据库中插入到另外一个数据库?

两个数据库名分别为:data1 和 data2,对应的表依次分别为:table1和table2。我想把data1里的table1的数据插入到data2里的table2,怎么写SQL语句?

情况一:data2表中无table2表

select * into data2.dbo.table2 in data2 from data1.dbo.table1

情况二:结构不一样或者你要指定字段

insert into data2.table2(字段1,字段2,字段) select 字段j,字段k,字段m fromdata1.table1

情况三:结构一样

insert into data2.table2select * from data1.table1

如何使用代理IP进行数据抓取,PHP爬虫抓取亚马逊商品数据

什么是代理?什么情况下会用到代理IP?
代理服务器(Proxy Server),其功能就是代用户去取得网络信息,然后返回给用户。形象的说:它是网络信息的中转站。通过代理IP访问目标站,可以隐藏用户的真实IP。

比如你要抓取一个网站数据,该网站有100万条内容,他们做了IP限制,每个IP每小时只能抓1000条,如果单个IP去抓因为受限,需要40天左右才能采集完,如果用了代理IP,不停的切换IP,就可以突破每小时1000条的频率限制,从而提高效率。

其他想切换IP或者隐藏身份的场景也会用到代理IP,比如SEO等。

代理IP有开放代理也有私密代理,开放代理是全网扫描而来的,不稳定,不适合爬虫,如果自己随便用用还好。用爬虫抓数据,最好使用私密代理。私密代理网上有很多提供商,稳定性参差不齐,现在我们公司使用的是“百变IP”提供的私密代理。

我们公司有个项目是抓取亚马逊数据来进行分析销量、评论等,用PHP进行抓取,抓取亚马逊要特别注意header头,否则输出的数据就是空了。还有一种方法,可以用PHP通过shell_exec来调用curl命令来进行抓取。

function curl_via_proxy($url) {
    $user_agent = ‘curl’;
    $ch = curl_init($url); //创建CURL对象  
    curl_setopt($ch, CURLOPT_HEADER, 0); //返回头部  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回信息  
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); //连接超时时间
    curl_setopt($ch, CURLOPT_TIMEOUT, 5); //读取超时时间
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //对认证证书来源的检查
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //从证书中检查SSL加密算法是否存在
    curl_setopt($ch, CURLOPT_PROXY, ‘proxy.baibianip.com’); //代理服务器地址
    curl_setopt($ch, CURLOPT_PROXYPORT, ‘8000’); //代理服务器端口
curl_setopt($ch, CURLOPT_ENCODING,’gzip’);
    $headers = array(
‘authority:www.amazon.com’,
‘upgrade-insecure-requests:1’,
‘user-agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36’,
‘accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8′,
‘accept-encoding:gzip, deflate, br’,
‘accept-language:zh-CN,zh;q=0.9,en;q=0.8’,
     );
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    $res = curl_exec($ch);
    $curl_errno = curl_errno($ch);
    if ($curl_errno) {
        curl_close($ch);
        return false;
    }
    curl_close($ch);
    return $res;
}

var_dump(curl_via_proxy(‘https://www.amazon.com/dp/B01H2S9F6C’));

php 使用代理IP进行数据抓取

什么是代理?什么情况下会用到代理IP?
代理服务器(Proxy Server),其功能就是代用户去取得网络信息,然后返回给用户。形象的说:它是网络信息的中转站。通过代理IP访问目标站,可以隐藏用户的真实IP。

比如你要抓取一个网站数据,该网站有100万条内容,他们做了IP限制,每个IP每小时只能抓1000条,如果单个IP去抓因为受限,需要40天左右才能采集完,如果用了代理IP,不停的切换IP,就可以突破每小时1000条的频率限制,从而提高效率。

其他想切换IP或者隐藏身份的场景也会用到代理IP,比如SEO等。

代理IP有开放代理也有私密代理,开放代理是全网扫描而来的,不稳定,不适合爬虫,如果自己随便用用还好。用爬虫抓数据,最好使用私密代理。私密代理网上有很多提供商,稳定性参差不齐,现在我们公司使用的是“百变IP”提供的私密代理。

我们公司有个项目是抓取亚马逊数据来进行分析销量、评论等,用PHP进行抓取,抓取亚马逊要特别注意header头,否则输出的数据就是空了。还有一种方法,可以用PHP通过shell_exec来调用curl命令来进行抓取。

复制代码
    PHP如果是使用curl函数来抓取,主要设置下面几项即可。

    curl_setopt($ch, CURLOPT_PROXY, 'proxy.baibianip.com'); //代理服务器地址
    curl_setopt($ch, CURLOPT_PROXYPORT, '8000'); //代理服务器端口

    如果是抓取HTTPS,把下面两项设置为false:
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //抓HTTPS可以把此项设置为false
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //抓HTTPS可以把此项设置为false
复制代码
方法一:完整示例代码如下,下面提供两种方式来调用:
复制代码
<?php

//代理ip列表
const PROXY_LIST = [
    '124.232.133.199:3128',
    '171.83.165.90:9999',
    
];

function curl_via_proxy($url,$proxy_ip,$headers = [],$user_agent = 'curl',$method = 'GET')
{
    $arr_ip = explode(':',$proxy_ip);

    $ch = curl_init($url); //创建CURL对象  
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
    curl_setopt($ch, CURLOPT_HEADER, 0); //返回头部  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回信息  
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); //连接超时时间
    curl_setopt($ch, CURLOPT_TIMEOUT, 5); //读取超时时间
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //对认证证书来源的检查
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //从证书中检查SSL加密算法是否存在
    curl_setopt($ch, CURLOPT_PROXY, $arr_ip[0]); //代理服务器地址
    curl_setopt($ch, CURLOPT_PROXYPORT, $arr_ip[1]); //代理服务器端口
    curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
    curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
    //添加头部信息
    if(!empty($headers)){
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }


    $res = curl_exec($ch);
    $curl_errno = curl_errno($ch);
    if ($curl_errno) {
        curl_close($ch);
        return false;
    }
    curl_close($ch);
    return $res;
}
$headers = array(
    'authority:www.amazon.com',
    'upgrade-insecure-requests:1',
    'user-agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36',
    'accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'accept-encoding:gzip, deflate, br',
    'accept-language:zh-CN,zh;q=0.9,en;q=0.8',
);
$url = 'https://api.apiopen.top/recommendPoetry';
var_dump(curl_via_proxy($url,PROXY_LIST[array_rand(PROXY_LIST,1)],$headers));
die;
复制代码

 方式二:利用PHP调用Linux的curl命令来进行抓取,Windows下下载curl.exe即可。

$html = shell_exec("curl -x 'proxy.baibianip.com:8000' 'http://www.baidu.com' --connect-timeout 3 -m 5");
echo $html;

Nginx反向代理导致PHP获取不到正确的HTTP_HOST,SERVER_NAME,客户端IP的解决方法

今天第一次配Nginx负载均衡,发现PHP无法获取HTTP_HOST

贴上的Nginx配置

复制代码
upstream abc.com {
        server 10.141.8.55:8005;
        server 10.141.8.55:8006;
}
server {
        listen 80;
        server_name www.xxx.com;
        log_not_found off;
        access_log  /var/log/nginx/html-access.log  main;
        charset utf-8;
        location / {
                proxy_pass http://abc.com;
        }
}
复制代码

echo $_SERVER[‘HTTP_HOST’];

输出abc.com

仔细查看Nginx配置,很容易发现PHP获取到的HTTP_HOST是Nginx代理过来的。

解决的办法有两种

方法一,将abc.com改成你www.xxx.com

不过这种方法还不是很好,如果server_name有多个怎么办呢

所以

方法二

在location/{}加上 proxy_set_header    Host             $host;

既然HTTP_HOST有问题,那其他客户端的信息也肯定是不对的。比如客户端IP。

所以还得加上其他配置,完整的Nginx配置如下

复制代码
upstream abc.com {
        server 10.141.8.55:8005;
        server 10.141.8.55:8006;
}
server {
        listen 80;
        server_name www.xxx.com;
        log_not_found off;
        access_log  /var/log/nginx/html-access.log  main;
        charset utf-8;
        location / {
                proxy_pass http://abc.com;
                proxy_set_header    Host             $host;
                proxy_set_header    X-Real-IP        $remote_addr;
                proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
                proxy_set_header    HTTP_X_FORWARDED_FOR $remote_addr;
                proxy_redirect      default;
        }
}
复制代码

elasticsearch学习之路—Linux 下安装并启动elasticsearch

1.进入elastic官网下载elasticsearch 点击打开链接,,选择tar,右键复制链接

2.进入命令行输入: wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.3.tar.gz 下载到当前目录

3.输入命令:tar -vxf elasticsearch-6.2.3.tar.gz 解压,此时会生成 elasticsearch-6.2.3 文件

4.输入:vi elasticsearch-6.2.3/config/jvm.options 修改Xms1g和Xmx1g这两个值,我修改的为 Xms512m和Xmx512m,若果 你内存够大,可以不用修改

5.然后进入bin文件夹输入:./elasticsearch 启动

如果出现上述错误则是说明:不能用root用户启动,这时我们需要换一个普通用户

6.创建一个用户: 输入:useradd elastic

7.输入 su elastic 登录elastic用户

8.进入elasticsearch-6.2.3 bin 目录,输入 ./elasticsearch 启动elastic

如若出现上述错误,则是elastic这个用户权限不足,这时我们可以输入 ls -l查看,会发现elasticsearch的权限为 root root,所以此时我们需要为elastic这个用户添加权限

9.切换为root用户回到elasticsearch-6.2.3文件夹父级目录执行命令: chown elastic elasticsearch-6.2.3 -R

10.切换为elastic用户进入 elasticsearch-6.2.3 bin目录 输入:./elasticsearch 启动elasticsearch

如图所示:此时我们可以看出elasticSearch已经启动成功,我们可以出浏览器输入 127.0.0.1:9200进行一个验证

如图:启动成功

./elasticsearch -Ecluster.name = my_cluster_name -Enode.name = my_node_name

  1. 停掉服务:按 Ctrl+c ;然后刷新浏览器已经不在显示,说明服务已停止

12.后台启动elasticsearch ,输入:./elasticsearch -d 启动elasticsearch; 稍等一会,可以再去浏览器进行验证,也可以输入

    ps aux|grep elasticsearch 命令进行查询

启动成功!

简单粗暴,若有讲错的地方,欢迎大家提出意见,一起探讨!

Linux安装jdk详细步骤

Linux的使用相信大家都要用到java吧!在使用java前我们得先安装jdk以及配置环境变量等工作;下面小编给大家分享关于Linux安装jdk的详细步骤:

一、登录虚拟机进入终端切换到root用户,输入:su 接着输入密码 再输入:cd … 回到root用户

二、查看Linux系统是否有自带的jdk:
1、输入:java -version
2、发现有输入:rpm -qa | grep java 检测jdk的安装包,(注意:rpm命令符没有时记得下载一个输入:apt-get install +命令名称)
3、接着进行一个个删除包,输入:rpm -e –nodeps +包名
4、最后再次:rpm -qa | grep java检查是否删除完即可(此步由于之前已经卸载,所以没有截图)
三、接着在终端进入对应文件新建一个装jdk包的文件夹,输入:mkdir +目录名称

四、接下来要修改文件夹的权限,输入:chmod 777 +要删除的文件名

五、上官网下载对应版本的安装包:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

六、下载完后用FileZilla文件传输器,把jdk安装包传到虚拟机对应的文件夹当中;或者直接在Linux下载即可

七、在终端进入对应的文件的目录,进一步解压,输入tar -zxvf +需要解压的包名,下图为解压中

八、编辑配置文件,配置环境变量,分别有两种方法:
方法一:打开文件进行修改,输入:mousepad /etc/profile 后直接在文本上修改建议使用这种方法

方法二:用vim /etc/profile进入编辑状态
vim文本编辑器 profile全局变量文件
Esc Shift+q 进入键盘编辑
q! 不保存退出
qw! 写入并强制退出,如果不管用键盘x

九、重新加载配置文件,输入:source /etc/profile

十、最后一步,查看安装情况
1、输入:java -version

2、输入:javac

3、输入:java

到此Linux安装jdk就完成啦!

Elasticsearch集群部署

Elastic 的底层是开源库 Lucene。但是,你没法直接用 Lucene,必须自己写代码去调用它的接口。Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。Elastic 的底层是开源库 。但是,你没法直接用 Lucene,必须自己写代码去调用它的接口。Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。

一、ES中的基本概念

cluster

代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。

shards

代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。

replicas

代表索引副本,es可以设置多个索引的副本,副本的作用一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。

recovery

代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。

river

代表es的一个数据源,也是其它存储方式(如:数据库)同步数据到es的一个方法。它是以插件方式存在的一个es服务,通过读取river中的数据并把它索引到es中,官方的river有couchDB的,RabbitMQ的,Twitter的,Wikipedia的。

gateway

代表es索引快照的存储方式,es默认是先把索引存放到内存中,当内存满了时再持久化到本地硬盘。gateway对索引快照进行存储,当这个es集群关闭再重新启动时就会从gateway中读取索引备份数据。es支持多种类型的gateway,有本地文件系统(默认),分布式文件系统,Hadoop的HDFS和amazon的s3云存储服务。

discovery.zen

代表es的自动发现节点机制,es是一个基于p2p的系统,它先通过广播寻找存在的节点,再通过多播协议来进行节点之间的通信,同时也支持点对点的交互。

Transport

代表es内部节点或集群与客户端的交互方式,默认内部是使用tcp协议进行交互,同时它支持http协议(json格式)、thrift、servlet、memcached、zeroMQ等的传输协议(通过插件方式集成)。


二、部署环境

采用三台CentOS7.3部署Elasticsearch集群,部署Elasticsearch集群就不得不提索引分片,以下是索引分片的简单介绍。

系统节点名IP
CentOS7.3els1172.18.68.11
CentOS7.3els2172.18.68.12
CentOS7.3els3172.18.68.13

ES集群中索引可能由多个分片构成,并且每个分片可以拥有多个副本。通过将一个单独的索引分为多个分片,我们可以处理不能在一个单一的服务器上面运行的大型索引,简单的说就是索引的大小过大,导致效率问题。不能运行的原因可能是内存也可能是存储。由于每个分片可以有多个副本,通过将副本分配到多个服务器,可以提高查询的负载能力。

三、部署Elasticsearch集群

1.安装JDK

Elasticsearch是基于Java开发是一个Java程序,运行在Jvm中,所以第一步要安装JDK

yum install -y java-1.8.0-openjdk-devel

2.下载elasticsearch

https://artifacts.elastic.co/downloads/elasticsearch/ 是ELasticsearch的官方站点,如果需要下载最新的版本,进入官网下载即可。可以下载到本地电脑然后再导入CentOS中,也可以直接在CentOS中下载。

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.0.1.rpm

3.配置目录

安装完毕后会生成很多文件,包括配置文件日志文件等等,下面几个是最主要的配置文件路径

/etc/elasticsearch/elasticsearch.yml                            # els的配置文件
/etc/elasticsearch/jvm.options                                  # JVM相关的配置,内存大小等等
/etc/elasticsearch/log4j2.properties                            # 日志系统定义
/var/lib/elasticsearch                                          # 数据的默认存放位置

4.创建用于存放数据与日志的目录

数据文件会随着系统的运行飞速增长,所以默认的日志文件与数据文件的路径不能满足我们的需求,那么手动创建日志与数据文件路径,可以使用NFS、可以使用Raid等等方便以后的管理与扩展

mkdir /els/{log,date}
chown -R elasticsearch.elasticsearch /els/*

5.集群配置

集群配置中最重要的两项是node.namenetwork.host,每个节点都必须不通。其中node.name是节点名称主要是在Elasticsearch自己的日志加以区分每一个节点信息。
discovery.zen.ping.unicast.hosts是集群中的节点信息,可以使用IP地址、可以使用主机名(必须可以解析)。

vim /etc/elasticsearch
cluster.name: aubin-cluster                                 # 集群名称
node.name: els1                                             # 节点名称,仅仅是描述名称,用于在日志中区分

path.data: /var/lib/elasticsearch                           # 数据的默认存放路径
path.logs: /var/log/elasticsearch                           # 日志的默认存放路径

network.host: 192.168.0.1                                   # 当前节点的IP地址
http.port: 9200                                             # 对外提供服务的端口,9300为集群服务的端口

discovery.zen.ping.unicast.hosts: ["172.18.68.11", "172.18.68.12","172.18.68.13"]       
# 集群个节点IP地址,也可以使用els、els.shuaiguoxia.com等名称,需要各节点能够解析

discovery.zen.minimum_master_nodes: 2                       # 为了避免脑裂,集群节点数最少为 半数+1

6.JVM配置

由于Elasticsearch是Java开发的,所以可以通过/etc/elasticsearch/jvm.options配置文件来设定JVM的相关设定。如果没有特殊需求按默认即可。
不过其中还是有两项最重要的-Xmx1g-Xms1gJVM的最大最小内存。如果太小会导致Elasticsearch刚刚启动就立刻停止。太大会拖慢系统本身

vim /etc/elasticsearch/jvm.options
-Xms1g                                                  # JVM最大、最小使用内存
-Xmx1g

7.启动Elasticsearch

由于启动Elasticsearch会自动启动daemon-reload所以最后一项可以省略。

systemctl enable elasticsearch.service
systemctl start elasticsearch
systemctl daemon-reload                                 # 可以省略

8.测试

Elasticsearch直接听过了http接口,所以直接使用curl命令就可以查看到一些集群相关的信息。

可以使用curl命令来获取集群的相关的信息,

  • _cat代表查看信息
  • nodes为查看节点信息,默认会显示为一行,所以就用刀了?preety让信息更有好的显示
  • ?preety让输出信息更友好的显示
curl -XGET 'http://172.18.68.11:9200/_cat/nodes?pretty'
172.18.68.12 18 68 0 0.07 0.06 0.05 mdi - els2
172.18.68.13 25 67 0 0.01 0.02 0.05 mdi * els3             #  *号表示为当前节点为主节点的意思
172.18.68.11  7 95 0 0.02 0.04 0.05 mdi - els1

如果你要想查看更多有关于集群信息、当前节点统计信息等等,可以使用一下命令来获取到所有可以查看的信息。

curl -XGET 'http://172.18.68.11:9200/_cat?pretty'   

吃透Elasticsearch 堆内存

1、什么是堆内存?
Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。
在 Java 中,堆被划分成两个不同的区域:

  • 新生代 ( Young )、
  • 老年代 ( Old )。

新生代 ( Young ) 又被划分为三个区域

  • Eden、
  • From Survivor、
  • To Survivor。

这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。

2、堆内存的作用是什么?
在虚拟机启动时创建。

堆内存的唯一目的就是创建对象实例,所有的对象实例和数组都要在堆上分配。

堆是由垃圾回收来负责的,因此也叫做“GC堆”,垃圾回收采用分代算法,堆由此分为新生代和老年代。

堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。

但缺点是,由于要在运行时动态分配内存,存取速度较慢。当堆内存因为满了无法扩展时就会抛出java.lang.OutOfMemoryError:Java heap space异常。出现这种情况的解决办法具体参见java调优。

3、堆内存如何配置?
默认情况下,Elasticsearch JVM使用堆内存最小和最大大小为2 GB(5.X版本以上)。

早期版本默认1GB,官网指出:这明显不够。

在转移到生产环境时,配置足够容量的堆大小以确保Elasticsearch功能和性能是必要的。

Elasticsearch将通过Xms(最小堆大小)和Xmx(最大堆大小)设置来分配jvm.options中指定的整个堆。

举例如下:

设置方式一:
在jvm.option配置文件中设置堆内存。

-Xms2g
-Xmx2g
1
2
设置方式二:
通过环境变量设置。
这可以通过注释掉jvm.options文件中的Xms和Xmx设置并通过ES_JAVA_OPTS设置这些值来完成:

ES_JAVA_OPTS=”-Xms2g -Xmx2g” ./bin/elasticsearch
ES_JAVA_OPTS=”-Xms4000m -Xmx4000m” ./bin/elasticsearch
1
2
4、堆内存的决定因素
堆内存的值取决于服务器上可用的内存大小。

5、堆内存配置建议
将最小堆大小(Xms)和最大堆大小(Xmx)设置为彼此相等。

Elasticsearch可用的堆越多,可用于缓存的内存就越多。但请注意,太多的堆内存可能会使您长时间垃圾收集暂停。

将Xmx设置为不超过物理内存的50%,以确保有足够的物理内存留给内核文件系统缓存。

  • 不要将Xmx设置为JVM超过32GB。

大小建议:
宿主机内存大小的一半和31GB,取最小值。
1
2
6、堆内存为什么不能超过物理机内存的一半?
堆对于Elasticsearch绝对重要。
它被许多内存数据结构用来提供快速操作。但还有另外一个非常重要的内存使用者:Lucene。

Lucene旨在利用底层操作系统来缓存内存中的数据结构。 Lucene段(segment)存储在单个文件中。因为段是一成不变的,所以这些文件永远不会改变。这使得它们非常容易缓存,并且底层操作系统将愉快地将热段(hot segments)保留在内存中以便更快地访问。这些段包括倒排索引(用于全文搜索)和文档值(用于聚合)。

Lucene的性能依赖于与操作系统的这种交互。但是如果你把所有可用的内存都给了Elasticsearch的堆,那么Lucene就不会有任何剩余的内存。这会严重影响性能。

标准建议是将可用内存的50%提供给Elasticsearch堆,而将其他50%空闲。它不会被闲置; Lucene会高兴地吞噬掉剩下的东西。

如果您不字符串字段上做聚合操作(例如,您不需要fielddata),则可以考虑进一步降低堆。堆越小,您可以从Elasticsearch(更快的GC)和Lucene(更多内存缓存)中获得更好的性能。

7、堆内存为什么不能超过32GB?
在Java中,所有对象都分配在堆上并由指针引用。普通的对象指针(OOP)指向这些对象,传统上它们是CPU本地字的大小:32位或64位,取决于处理器。

对于32位系统,这意味着最大堆大小为4 GB。对于64位系统,堆大小可能会变得更大,但是64位指针的开销意味着仅仅因为指针较大而存在更多的浪费空间。并且比浪费的空间更糟糕,当在主存储器和各种缓存(LLC,L1等等)之间移动值时,较大的指针消耗更多的带宽。

Java使用称为压缩oops的技巧来解决这个问题。而不是指向内存中的确切字节位置,指针引用对象偏移量。这意味着一个32位指针可以引用40亿个对象,而不是40亿个字节。最终,这意味着堆可以增长到约32 GB的物理尺寸,同时仍然使用32位指针。

一旦你穿越了这个神奇的〜32 GB的边界,指针就会切换回普通的对象指针。每个指针的大小增加,使用更多的CPU内存带宽,并且实际上会丢失内存。实际上,在使用压缩oops获得32 GB以下堆的相同有效内存之前,需要大约40-50 GB的分配堆。

以上小结为:即使你有足够的内存空间,尽量避免跨越32GB的堆边界。
否则会导致浪费了内存,降低了CPU的性能,并使GC在大堆中挣扎。

8、我是内存土豪怎么办?
假设,我有一台带有1TB RAM的机器!
1
32 GB的基线相当重要。那么当你的机器有很多内存时你怎么做?当前具有512-768 GB RAM的超级服务器变得越来越普遍。

首先,我们建议避免使用这种大型机器。

但是如果你已经有了这些机器,你有三种实用的选择:

  1. 你是否主要进行全文搜索?

考虑给Elasticsearch提供4-32 GB,并让Lucene通过操作系统文件系统缓存使用剩余的内存。所有内存都会缓存段,并导致快速全文搜索。

  1. 你在做很多排序/聚合?

大部分聚合数字,日期,地理位置和not_analyzed字符串?你很幸运,你的聚合将在内存缓存的文档值上完成!

从4-32 GB的内存中给Elasticsearch一个地方,剩下的让操作系统在内存中缓存doc值。

  1. 你是否对分析过的字符串进行了很多排序/聚合(例如对于字标记或SigTerms等)?

不幸的是,这意味着你需要fielddata,这意味着你需要堆空间。

考虑在一台机器上运行两个或多个节点,而不是一个节点数量巨大的RAM。

尽管如此,仍然坚持50%的规则。

To土豪内存小结:

因此,如果您的机器具有128 GB的RAM,请运行两个节点,每个节点的容量低于32 GB。这意味着小于64 GB将用于堆,而Lucene将剩余64 GB以上。

如果您选择此选项,请在您的配置中设置cluster.routing.allocation.same_shard.host:true。这将阻止主副本分片共享同一台物理机(因为这会消除副本高可用性的好处)。

9、堆内存优化建议
方式一:最好的办法是在系统上完全禁用交。
这可以暂时完成:

sudo swapoff -a
1
要永久禁用它,你可能需要编辑你的/ etc / fstab。

方式二:控制操作系统尝试交换内存的积极性。

如果完全禁用交换不是一种选择,您可以尝试降低swappiness。该值控制操作系统尝试交换内存的积极性。这可以防止在正常情况下交换,但仍然允许操作系统在紧急内存情况下进行交换。

对于大多数Linux系统,这是使用sysctl值配置的:

vm.swappiness = 1
1
1的swappiness优于0,因为在某些内核版本上,swappiness为0可以调用OOM杀手。

方式三:mlockall允许JVM锁定其内存并防止其被操作系统交换。

最后,如果两种方法都不可行,则应启用mlockall。文件。这允许JVM锁定其内存并防止其被操作系统交换。在你的elasticsearch.yml中,设置这个:

bootstrap.mlockall:true
1
10、注意
修改JVM相关配置很容易,但容易产生难以测量的不透明效果,并最终将您的群集解调为缓慢,不稳定的混乱
在调试群集时,第一步通常是删除所有自定义配置。大约一半的时间,仅靠这一点就恢复了稳定性和性能。
11、最新认知
wood@Ctrip
事实上,给ES分配的内存有一个魔法上限值26GB,

这样可以确保启用zero based Compressed Oops,这样性能才是最佳的。

参考:https://elasticsearch.cn/question/3995
https://www.elastic.co/blog/a-heap-of-trouble

12、小结
这是一篇官网文档&原理的整合文章,主要目的是梳理认知。

13、参考
基础:http://t.cn/RH4DDYu
设置:http://t.cn/RmKbO1i
建议:http://t.cn/RmKbjsF
注意:http://t.cn/RmKbHp5
堆:http://t.cn/RmKbRji