ElasticSearch 使用 – 传递数据规避重复数据

我们一般把mysql的数据,传递到ElasticSearch中,然后进行即使查询,但是ES有一个问题就是,刚刚插入的数据,在1秒内是查询不到的(大致一秒内,粗略的说法),也就是说在很短的时间内,插入一条数据,然后去查询这条数据是查询不到的,因此,我们必须用ES的_id来规避重复数据。

ES中一般除了_id, 再加一个id的字段,用来存储mysql中的id,当然es中的_id也要用mysql中的id来赋值,来规避重复数据。

#通过_id获取数据

$EsTraceDataOne = EsTraceData::findOne($one['_id']);
            if(!$EsTraceDataOne){
                $EsTraceDataOne = new EsTraceData;

# 给Es的表的_id设置值
                $EsTraceDataOne->setPrimaryKey($one['_id']);
            }

# 设置Id
            $EsTraceDataOne->id = $one['_id'];
            $attributes = $EsTraceDataOne->attributes();
            foreach($one as $k=>$v){
                if(in_array($k,$attributes)){

# 只取attributes()方法返回的数组中存在的数据
                    $EsTraceDataOne[$k] = $v;
                }
            }

# 保存
            $EsTraceDataOne->save();

 

通过上面的方法,基本可以规避数据多次传递重复插入数据的问题。

 

附:

<?php

namespace appadmin\code\Ta\models\elasticSearch;

use yii\elasticsearch\ActiveRecord;

class TraceData extends ActiveRecord
{
  public static $currentIndex;
  
  # 定义db链接
  public static function getDb()
  {
    return \Yii::$app->get('elasticsearch_TA');
  }
  
  # 不同的website 使用的是不同的db ,使用前需要先初始化
  # db的名字
  public static function initDb($website_id){
    //echo 888;
    if($website_id){
      //echo 999;
      self::$currentIndex = 'ta'."_".$website_id;
      //echo self::$currentIndex;
      //echo 3;
    }
  }
  
  
  
  # db
  public static function index()
  {
    return self::$currentIndex;
  }
  # table
  public static function type()
  {
    return 'trace_data';
  }
  
   public function attributes()
    {
        // path mapping for '_id' is setup to field 'id'
        return [
      'id',
      'ip',
      'service_date_str',
      'service_datetime',
      'service_timestamp',
      'devide',
      'user_agent',
      'browser_name',
      'browser_version',
      'browser_date',
      'browser_lang',
      'operate',
      'operate_relase',
      'domain',
      'url',
      'title',
      'refer_url',
      'first_referrer_domain',
      'is_return',
      'uuid',
      'device_pixel_ratio',
      'resolution',
      'color_depth',
      'website_id',
      'sku',
      'country_code',
      'country_name',
      
      'order_status',
      'cart',
      'order',
      'category',
      'login_email',
      'register_email',
      'search',
      'currency',
    ];
    }
  
  
  
}

 

安装ElasticSearch ,以及在yii2中的使用

ElasticSearch 是一款优秀的搜索引擎,用java编写,restful接口的方式进行对接。

1. 安装ElasticSearch

安装Java环境
首先检测是否安装java

java -version  
echo $JAVA_HOME

如果java的版本过低,建议安装高版本,下面安装的是java 1.8

cd /opt/    
wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-x64.tar.gz"    
tar xzf jdk-8u66-linux-x64.tar.gz

上面的下载,如果失效,您可以在我的百度网盘中下载jdk: https://pan.baidu.com/s/1kVwRD2Z

 

cd /opt/jdk1.8.0_66/    
alternatives --install /usr/bin/java java /opt/jdk1.8.0_66/bin/java 2    
alternatives --config java

运行了上面的,会初选一个选择的地方,我的机器显示:

There are 3 programs which provide 'java'.    
    
  Selection    Command    
-----------------------------------------------    
*  1           /opt/jdk1.7.0_71/bin/java    
 + 2           /opt/jdk1.8.0_45/bin/java    
   3           /opt/jdk1.8.0_51/bin/java    
   4           /opt/jdk1.8.0_66/bin/java    
    
Enter to keep the current selection[+], or type selection number: 4

我们安装的是jdk1.8.0.66 所以,我选择的是4,这个看具体情况,jdk1.8.0.66 是第几个,就选择那个数字。

alternatives --install /usr/bin/jar jar /opt/jdk1.8.0_66/bin/jar 2    
alternatives --install /usr/bin/javac javac /opt/jdk1.8.0_66/bin/javac 2    
alternatives --set jar /opt/jdk1.8.0_66/bin/jar    
alternatives --set javac /opt/jdk1.8.0_66/bin/javac

安装完成,检查版本

