mongodb

2019-05-17  本文已影响0人  我已不是少年郎

需求

要记录日志,并且要根据日志的多个字段来查询(复杂查询),保存30天以上的日志要删除,日志的准确性和完整性要求低。

选型

目前项目使用了redis和mysql,由于日志数量太多,写入和读取操作频繁,所以mysql首先排除了,其次需要复杂的查询操作,用redis操作复杂度略高,并且mongodb是存入硬盘而redis则大部分都在内存里(这句可能是错误的),所以综合考虑选用mongodb来存储日志。

安装

参考官方文档 Install on Red Hat

[root@centos]# vim /etc/yum.repos.d/mongodb-org-4.0.repo
复制以下内容

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

保存之后安装
[root@centos]# yum install -y mongodb-org

状态、重启、停止
systemctl status mongod.service
systemctl restart mongod.service
systemctl stop mongod.service

安装填坑

总之要借助systemctl status -l mongod.service指令和/var/log/mongodb/mongod.log日志文件来排查错误。

1、由于安装时先写的repo然后又执行了yum makecache,最后总是提示找不到版本,所以要执行
[root@centos]# yum clean all

2、千万不要直接[root@centos]# yum install -y mongodb这样会直接安装centos自带的repo版本,版本是2.×的老版本,后面还要卸载,按照官方文档执行
[root@centos]# yum install -y mongodb-org

3、selinux权限放行,这个查看了mongo日志,按照操作日志提示执行

2019-05-14T19:56:02.967+0800 E STORAGE [initandlisten] Failed to set up listener: # SocketException: Cannot assign requested address

ausearch -c 'mongod' --raw | audit2allow -M my-mongod
semodule -i my-mongod.pp

[root@centos]# ausearch -c 'mongod' --raw | audit2allow -M my-mongod
[root@centos]# semodule -i my-mongod.pp

4、启动不了,因为删除了整个数据库文件,然后用的是root权限启动mogod,这就导致了重新创建的storage.bson文件权限太高,以后如果用systemctl来启动,无权访问的问题。

2019-05-14T20:44:40.366+0800 E STORAGE [initandlisten] Unable to read the storage engine metadata file: FileNotOpen: Failed to read metadata from /var/lib/mongo/storage.bson

2019-05-14T20:44:40.366+0800 F - [initandlisten] Fatal Assertion 28661 at src/mongo/db/storage/storage_engine_metadata.cpp 93

根据提示修改整个数据库文件归属

[root@centos]# chown mongod -R mongo
[root@centos]# chgrp mongod -R mongo

5、还是启动不了,因为刚安装好就启动了一下,然后就kill -9杀掉了进程,不了解要先删除mongod.lockWiredTiger.lock文件,这里就不贴日志了,说一下除了systemctl stop之外的正确停止mongodb的做法

关闭方法一

在mongo的shell里执行,需要先切换到admin,如果开了权限认证需要先认证db.auth("admin", "password" )才能成功
db.shutdownServer()

关闭方法二

[root@centos]# killall mongod

创建管理员和数据库账号

进入mongodb管理shell
[root@centos]# mongo --port 27017

查看所有数据库
show dbs
切换到admin数据库
use admin
创建管理员账号

db.createUser(
  {
    user: "admin",
    pwd: "adminpassword",
    roles: [
        { role: "root", db: "admin" },
        { role: "userAdminAnyDatabase", db: "admin" } 
    ]
  }
)

切换到logdb数据库
use logdb
创建数据库账号,就是程序里连接时用的账号密码

db.createUser(
  {
    user: "loguser",
    pwd: "logpassword",
    roles: [
        { role: "dbOwner", db: "logdb" }
    ]
  }
)

查找所有用户
db.system.users.find().pretty()

开启账户认证和远程访问

主要在配置文件mongod.conf里进行
[root@centos]# vim /etc/mongod.conf

设置mongodb远程访问

主要在net模块

net:
port: 27017
bindIp: 127.0.0.1

port就是端口号,如果修改了端口号,比如修改为17017,那么进入mongo的时候就要执行[root@centos]# mongo --port 17017

bindIp和redis的bind配置的坑一样,都是又大又深,网上的各种解释和不负责任的随手粘帖复制,真让我无f可k说!!

网络错误说法一

编辑mongod.conf注释bindIp,并重启mongodb.(这句配置代表只能本机使用,所以需注释)。

正确操作

查看官方文档就知道,即使注释掉也没卵用,因为人家默认的就是本机访问,必须手动改为bindIp: 0.0.0.0

网络错误说法二

绑定多个IP只需要在后面追加新IP用逗号隔开即可,如果还是报错

Failed to set up listener: SocketException: Cannot assign requested address

则需要把所有IP用中括号括起来, bindIp: [127.0.0.1,115.159.159.129],尝试了之后会给你这样一个报错

Scalar option 'net.bindIp' must be a single value

正确操作

经过多次修改试验得出结论,
1、IP只能配置1个,配置多个IP均启动不了
2、IP只能是127.0.0.1或者0.0.0.0,配置局域网的其他IP或者广域网IP都启动不了
3、配置127.0.0.1,局域网内不能访问,0.0.0.0则可以访问

