mongodb

作者: 我已不是少年郎 | 来源:发表于2019-05-17 16:55 被阅读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跑通了。

    相关文章

      网友评论

          本文标题:mongodb

          本文链接:https://www.haomeiwen.com/subject/uvvsaqtx.html