java -version    
    
java version "1.8.0_66"    
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)    
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

保存到文件  /etc/environment中,当服务器重启的时候加载

vi /etc/profile  
  
export JAVA_HOME=/opt/jdk1.8.0_66    
export JRE_HOME=/opt/jdk1.8.0_66/jre    
export PATH=$PATH:/opt/jdk1.8.0_66/bin:/opt/jdk1.8.0_66/jre/bin

重启linux

reboot

查看是否安装成功

java -version  
echo $JAVA_HOME

 

2.

2.1安装ElasticSearch

cd /tools  
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.1.3.zip 
unzip elasticsearch-6.1.3.zip 
mv ./elasticsearch-6.1.3 /usr/local/elasticsearch  
cd /usr/local  
groupadd elasticsearch  
useradd -g elasticsearch  elasticsearch  
chown elasticsearch:elasticsearch -R elasticsearch

上面安装的是es6.

2.2 设置开机启动: 我是本地,直接把iptables 关掉了

vim /etc/rc.d/rc.local  

service iptables stop  
su elasticsearch  -c "/usr/local/elasticsearch/bin/elasticsearch -d"

一定要注意,elasticSearch不能用root账户启动,elasticSearch不能用root账户启动,elasticSearch不能用root账户启动,重要的说三遍,我这里用的是我新建的elasticsearch账户开机启动

报错:

$./bin/elasticsearch
Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000085330000, 2060255232, 0) failed; error='Cannot allocate memory' (errno=12)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /data/elasticsearch-5.2.2/hs_err_pid26945.log

解决方案:调小启动内存

# vi /usr/local/elasticsearch/config/jvm.options

#-Xms2g
#-Xmx2g
-Xms256m
-Xmx64m

上面设置的分配的内存的最大值为256MB和最小值64mb,您可以根据自己的机器情况设置内存大小。

重新启动即可。

 

2.3 配置

vim /usr/local/elasticsearch/config/elasticsearch.yml

修改如下:

cluster.name: TA-application  
node.name: node-210  
network.host: 192.168.0.210

其中cluster.name 是集群名称,这个不要使用默认的,要修改,去掉注释,如果有多个机器,加入同一个集群,那么这个值必须一样

noide.name 是集群里面每个节点的值,也就是当前机器的节点的值,这个值,每个节点要不一样。

network host 改成当前的内网ip

 

下面的部分是elasticsearch 2 部分的插件,在es6中已经不可用,

es6的可视化GUI,请查看:http://www.fecshop.com/topic/668

 

 

2.3  #安装head 插件

su elasticsearch  
  
cd  /usr/local/elasticsearch  
  
bin/plugin install mobz/elasticsearch-head  
  
#启动:  
  
bin/elasticsearch -d

2.4 查看:

http://192.168.0.210:9200/

head插件地址:

http://192.168.0.210:9200/_plugin/head/

2.5 集群设置

如果想要建立一个elasticSearch集群,可以按照下面的步骤,非常的简单,首先,想说明的是:对于elasticSearch,他隐藏了分布式的复杂性,分片和复制集,都是他自动完成,你只需要配置好ip就可以了,下面是配置的步骤:

我有两台机器  192.169.0.210   192.168.0.199

我的两台机器都按照上面的步骤配置完成,下面配置集群

首先是192.168.0.210

vim /usr/local/elasticsearch/config/elasticsearch.yml

#找到行 , 修改如下:

discovery.zen.ping.unicast.hosts: ["192.168.0.199"]

上面的ip就是其他的节点的ip,如果我有5台机器,那么,这里需要把其他四台机器的ip写上。

同理,对于其他的节点,需要把其他的节点协商,用逗号隔开

elasticSearch会找到对应的节点,自动分片和做复制集。

3. 资料片

官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/5.0/_installation.html

入门教程

http://www.jianshu.com/p/f437b893502a

权威指南

http://es.xiaoleilu.com/

 

4.  Elasticsearch集群关闭节点

关闭节点

关闭节点的API允许关闭集群中的一个或多个(或者全部)节点。下面是一个关闭 _local 节点的例子:

$ curl -XPOST 'http://localhost:9200/_cluster/nodes/_local/_shutdown' 

也可以通过各自的节点ID来关闭指定的节点(或者像这里说明 的别的选项):

$ curl -XPOST 'http://localhost:9200/_cluster/nodes/nodeId1,nodeId2/_shutdown' 

集群的主节点也可以使用下面的方法来关闭:

$ curl -XPOST 'http://localhost:9200/_cluster/nodes/_master/_shutdown' 

最后,可以使用如下的任意一种方法来关闭所有的节点:

