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

es设置堆大小

默认情况下,Elasticsearch告诉JVM使用最小和最大大小为1 GB的堆。迁移到生产环境时,配置堆大小以确保Elasticsearch有足够的可用堆是很重要的。

Elasticsearch将通过(最小堆大小)和(最大堆大小)设置分配jvm.options中指定的整个堆 。您应该将这两个设置设置为彼此相等。XmsXmx

这些设置的值取决于服务器上可用的RAM量:

  • 设置Xmx并且Xms不超过物理RAM的50%。Elasticsearch需要内存用于JVM堆以外的其他目的,为此留出空间很重要。例如,Elasticsearch使用堆外缓冲区进行有效的网络通信,依赖操作系统的文件系统缓存来有效访问文件,而JVM本身也需要一些内存。使用比使用该Xmx设置配置的限制更多的内存来观察Elasticsearch进程是正常的。
  • 设置Xmx并且Xms不超过JVM用于压缩对象指针的阈值(压缩oops); 确切的阈值变化但接近32 GB。您可以通过在日志中查找如下所示的行来验证您是否低于阈值:堆大小[1.9gb],压缩普通对象指针[true]
  • 理想情况下设置Xmx并且Xms不超过基于零的压缩oops的阈值; 确切的阈值会有所不同,但大多数系统上26 GB是安全的,但在某些系统上可能高达30 GB。您可以通过使用JVM选项启动Elasticsearch -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode并查找如下所示的行来验证您是否低于此阈值 :堆地址:0x000000011be00000,大小:27648 MB,基于零的压缩哎呀显示已启用从零开始的压缩oops。如果未启用从零开始的压缩oops,则会看到如下所示的行:堆地址:0x0000000118400000,大小:28672 MB,带基数的压缩哎呀:0x00000001183ff000

Elasticsearch可用的堆越多,它可用于其内部缓存的内存就越多,但它为操作系统用于文件系统缓存的内存就越少。此外,较大的堆可能导致更长的垃圾收集暂停。

以下是如何通过jvm.options文件设置堆大小的示例:

- Xms2g 
- Xmx2g 
将最小堆大小设置为2g。
将最大堆大小设置为2g。

也可以通过环境变量设置堆大小。这可以通过注释掉来完成Xms,并Xmx设置在 jvm.options文件中,并通过设置这些值ES_JAVA_OPTS

ES_JAVA_OPTS = “-Xms2g -Xmx2g” ./ 箱/ elasticsearch 
ES_JAVA_OPTS = “-Xms4000m -Xmx4000m” ./ 箱/ elasticsearch 
将最小和最大堆大小设置为2 GB。
将最小和最大堆大小设置为4000 MB。
注意

Windows服务配置堆不同于上面的堆。最初为Windows服务填充的值可以如上配置,但在安装服务后不同。有关其他详细信息,请参阅Windows服务文档

Bulk 异常引发的 Elasticsearch 内存泄漏

运维线上ES集群时,偶然遇到内存泄露的问题,排查问题时看到了这篇文章,清晰明了,所以分享给大家,希望给大家问题排查提供一些思路。

本文转载自简书,如遇版权问题,请及时联系小编。

PS:丰富的一线技术、多元化的表现形式,尽在“ HULK一线技术杂谈 ”,点关注哦!

背景介绍

前天公司度假部门一个线上ElasticSearch集群发出报警,有Data Node的Heap使用量持续超过80%警戒线。 收到报警邮件后,不敢怠慢,立即登陆监控系统查看集群状态。还好,所有的节点都在正常服务,只是有2个节点的Heap使用率非常高。此时,Old GC一直在持续的触发,却无法回收内存。

Bulk 异常引发的 Elasticsearch 内存泄漏

问题排查

问题节点的Heapsize分配了30GB,80%的使用率约等于24GB。 但集群的数据总量并不大,5个节点所有索引文件加起来占用的磁盘空间还不到10GB。

Bulk 异常引发的 Elasticsearch 内存泄漏

查看各节点的segment memory和cache占用量也都非常小,是MB级别的。

Bulk 异常引发的 Elasticsearch 内存泄漏

集群的QPS只有30上下,CPU消耗10%都不到,各类thread pool的活动线程数量也都非常低。

Bulk 异常引发的 Elasticsearch 内存泄漏

非常费解是什么东西占着20多GB的内存不释放?

出现问题的集群ES版本是5.3.2,而这个版本的稳定性在公司内部已经经过长时间的考验,做为稳定版本在线上进行了大规模部署。 其他一些读写负载非常高的集群也未曾出现过类似的状况,看来是遇到新问题了。

查看问题节点ES的日志,除了看到一些Bulk异常以外,未见特别明显的其他和资源相关的错误:

