[面试] 3. 关于mongodb和redis的面试题

本文章收录于:后端工程师面试题目总结(提供参考答案)

 

#MongoDB相关#

1、mongo的适用场景和不适用的场景


    适用场景:
        1. 存储大量的非结构化和半结构化数据
        2. 需要能够迅速水平扩展容量的db
        3. 高性能的实时数据插入/更新
    不适用:
        1. 需要事务控制的场景,比如银行和会计系统
        2. 需要复杂SQL语句的场景
        


2、mongo的简单查询、聚合查询使用

 

  •     大于小于:find( {'number':{$gt:9990,$lt:9999}} )
  •     all: find( {'number':{$all:[99,0]} )  #字段必须是数组
  •     exists: find{'number':{$exists:true}}) #是否存在该字段
  •     只显示某一字段: find( {"number":{$lt:10}},{'name':1} )
  •     update: update({'number':{$gt:9990}},    {$set:{'name':null}},  {'multi':true})
  •     分页:传入ps pn

          skip = ps * (pn - 1)
          coll.find(query_filter).sort(sort).limit(ps).skip(skip)

  •     聚合查询:

        db.login_info.aggregate([
            {
                '$match': {  #先查询出满足条件的数据
                            'user.login_time':{
                                                '$gte':-1,
                                                '$lte':1555555555555
                                            }
                        }
            },
            {   
                '$project':  # 对某个字段进行处理
                            {'hour':{  #dateToString转换只接受毫秒时间戳
                                        '$multiply':['$user.login_time',1000]
                                    }
                            }
     
            },
            {                # 再处理
                '$project': {'hour':{'$dateToString': { #format 提取时间单位,具体格式参见底下的图
                                                            format:'%H', 
                                    date: {'$add': [new Date(0),'$hour']}
                                                        }
                                        }
                                }
            },
            {                  #然后聚合
                '$group': {'_id':'$hour','count':{'$sum': 1}}
            },
            {               #再对需要输出的字段进行整理
                '$project':{'_id':0,'hour':'$_id','count':'$count'  }
            }
    ])

 

3、如何执行事务/加锁?


       MongoDB不支持事务和锁机制,同时也提高了它的性能。


4、mongodb中的(namespace)是什么?


    库名和集合名以点拼接起来的字符串。

 

#Redis相关#

 

1、什么场景用redis,为什么mysql不适合?

 

  •     1.1 缓存

        也就是热点数据的存储,通常是查询多,删改少的数据。redis性能远高于mysql
        

  •     1.2 计数器

        比如对点击数、点赞数的统计,这种场景对数据库操作频繁,对速率要求很高(毫秒级别),mysql不可行。
        

  •     1.3 位操作(大数据处理|去重)

        比如统计千万或上亿级别用户的在线状态,如果为每个用户建立一个key,那消耗的内存就太恐怖了,
        用mysql更不用考虑了,mysql完全不适用大数据处理。
        而redis支持bitmap操作,可以创建一个key,value是str,redis中一个str的最大长度是512MB,
        对应2^32-1个比特位,支持42亿个用户仅需512MB内存,然后使用offset作为一个唯一用户标识,
        value为0或1标识用户的在线与否。
        

  •     1.4 分布式锁

        比如对分布式服务中的某一个操作做并发限制,就可以设计让一个操作执行前往redis中
        执行set k1 v1 nx=true ex=10, 如果set成功,标识该操作可以执行,否则就要等待其他操作完成后再执行。
        执行完后delete这个key。ex参数用于防止死锁。
        

  •     1.5 队列

        使用List结构配合 lpush + rpop操作实现队列。 
    

  •     1.6 排行榜

        使用zset结构可以轻松是实现排行榜场景。
        
    小结:这些场景多利用redis的数据结构和内存高速读写的优势,mysql无法比拟。


    
2、谈redis的事务?用事务模拟原子+1操作?原子操作还有其它解决方案吗?

 

  •     2.1 redis的事务

        主要命令:
        MULTI: 开启事务
        EXEC:顺序执行事务队列中的命令
        DISCARD:放弃事务
        WATCH:监视一个key的,若改动前该key变动了,事务执行中断。
        UNWATCH:取消对所有key的监视
        
        几个特性:
            1. 命令进入事务队列时,会检查命令拼写的正确性,但不会检查命令用法的正确性。
            2. 单条命令具有原子性,单个事务不具有完整的原子性(当事务后面部分的命令用法错误时,前面有效的命令仍然执行成功;而当所有事务内命令用法都正确时,可以利用watch命令实现原子性,即可回滚)

  •     2.2 用事务模拟原子+1操作?

        依次输入(假设key_1已存在):
            watch key_1 #必须在事务开始前监视要改动的key
            multi
            incr key_1
            exec #若key被改动,事务执行失败,key不会加一

  •     2.3 其他原子操作解决方案

        使用互斥锁,即改值的顺序变为: 获得锁-->改值-->释放锁。(这是悲观锁模型)

 

3、 redis内存满了会怎么样?


    很明显,满了就无法再写入新key,那怎么解决呢?

  •     3.1 不差钱,加内存。
  •     3.2 使用内存淘汰策略,即按规则删除旧数据。

        规则有六种,重点说LRU相关的两条。
        1. volatile-lru 使用LRU算法删除一个键(只对设置了生存时间的键)
        2. allkeys-lru 使用LRU算法删除一个键
        LRU:Least RecentlyUsed 最近最少使用算法,即删除最近最少使用的key。
        可能需要你手写LRU算法。

  •     3.3 对于内存需求较大的话,就需要redis集群了,此处不细讲。

4、 和memcache区别?


    4.1 mem数据类型仅支持字符串
    4.2 mem是多线程服务模式,redis是单线程模式。
    4.3 mem不支持持久化,redis支持。

5、redis为何是单线程模型?

    5.1 键值存储结构,大部分操作的时间复杂度为O(1),这点决定了查找不需要什么CPU计算。(但是LSET/HGETALL这些是O(n)复杂度操作,因为单线程,它会阻塞别的操作,要慎用)

    5.2 单线程没有多线程多进程带来的上下文切换以及同步问题,同步会导致很大的性能损耗

    5.3 使用的多路IO复用模型(epoll),这是一种高性能IO处理模型

    5.4 如何利用多核CPU:一台服务器上启动多个实例(等于CPU个数),每个实例绑定一个CPU,不让操作系统调度选择哪个CPU,这样就避免了切换开销。

6、   redis的aof文件太大怎么办?


    a.手动执行redis命令:bgrewriteaof, 执行后,AOF文件重写操作会被预定,待redis空闲时会执行。
    b.修改配置自动触发重写操作。
        #代表当前AOF文件空间和上次重写后AOF空间的比值。
        auto-aof-rewrite-percentage 100
        #AOP超过10m就开始收缩
        auto-aof-rewrite-min-size 10mb
        说明:当AOF文件第一次达到10mb时就开始进行文件重写,下一次重写的文件大小是上一次的一倍即20mb,再下次就是40mb。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值