$ curl -XPOST 'http://localhost:9200/_shutdown' $ curl -XPOST 'http://localhost:9200/_cluster/nodes/_shutdown' $ curl -XPOST 'http://localhost:9200/_cluster/nodes/_all/_shutdown' 

延迟

默认情况下,关闭命令会延迟1秒(1s)之后执行。可以通过设置 delay 参数 来指定延迟的时间。比如:

$ curl -XPOST 'http://localhost:9200/_cluster/nodes/_local/_shutdown?delay=10s' 

禁用关闭命令

关闭的API可以通过设置节点里的 action.disable_shutdown 选项来禁用。

5. Yii2 使用elasticSearch

首先需要安装yii2的elasticSearch插件

https://github.com/yiisoft/yii2-elasticsearch

安装步骤,参看官网的下面的说明,用起来还是不错

里面有聚合功能,可以做一些小规模数据的快速统计。

yii2 elasticSearch的使用说明地址:https://github.com/yiisoft/yii2-elasticsearch/blob/master/docs/guide/README.md

 

 

 

 

 

mongodb 导出csv文件

/usr/bin/mongoexport  --csv  -d erp -f _id,item_no,gross_default,profit,product_cost,order_gross_default,order_profit,order_product_cost,profit_difference,gross_difference,product_cost_difference -c tmp_erp_order_check_problem_profit -o  /backup/erp/tmp_erp_order_check_problem_profit-07-09.csv

/usr/bin/mongoexport  --csv  -d erp  -f _id,item_no,gross_default,profit,product_cost,order_gross_default,order_profit,order_product_cost,profit_difference,gross_difference,product_cost_difference  -c tmp_erp_order_check_problem_gross -o  /backup/erp/tmp_erp_order_check_problem_gross-07-09.csv

/usr/bin/mongoexport  --csv  -d erp  -f _id,item_no,gross_default,profit,product_cost,order_gross_default,order_profit,order_product_cost,profit_difference,gross_difference,product_cost_difference  -c tmp_erp_order_check_problem_product_cost -o  /backup/erp/tmp_erp_order_check_problem_product_cost-07-09.csv

上面是mongodb 表  导出 csv 的一个例子

XunSearch 安装,使用

  1. 安装:
wget http://www.xunsearch.com/download/xunsearch-full-latest.tar.bz2
tar -xjf xunsearch-full-latest.tar.bz2
cd xunsearch-full-1.4.10/
sh setup.sh

第一次安装的话,过程可能会稍显漫长,请不必着急,您大可泡杯茶一边喝一边等待即可。

待命令运行结束后,如果没有出错中断,则表示顺利安装完成,然后就可以启动/重新启动 xunsearch 的后台服务,下面命令中的 $prefix 请务必替换为您的安装目录,而不是照抄。

cd $prefix ; bin/xs-ctl.sh restart

强烈建议您将此命令添加到开机启动脚本中,以便每次服务器重启后能自动启动搜索服务程序, 在 Linux 系统中您可以将脚本指令写进 /etc/rc.local 即可。

其他更加详细的资料参看:

http://www.xunsearch.com/doc/php/guide/start.installation

 

2.

Yii2 Mongodb 数据库报错:remote connect close

我使用了mongodb的复制集,报错remote connect close

Yii2中我更改下了配置如下:

'mongodb_erp' => [
            'class' => 'yii\mongodb\Connection',
            'dsn' => 'mongodb://192.168.220.105:27017/erp,192.168.220.106:27017/erp?replicaSet=rs0&readPreference=secondaryPreferred', # primaryPreferred |  secondaryPreferred
      'options' => [
        'socketTimeoutMS' => 300000,  # 设置超时时间
        'connectTimeoutMS'=> 600000,  # 设置连接超时时间
      ],
],

 

将读改成副本读优先,对于读的优先级的设置解释如下:

/**
        const string RP_PRIMARY = "primary" ;  						# 始终用主读
        const string RP_PRIMARY_PREFERRED = "primaryPreferred" ;	# 主读优先
        const string RP_SECONDARY = "secondary" ;					# 始终用副本读
        const string RP_SECONDARY_PREFERRED = "secondaryPreferred" ;# 副本读优先
        const string RP_NEAREST = "nearest" ;						# 主和副本随机读
      */

另外,长连接可能存在失效问题,在连接mongodb的时候,需要循环一下:

Yii2 随机发生 php mongo报错:’Failed to connect to: XXXXX: Remote server has closed the connection’的解决方法

 

Yii2 随机发生 php mongo报错:’Failed to connect to: XXXXX: Remote server has closed the connection’的解决方法

 

运行mongodb连接的时候,遇到了 Remote server has closed the connection 的错误问题。而且是随机性发生的。