[2017-11-06T16:33:15,668][DEBUG][o.e.a.b.TransportShardBulkAction] [] [suggest-3][0] failed to execute bulk item (update) BulkShardRequest [[suggest-3][0]] containing [44204 ] requests org.elasticsearch.index.engine.DocumentMissingException: [ type ][纳格尔果德_1198]: document missing at org.elasticsearch.action.update.UpdateHelper.prepare(UpdateHelper.java:92) ~[elasticsearch-5.3.2.jar:5.3.2] at org.elasticsearch.action.update.UpdateHelper.prepare(UpdateHelper.java:81) ~[elasticsearch-5.3.2.jar:5.3.2]

和用户确认这些异常的原因,是因为写入程序会从数据源拿到数据后,根据doc_id对ES里的数据做update。会有部分doc_id在ES里不存在的情况,但并不影响业务逻辑,因而ES记录的document missing异常应该可以忽略。

至此别无他法,只能对JVM做Dump分析了。

Heap Dump分析

用的工具是Eclipse MAT,从这里下载的Mac版:Downloads 。 使用这个工具需要经过以下2个步骤:

  1. 获取二进制的head dump文件 jmap -dump:format=b,file=/tmp/es_heap.bin <pid> 其中pid是ES JAVA进程的进程号。
  2. 将生成的dump文件下载到本地开发机器,启动MAT,从其GUI打开文件。

要注意,MAT本身也是JAVA应用,需要有JDK运行环境的支持。

MAT第一次打dump文件的时候,需要对其解析,生成多个索引。这个过程比较消耗CPU和内存,但一旦完成,之后再打开dump文件就很快,消耗很低。 对于这种20多GB的大文件,第一次解析的过程会非常缓慢,并且很可能因为开发机内存的较少而内存溢出。因此,我找了台大内存的服务器来做第一次的解析工作:

1.将linux版的MAT拷贝上去,解压缩后,修改配置文件MemoryAnalyzer.ini,将内存设置为20GB左右:

Bulk 异常引发的 Elasticsearch 内存泄漏

这样能保证解析的过程中不会内存溢出。

2.将dump文件拷贝上去,执行下面几个命令生成索引及3个分析报告:

mat/ParseHeapDump.sh es_heap.bin org.eclipse.mat.api:suspects

mat/ParseHeapDump.sh es_heap.bin org.eclipse.mat.api:overview

mat/ParseHeapDump.sh es_heap.bin org.eclipse.mat.api:top_components

分析成功以后,会生成如下一堆索引文件(.index)和分析报告(.zip)

Bulk 异常引发的 Elasticsearch 内存泄漏

将这些文件打包下载到本地机器上,用MAT GUI打开就可以分析了。

在MAT里打开dump文件的时候,可以选择打开已经生成好的报告,比如Leak suspects:

Bulk 异常引发的 Elasticsearch 内存泄漏

通过Leak Suspects,一眼看到这20多GB内存主要是被一堆bulk线程实例占用了,每个实例则占用了接近1.5GB的内存。

Bulk 异常引发的 Elasticsearch 内存泄漏

进入”dominator_tree”面板,按照”Retained Heap”排序,可以看到多个bulk线程的内存占用都非常高。

Bulk 异常引发的 Elasticsearch 内存泄漏

将其中一个thread的引用链条展开,看看这些线程是如何Retain这么多内存的,特别注意红圈部分:

Bulk 异常引发的 Elasticsearch 内存泄漏

这个引用关系解读如下:

  1. 这个bulk线程的thread local map里保存了一个log4j的MultableLogEvent对象。
  2. MutablelogEvent对象引用了log4j的ParameterizedMessage对象。
  3. ParameterizedMessage引用了bulkShardRequest对象。
  4. bulkShardRequest引用了4万多个BulkitemRequest对象。

这样看下来,似乎是log4j的logevent对一个大的bulk请求对象有强引用而导致其无法被垃圾回收掉,产生内存泄漏。

联想到ES日志里,有记录一些 document missing 的bulk异常,猜测是否在记录这些异常的时候产生的泄漏。

问题复现

为了验证猜测,我在本地开发机上,启动了一个单节点的5.3.2测试集群,用bulk api做批量的update,并且有意为其中1个update请求设置不存在的doc_id。

为了便于测试,我在ES的配置文件elasticsearch.yml里添加了配置项processors: 1。 这个配置项影响集群thread_pool的配置,bulk thread pool的大小将减少为1个,这样可以更快速和便捷的做各类验证。

启动集群,发送完bulk请求后,立即做一个dump,重复之前的分析过程,问题得到了复现。

这时候想,是否其他bulk异常也会引起同样的问题,比如写入的数据和mapping不匹配? 测试了一下,问题果然还是会产生。再用不同的bulk size进行测试,发现无法回收的这段内存大小,取决于最后一次抛过异常的bulk size大小。至此,基本可以确定内存泄漏与log4j记录异常消息的逻辑有关系。

为了搞清楚这个问题是否5.3.2独有,后续版本是否有修复,在最新的5.6.3上做了同样的测试,问题依旧,因此这应该是一个还未发现的深层Bug.