结论

在看了官方文档之后其实还是一头雾水没搞清楚,就以4.0.9版本的实际操作来说,如果不限制IP访问,既不能注释,也无法配置多个,只能修改为

net:
port: 17017
bindIp: 0.0.0.0

开启账号密码认证

在执行这一步之前,需要先创建管理员和数据库账号,
方法很简单,在mongod.conf追加如下内容

security:
   authorization: enabled

千万记得,配置文件所有的冒号后面要跟一个空格!
第二行千万不要顶格写,加两个空格

保存之后可以直接在本机访问
[root@centos]# mongo --port 17017 -u "admin" -p "adminpassword" --authenticationDatabase "admin"
或者通过远程连接可视化工具,免费版的Robo 3T来连接。

其他优化项,关闭THP

这些优化项实在查阅资料时看到的,但并没有实际操作。关闭方法参考官方文档

Transparent Huge Pages (THP),通过使用更大的内存页面,可以减少具有大量内存的机器上的缓冲区(TLB)查找的开销。

但是,数据库工作负载通常对THP表现不佳,因为它们往往具有稀疏而不是连续的内存访问模式。您应该在Linux机器上禁用THP,以确保MongoDB的最佳性能。

集成spring

本文在spring4.3.9基础上集成,请注意版本号和适用性。

maven依赖

这里有个地方需要注意一下,如果spring4.X的话,那么spring-data-mongodb就必须用1.X,如果用最新的2.X,会在运行时报找不到此函数的错误

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: org.springframework.util.Assert.isInstanceOf(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/function/Supplier;)V

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>1.10.22.RELEASE</version>
        </dependency>
       <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongo-java-driver</artifactId>
            <version>3.10.2</version>
        </dependency>
配置spring的mongoTemplate

首先在spring配置文件里加上
xmlns:mongo="http://www.springframework.org/schema/data/mongo"在schema里添加http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
然后配置了mongodb的数据源,去掉了插入数据时生成的_class字段,最后配置mongoTemplate。

完整配置如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mongo="http://www.springframework.org/schema/data/mongo"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans  
           http://www.springframework.org/schema/beans/spring-beans.xsd  
           http://www.springframework.org/schema/aop  
           http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
           http://www.springframework.org/schema/context  
           http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd

            http://www.springframework.org/schema/data/mongo
            http://www.springframework.org/schema/data/mongo/spring-mongo.xsd

            http://www.springframework.org/schema/data/repository
            http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd
            ">
     <!-- 引入jdbc配置文件 -->
    <context:property-placeholder location="classpath:properties/*.properties"/>
   
   
    <!-- 自动扫描注解的bean -->
    <context:component-scan
            base-package="com.lucien.service"/>

    <!-- mongodb -->
    <mongo:mongo-client id="mongo" host="${mongo.host}" port="${mongo.port}"
                        credentials="${mongo.username}:${mongo.password}@${mongo.dbname}">
        <mongo:client-options
                connections-per-host="${mongo.connectionsPerHost}"
                threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}"
                connect-timeout="${mongo.connectTimeout}"
                max-wait-time="${mongo.maxWaitTime}"
                socket-keep-alive="${mongo.socketKeepAlive}"
                socket-timeout="${mongo.socketTimeout}"
        />
    </mongo:mongo-client>

    <!-- 设置使用的数据库名-->
    <mongo:db-factory id="mongoDbFactory" dbname="${mongo.dbname}" mongo-ref="mongo"/>

    <!-- 去掉_class字段配置 -->
    <bean id="mappingContext"
          class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />

    <bean id="defaultMongoTypeMapper"
          class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
        <constructor-arg name="typeKey"><null/></constructor-arg>
    </bean>

    <bean id="mappingMongoConverter"
          class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
        <constructor-arg name="mappingContext" ref="mappingContext" />
        <property name="typeMapper" ref="defaultMongoTypeMapper" />
    </bean>

    <!-- mongodb的模板 -->
    <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongoDbFactory"/>
        <constructor-arg ref="mappingMongoConverter"/>
    </bean>
</beans>
编写实体bean

@id的注解是为了接收mongodb自动生成的id
@Indexed(expireAfterSeconds = 30)则是该条数据在30秒后自动删除,所以添加的索引,这里有个地方需要注意,数据类型必须是Date型,如果是long型,存的是时间戳,则时间到期后不会自动删除

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Date;

@Document(collection = "log")
public class LogBean {
    @Id
    public String id;
    public String name;
    @Indexed(expireAfterSeconds = 30)
    public Date createTime;
}

测试用例代码片段

@Autowired
MongoTemplate mongoTemplate;

//插入10条数据
List<LogBean> list = new ArrayList<>();
        for(int i=0;i<10;i++) {
            LogBean data = new LogBean();
            data.name=i+"";
            data.createTime = new Date(System.currentTimeMillis());
            list.add(data);
        }
mongoTemplate.insert(list,"log");

查询
Criteria criteria = new Criteria("name").is("3");
List<LogBean> listres = mongoTemplate.find(new Query(criteria), LogBean.class);

至此,算是把demo跑通了。

上一篇下一篇

猜你喜欢

热点阅读