在Yii2中,需要进行的修改为:

Yii2-mongodb/Connecting.php

修改函数:

public function open()
   {
       if ($this->mongoClient === null) {
           if (empty($this->dsn)) {
               throw new InvalidConfigException($this->className() . '::dsn cannot be empty.');
           }
           $token = 'Opening MongoDB connection: ' . $this->dsn;
           try {
               Yii::trace($token, __METHOD__);
               Yii::beginProfile($token, __METHOD__);
               $options = $this->options;
               $options['connect'] = true;
               if ($this->defaultDatabaseName !== null) {
                   $options['db'] = $this->defaultDatabaseName;
               }
              // $this->mongoClient = new \MongoClient($this->dsn, $options);
         $this->mongoClient = $this->getCustomMongoClient($options);
               $this->initConnection();
               Yii::endProfile($token, __METHOD__);
           } catch (\Exception $e) {
               Yii::endProfile($token, __METHOD__);
               throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
           }
       }
   }

 

添加函数

public function getCustomMongoClient($options,$retry = 4) {
    try {
      return new \MongoClient($this->dsn, $options);
    } catch(Exception $e) {
      /* Log the exception so we can look into why mongod failed later */
      //logException($e);
    }
    if ($retry > 0) {
      return $this->getCustomMongoClient($options, --$retry);
    }
    throw new Exception("I've tried several times getting MongoClient.. Is mongod really running?");
  }

也就是通过多次尝试连接的方式。

Yii2 mongodb – 关于mongoid的使用

mongodb的_id默认是对象,在使用起来还是和mysql有很多的区别和不习惯,下面所以下:

1.mysql是可以通过getLastInsertId得到插入的id,但是mongdob不行,但是mongo可以事先得到mongoid,也就是通过:

$primaryVal = new \MongoId;

这样就得到了mongoId,然后赋值到数据,插入即可,也就是说mongoId是先得到在插入的。

2.如果我要自定义mongoId,可以这样:

$primaryVal = new \MongoId('xxxxxxxxxxx');

譬如查询的时候:

->where([$id => ['?ne'=> new \MongoId($primaryVal)]])

3.查询得到的记录,id是对象,我想得到字符串怎么办?

$one[$id]是_id的值,是一个对象,那么可以通过下面得到字符串:

$one[$id]->{'$id'}

 

 

Redis 分区实现原理

摘要

Redis Partitioning即Redis分区,简单的说就是将数据分布到不同的redis实例中,因此对于每个redis实例所存储的内容仅仅是所有内容的一个子集。分区(Partitioning)不仅仅是Redis中的概念,几乎是所有数据存储系统都会涉及到的概念,这篇文章将会在理解分区基本概念的基础之上进一步了解Redis对分区的支持。

我们为什么要分区

我们为什么要分区?分区的动机是什么?通常来说,Redis分区的好处大致有如下两个方面:

  1. 性能的提升,单机Redis的网络I/O能力和计算资源是有限的,将请求分散到多台机器,充分利用多台机器的计算能力可网络带宽,有助于提高Redis总体的服务能力。
  2. 存储的横向扩展,即使Redis的服务能力能够满足应用需求,但是随着存储数据的增加,单台机器受限于机器本身的存储容量,将数据分散到多台机器上存储使得Redis服务可以横向扩展。

总的来说,分区使得我们本来受限于单台计算机硬件资源的问题不再是问题,存储不够?计算资源不够?带宽不够?我们都可以通过增加机器来解决这些问题。

Redis分区基础

实际应用中有很多分区的具体策略,举个例子,假设我们已经有了一组四个Redis实例分别为R0、R1、R2、R3,另外我们有一批代表用户的键,如:user:1,user:2,……等等,其中“user:”后面的数字代表的是用户的ID,我们要做的事情是把这些键分散存储在这四个不同的Redis实例上。怎么做呢?最简单的一种方式是范围分区(range partitioning),下面我们来看看基于范围分区怎么做。

范围分区

所谓范围分区,就是将一个范围内的key都映射到同一个Redis实例中,加入数据集还是上面提到的用户数据,具体做法如下:

我们可以将用户ID从0到10000的用户数据映射到R0实例,而将用户ID从10001到20000的对象映射到R1实例,依次类推。

这种方法虽然简单,但是在实际应用中是很有效的,不过还是有问题:

  • 我们需要一张表,这张表用来存储用户ID范围到Redis实例的映射关系,比如用户ID0-10000的是映射到R0实例……。
  • 我们不仅需要对这张表进行维护,而且对于每种对象类型我们都需要一个这样的表,比如我们当前存储的是用户信息,如果存储的是订单信息,我们就需要再建一张映射关系表。
  • 如果我们想要存储的数据的key并不能按照范围划分怎么办,比如我们的key是一组uuid,这个时候就不好用范围分区了。