源码查根源

大致搞清楚问题查找的方向了,但根源还未找到,也就不知道如何修复和避免,只有去扒源码了。

在TransportShardBulkAction 第209行,找到了ES日志里抛异常的代码片段。

if (isConflictException(failure)) {

logger.trace((Supplier<?>) () -> new ParameterizedMessage(“{} failed to execute bulk item ({}) {}”,

request.shardId(), docWriteRequest.opType().getLowercase(), request), failure);

} else {

logger.debug((Supplier<?>) () -> new ParameterizedMessage(“{} failed to execute bulk item ({}) {}”,

request.shardId(), docWriteRequest.opType().getLowercase(), request), failure);

}

这里看到了ParameterizedMessage实例化过程中,request做为一个参数传入了。这里的request是一个BulkShardRequest对象,保存的是要写入到一个shard的一批bulk item request。 这样以来,一个批次写入的请求数量越多,这个对象retain的内存就越多。 可问题是,为什么logger.debug()调用完毕以后,这个引用不会被释放?

通过和之前MAT上的dominator tree仔细对比,可以看到ParameterizedMessage之所以无法释放,是因为被一个MutableLogEvent在引用,而这个MutableLogEvent被做为一个thread local存放起来了。 由于ES的Bulk thread pool是fix size的,也就是预先创建好,不会销毁和再创建。 那么这些MutableLogEvent对象由于是thread local的,只要线程没有销毁,就会对该线程实例一直全局存在,并且其还会一直引用最后一次处理过的ParameterizedMessage。 所以在ES记录bulk exception这种比较大的请求情况下, 整个request对象会被thread local变量一直强引用无法释放,产生大量的内存泄漏。

再继续挖一下log4j的源码,发现MutableLogEvent是在org.apache.logging.log4j.core.impl.ReusableLogEventFactory里做为thread local创建的。

public class ReusableLogEventFactory implements LogEventFactory { private static finalThreadNameCachingStrategy THREAD_NAME_CACHING_STRATEGY = ThreadNameCachingStrategy.create(); private static final Clock CLOCK = ClockFactory.getClock(); private static ThreadLocal<MutableLogEvent> mutableLogEventThreadLocal = new ThreadLocal<>();

而org.apache.logging.log4j.core.config.LoggerConfig则根据一个常数ENABLE_THREADLOCALS的值来决定用哪个LogEventFactory。

if (LOG_EVENT_FACTORY == null) {

LOG_EVENT_FACTORY = Constants.ENABLE_THREADLOCALS

? new ReusableLogEventFactory()

: new DefaultLogEventFactory();}

继续深挖,在org.apache.logging.log4j.util.Constants里看到,log4j会根据运行环境判断是否是WEB应用,如果不是,就从系统参数log4j2.enable.threadlocals读取这个常量,如果没有设置,则默认值是true。

public static final boolean ENABLE_THREADLOCALS = !IS_WEB_APP && PropertiesUtil.getProperties().getBooleanProperty( “log4j2.enable.threadlocals”, true);

由于ES不是一个web应用,导致log4j选择使用了ReusableLogEventFactory,因而使用了thread_local来创建MutableLogEvent对象,最终在ES记录bulk exception这个特殊场景下产生非常显著的内存泄漏。

再问一个问题,为何log4j要将logevent做为thread local创建? 跑到log4j的官网去扒了一下文档,在这里 Garbage-free Steady State Logging 找到了合理的解释。 原来为了减少记录日志过程中的反复创建的对象数量,减轻GC压力从而提高性能,log4j有很多地方使用了thread_local来重用变量。 但使用thread local字段装载非JDK类,可能会产生内存泄漏问题,特别是对于web应用。 因此才会在启动的时候判断运行环境,对于web应用会禁用thread local类型的变量。

ThreadLocal fields holding non-JDK classes can cause memory leaks in web applications when the application server’s thread pool continues to reference these fields after the web application is undeployed. To avoid causing memory leaks, Log4j will not use these ThreadLocals when it detects that it is used in a web application (when the javax.servlet.Servlet class is in the classpath, or when system property log4j2.is.webapp is set to “true”).

参考上面的文档后,也为ES找到了规避这个问题的措施: 在ES的JVM配置文件jvm.options里,添加一个log4j的系统变量- Dlog4j2.enable.threadlocals=false ,禁用掉thread local即可。 经过测试,该选项可以有效避开这个内存泄漏问题。

这个问题Github上也提交了Issue,对应的链接是: Memory leak upon partial TransportShardBulkAction failure

https://github.com/elastic/elasticsearch/issues/27300

ES的确是非常复杂的一个系统,包含非常多的模块和第三方组件,可以支持很多想象不到的用例场景,但一些边缘场景可能会引发一些难以排查的问题。完备的监控体系和一个经验丰富的支撑团队对于提升业务开发人员使用ES开发的效率、提升业务的稳定性是非常重要的!