因此,在实际应用中,范围分区并不是很好的选择,不用担心,我们还有更好的方法,接下来认识下哈希分区。

哈希分区

哈希分区跟范围分区相比一个明显的优点是哈希分区适合任何形式的key,而不像范围分区一样需要key的形式为object_name:<id>,而且分区方法也很简单,一个公式就可以表达:

id=hash(key)%N

其中id代表Redis实例的编号,公式描述的是首先根据key和一个hash函数(如crc32函数)计算出一个数值型的值。接着上面的例子,我们的第一个要处理的key是user:1,hash(user:1)的结果是93024922。

然后哈希结果进行取模,取模的目的是计算出一个介于0到3之间的值,因此这个值才可以被映射到我们的一台Redis实例上面。比如93024922%4结果是2,我们就会知道foobar将要被存储在R2上面。

当然除了上面提到的两种分区方法,还有很多其他的方法。比如一种从哈希分区演进而来的consistent hashing分区,相信信息可以参考我的另一篇文章《memcached分布式实现原理》,其已经被redis client和proxies实现了。

不同的分区实现

分区可以在redis软件栈的不同部分被实现,我们来看看下面几种:

客户端实现

客户端实现即key在redis客户端就决定了要被存储在那台Redis实例中,见下图:

客户端实现分区示意图

上面为客户端实现Redis分区的示意图。

代理实现

代理实现即客户端将请求发往代理服务器,代理服务器实现了Redis协议,因此代理服务器可以代理客户端和Redis服务器通信。代理服务器通过配置的分区schema来将客户端的请求转发到正确的Redis实例中,同时将反馈消息返回给客户端。代理实现Redis分区示意图如下:

代理实现Redis分区示意图

Redis和Memcached代理Twemoroxy都实现了代理分区。

查询路由

查询路由是Redis Cluster实现的一种Redis分区方式:

查询路由Redis分区示意图

查询路由的过程中,我们可以将查询请求随机的发送到任意一个Redis实例,这个Redis实例负责将请求转发至正确的Redis实例中。Redis集群实现了一个通过和客户端协作的hybrid来做查询路由。

Redis分区的缺点

尽管Redis分区到现在为止,so far so good,但是Redis分区有一些致命的缺点,这导致一些Redis功能在分区的环境下并不能很好地工作,我们来看看:

  • 多键操作是不被支持的,比如我们将要批量操作的键被映射到了不同的Redis实例中。
  • 多键的Redis事务是不被支持的。
  • 分区的最小粒度是键,因此我们不能将关联到一个键的很大的数据集映射到不同的实例。
  • 当应用分区的时候,数据的处理是非常复杂的,比如我们需要处理多个rdb/aof文件,将分布在不同实例的文件聚集到一起备份。
  • 添加和删除机器是很复杂的,例如Redis集群支持几乎运行时透明的因为增加或减少机器而需要做的rebalancing,然而像客户端和代理分区这种方式是不支持这种功能的。

既然有问题,那么就需要解决方案,这个时候Pre-sharding来了,后面我们会介绍Pre-Sharding。

持久存储用还是缓存

尽管数据分区对于Redis来说无论是数据持久化存储还是缓存,在概念上都是一样的,然而对于数据持久化存储还是有一个很大的限制。当我们使用Redis来作为持久化存储的时候,每一个key必须一直被映射到同一个Redis实例。而当Redis被当做缓存使用的时候,对于这个key,如果一个实例不能用了,这个key还可以被映射到其他的实例中。

Consistent hashing实现通常使得当一个key被映射到的实例不能用的时候将这个key映射到其他实例成为可能。类似,如果增加了一台机器,一部分的key将会被映射到这台新的机器上,我们需要了解的两点如下:

  1. 如果Redis被用来当做缓存,且要求容易增加或删除机器,使用consistent hashing是非常简单的。
  2. 如果Redis被用来当做(持久)存储,一个固定的key到实例的映射是需要的,因此我们不能够再灵活的添加或删除机器。否则,我们需要在增加或删除机器的时候系统能够rebalace,当前Redis Cluster已经支持。

Pre-Sharding

通过上面的介绍,我们知道Redis分区应用起来是有问题的,除非我们只是使用Redis当做缓存,否则对于增加机器或删除机器是非常麻烦的。

然而,通常我们Redis容量变动在实际应用中是非常常见的,比如今天我需要10台Redis机器,明天可能就需要50台机器了。

鉴于Redis是很轻量级的服务(每个实例仅仅占用1M),对于上面的问题一种简单的解决办法是:

我们可以开启多个Redis实例,尽管是一台物理机器,我们在刚开始的时候也可以开启多个实例。我们可以从中选择一些实例,比如32或64个实例来作为我们的工作集群。当一台物理机器存储不够的时候,我们可以将一般的实例移动到我们的第二台物理机上,依次类对,我们可以保证集群中Redis的实例数不变,又可以达到扩充机器的目的。

怎么移动Redis实例呢?当需要将Redis实例移动到独立的机器上的时候,我们可以通过下面步骤实现:

  1. 在新的物理机上启动一个新的Redis实例。
  2. 将新的物理机作为要移动的那台的slave机器。
  3. 停止客户端。
  4. 更新将要被移动的那台Redis实例的IP地址。
  5. 对于slave机器发送SLAVEOF ON ONE命令。
  6. 使用新的IP启动Redis客户端。
  7. 关闭不再使用的那个Redis实例。

总结

这篇文章在理解Redis分区概念的基础之上又介绍了Redis分区常见的几种实现方式及原理,最后根据实现中遇到的问题引入了Pre-Sharding解决方案。

配置mongodb 复制集3.2

我们这里讲解的是如何配置mongodb复制集,首先在每一个机器上面安装mongodb,我们是通过yum的方式安装mongodb3.2

1.在文章中添加yum安装的配置文件:

touch /etc/yum.repos.d/mongodb-org-3.2.repo
vim /etc/yum.repos.d/mongodb-org-3.2.repo

打开文件后里面添加内容:

[mongodb-org-3.2]  
name=MongoDB Repository  
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/  
gpgcheck=1  
enabled=1  
gpgkey=https://www.mongodb.org/static/pgp/server-3.2.asc

2.yum安装

sudo yum install -y mongodb-org

其他节点也进行进行上面的步骤,将其他的节点按照上面,进行安装mongodb,安装完成后,都关闭mongodb:

sudo service mongod stop

3. 在每一个mongodb的节点,修改hosts

节点:192.168.220.105  mongo0.example.com

节点:192.168.220.106 mongo1.example.com

vim /etc/hosts

192.168.220.105:在上面打开的文件中添加或修改:

127.0.0.1   localhost mongo0.example.com  
192.168.220.105 mongo0.example.com  
192.168.220.106 mongo1.example.com

192.168.220.106:在上面文件中添加或者修改:

127.0.0.1   localhost mongo1.example.com  
192.168.220.105 mongo0.example.com  
192.168.220.106 mongo1.example.com

 

4. 在每一个mongodb的节点,修改hostname

节点:192.168.220.105  mongo0.example.com

节点:192.168.220.106 mongo1.example.com

192.168.220.105:

sudo hostname mongo0.example.com  
vim   /etc/hostname
#在打开的文件中添加: 
mongo0.example.com

192.168.220.106:

sudo hostname mongo1.example.com  
vim   /etc/hostname 
mongo1.example.com

5.修改mongodb的配置  vim /etc/mongod.conf

进行的修改为: 把bingIp注释掉,因为这一行只让本地访问:

net:  
  port: 27017  
#  bindIp: 127.0.0.1  # Listen to local interface only, comment to listen on all interfaces.

添加复制集配置:

replication:  
   replSetName: rs0

编辑完的内容为:

# mongod.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

# Where and how to store data.
storage:
  dbPath: /var/lib/mongo
  journal:
    enabled: true
#  engine:
#  mmapv1:
#  wiredTiger:

# how the process runs
processManagement:
  fork: true  # fork and run in background
  pidFilePath: /var/run/mongodb/mongod.pid  # location of pidfile

# network interfaces
net:
  port: 27017
#  bindIp: 127.0.0.1  # Listen to local interface only, comment to listen on all interfaces.

#security:

#operationProfiling:

replication:
   replSetName: rs0
#sharding:

## Enterprise-Only Options

#auditLog:

#snmp:

保存文件后,重启linxu。

添加开机启动

chkconfig mongod on

启动mongodb:

/etc/init.d/mongod restart

 

8.现在有两台linux,现在开始配置,注意,只能进入一台mongodb,其他的mongdb不要配置,启动就好。

在mongo0中:(这个作为主节点)

mongo

执行log如下:

[root@mongo0 ~]# mongo  
MongoDB shell version: 3.2.7  
connecting to: test  
Server has startup warnings:   
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten]   
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.  
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'  
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten]   
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.  
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'  
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten]   
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten] ** WARNING: soft rlimits too low. rlimits set to 1024 processes, 64000 files. Number of processes should be at least 32000 : 0.5 times number of files.  
2016-06-20T14:10:48.876+0800 I CONTROL  [initandlisten]

然后初始化:(注意,从节点不要执行rs.initiate()  , 只有主节点执行这个)

rs.initiate()

log如下:

rs.initiate()  
{  
    "info2" : "no configuration specified. Using a default configuration for the set",  
    "me" : "mongo0.example.com:27017",  
    "ok" : 1  
}  
rs0:OTHER>

增加节点:(在主节点中执行,添加从节点,主要从节点的iptables要添加信任,如果是本地,则可以直接关掉iptables,最好是添加ip信任,添加ip信任的方式

/sbin/iptables -I INPUT -p tcp --dport 27017 -j ACCEPT
/etc/rc.d/init.d/iptables save

rs0:OTHER> rs.add("mongo1.example.com:27017");  
{ "ok" : 1 }

rs.config() 查看配置,log如下:

rs0:PRIMARY> rs.config();  
{  
    "_id" : "rs0",  
    "version" : 2,  
    "protocolVersion" : NumberLong(1),  
    "members" : [  
        {  
            "_id" : 0,  
            "host" : "mongo0.example.com:27017",  
            "arbiterOnly" : false,  
            "buildIndexes" : true,  
            "hidden" : false,  
            "priority" : 1,  
            "tags" : {  
                  
            },  
            "slaveDelay" : NumberLong(0),  
            "votes" : 1  
        },  
        {  
            "_id" : 1,  
            "host" : "mongo1.example.com:27017",  
            "arbiterOnly" : false,  
            "buildIndexes" : true,  
            "hidden" : false,  
            "priority" : 1,  
            "tags" : {  
                  
            },  
            "slaveDelay" : NumberLong(0),  
            "votes" : 1  
        }  
    ],  
    "settings" : {  
        "chainingAllowed" : true,  
        "heartbeatIntervalMillis" : 2000,  
        "heartbeatTimeoutSecs" : 10,  
        "electionTimeoutMillis" : 10000,  
        "getLastErrorModes" : {  
              
        },  
        "getLastErrorDefaults" : {  
            "w" : 1,  
            "wtimeout" : 0  
        },  
        "replicaSetId" : ObjectId("5767896cd1595b8f30b7cd4a")  
    }  
}

查看状态:

rs0:PRIMARY> rs.status()

如果后续添加节点,那么,安装完mongodb,设置完成mongodb后,启动mongodb后,设置完成hosts后,直接在主节点中执行添加:

执行log如下:

rs0:PRIMARY> rs.add("mongo2.example.com:27017");
{ "ok" : 1 }
rs0:PRIMARY> rs.status
function () {
    return db._adminCommand("replSetGetStatus");
}
rs0:PRIMARY> rs.status();
{
  "set" : "rs0",
  "date" : ISODate("2016-10-08T02:45:54.068Z"),
  "myState" : 1,
  "term" : NumberLong(19),
  "heartbeatIntervalMillis" : NumberLong(2000),
  "members" : [
    {
      "_id" : 0,
      "name" : "mongo0.example.com:27017",
      "health" : 1,
      "state" : 1,
      "stateStr" : "PRIMARY",
      "uptime" : 1126,
      "optime" : {
        "ts" : Timestamp(1475894744, 1),
        "t" : NumberLong(19)
      },
      "optimeDate" : ISODate("2016-10-08T02:45:44Z"),
      "electionTime" : Timestamp(1475894303, 1),
      "electionDate" : ISODate("2016-10-08T02:38:23Z"),
      "configVersion" : 3,
      "self" : true
    },
    {
      "_id" : 1,
      "name" : "mongo1.example.com:27017",
      "health" : 1,
      "state" : 2,
      "stateStr" : "SECONDARY",
      "uptime" : 617,
      "optime" : {
        "ts" : Timestamp(1475894744, 1),
        "t" : NumberLong(19)
      },
      "optimeDate" : ISODate("2016-10-08T02:45:44Z"),
      "lastHeartbeat" : ISODate("2016-10-08T02:45:52.842Z"),
      "lastHeartbeatRecv" : ISODate("2016-10-08T02:45:52.847Z"),
      "pingMs" : NumberLong(0),
      "syncingTo" : "mongo0.example.com:27017",
      "configVersion" : 3
    },
    {
      "_id" : 2,
      "name" : "mongo2.example.com:27017",
      "health" : 0,
      "state" : 8,
      "stateStr" : "(not reachable/healthy)",
      "uptime" : 0,
      "optime" : {
        "ts" : Timestamp(0, 0),
        "t" : NumberLong(-1)
      },
      "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
      "lastHeartbeat" : ISODate("2016-10-08T02:45:52.842Z"),
      "lastHeartbeatRecv" : ISODate("1970-01-01T00:00:00Z"),
      "pingMs" : NumberLong(0),
      "lastHeartbeatMessage" : "No route to host",
      "configVersion" : -1
    }
  ],
  "ok" : 1
}
rs0:PRIMARY> 

 

 

日志:

rs0:PRIMARY> rs.status()  
{  
    "set" : "rs0",  
    "date" : ISODate("2016-06-20T06:14:17.377Z"),  
    "myState" : 1,  
    "term" : NumberLong(1),  
    "heartbeatIntervalMillis" : NumberLong(2000),  
    "members" : [  
        {  
            "_id" : 0,  
            "name" : "mongo0.example.com:27017",  
            "health" : 1,  
            "state" : 1,  
            "stateStr" : "PRIMARY",  
            "uptime" : 92,  
            "optime" : {  
                "ts" : Timestamp(1466403201, 1),  
                "t" : NumberLong(1)  
            },  
            "optimeDate" : ISODate("2016-06-20T06:13:21Z"),  
            "infoMessage" : "could not find member to sync from",  
            "electionTime" : Timestamp(1466403181, 1),  
            "electionDate" : ISODate("2016-06-20T06:13:01Z"),  
            "configVersion" : 2,  
            "self" : true  
        },  
        {  
            "_id" : 1,  
            "name" : "mongo1.example.com:27017",  
            "health" : 1,  
            "state" : 2,  
            "stateStr" : "SECONDARY",  
            "uptime" : 55,  
            "optime" : {  
                "ts" : Timestamp(1466403201, 1),  
                "t" : NumberLong(1)  
            },  
            "optimeDate" : ISODate("2016-06-20T06:13:21Z"),  
            "lastHeartbeat" : ISODate("2016-06-20T06:14:15.784Z"),  
            "lastHeartbeatRecv" : ISODate("2016-06-20T06:14:14.010Z"),  
            "pingMs" : NumberLong(0),  
            "configVersion" : 2  
        }  
    ],  
    "ok" : 1  
}

如果连接的机器是另外一个机器,也就说不是mongodb复制集中的机器,需要添加host(此处需要说明的是php,如果是和mongodb分开的那么需要在php机器的hosts中添加hosts)

vim  /etc/hosts

192.168.220.105 mongo0.example.com
192.168.220.106 mongo1.example.com

 

在shell 下面链接:(test是数据库的名字,rs0是复制集的名字)

mongo --host rs0/mongo0.example.com:27017,mongo1.example.com:27017 test

在php中连接:

'dsn' => 'mongodb://192.168.220.105:27017/erp,192.168.220.106:27017/erp?replicaSet=rs0&readPreference=primaryPreferred',

备份:

mongodump  -h 192.168.220.60:27500 -d erp -o /terry/erp

恢复:

mongorestore  -h 192.168.220.105:27017 -d erp  /terry/erp

如果在配置复制集过程中出现了问题,无法进行,一般是想重新配置,但有一种比较好的办法,就是删除掉dbPath,在配置文件中dbPath的配置如下:

dbPath: /var/lib/mongo
rm -rf /var/lib/mongo/*

 

其他:

1. 删除mongodb的库,重新启动

mv /var/lib/mongo /var/lib/mongo1  
mkdir /var/lib/mongo  
chown mongod:mongod /var/lib/mongo

2.删除复制集节点:

rs.remove("mongo0.example.com:27017")

3.在副本集的副节点读数据:

db.setSlaveOk()

4.主从切换

进入主mongo  执行:

rs.stepDown(15);

log如下:

rs0:PRIMARY> rs.stepDown(15);
2016-10-08T10:37:25.511+0800 E QUERY    [thread1] Error: error doing query: failed: network error while attempting to run command 'replSetStepDown' on host '127.0.0.1:27017'  :
DB.prototype.runCommand@src/mongo/shell/db.js:135:1
DB.prototype.adminCommand@src/mongo/shell/db.js:153:16
rs.stepDown@src/mongo/shell/utils.js:1181:12
@(shell):1:1

2016-10-08T10:37:25.513+0800 I NETWORK  [thread1] trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed
2016-10-08T10:37:25.525+0800 I NETWORK  [thread1] reconnect 127.0.0.1:27017 (127.0.0.1) ok
rs0:SECONDARY> 
rs0:SECONDARY> 
rs0:SECONDARY> 
rs0:SECONDARY> 
rs0:SECONDARY> 

 

 

参考:

https://gist.github.com/leommoore/309de7c0042ed697ee84

https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/

https://docs.mongodb.com/manual/tutorial/deploy-replica-set/

https://docs.mongodb.com/manual/reference/configuration-options/