1.工厂模式

简单工厂模式
包含如下角色:
Factory:工厂角色-负责实现创建所有实例的内部逻辑.
Product:抽象产品角色-是所创建的所有对象的父类,负责描述所有实例所共有的公共接口。
ConcreteProduct:具体产品角色-是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
设计模式 - 图1
(2)优缺点
优点:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。
缺点:是当产品修改时,工厂类也要做相应的修改。
(1)工厂方法模式结构
工厂方法模式包含如下角色:

  • Product:抽象产品
  • ConcreteProduct:具体产品
  • Factory:抽象工厂
  • ConcreteFactory:具体工厂
  • 设计模式 - 图2

    工厂模式总结

    (1)适用场景
    输出的产品是标准品,谁来做都可以。
    (2)举例
    常见的数据库连接工厂,SqlSessionFactory,产品是一个数据库连接,至于是oracle提供的,还是mysql提供的,我并不需要关心,因为都能让我通过sql来操作数据。
    3)注意事项
    项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,增加了代码的复杂度,增加了调用层次,增加了内存负担。所以要注意防止模式的滥用。
    优点:
    一个调用者想创建一个对象,只要知道其名称就可以了。
    扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
    屏蔽产品的具体实现,调用者只关心产品的接口。
    缺点:
    每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
    工厂模式的使用范围
    1当一个类不知道它所必须创建的对象的类的时候。
    2.当一个类希望由它的子类来指定它所创建的对象的时候。
    3.当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候

    工厂方法举例

    //利用多态去解耦合 抽象工厂和公共接口不需要改变,只需加实现类就OK了

设计模式 - 图3
设计模式 - 图4
设计模式 - 图5

工厂方法的优缺点
优点
工厂方法模式把具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则
更符合开-闭原则,新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可,抽象工厂和公共接口不需要改变,只需加实现类就OK了
符合单一职责原则,每个具体工厂类只负责创建对应的产品
符合接口隔离原则 ,
4.依赖倒置原则(DIP)
定义:高层模块不应该依赖低层模块,两个都应该依赖于抽象。
5.迪米特原则(LOD)
定义:一个软件实体应当尽可能少地与其他实体发生相互作用。
3.里氏替换原则(LSP)
里氏代换原则是实现开闭原则的重要方式之一
子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法
工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。
缺点
添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
一个具体工厂只能创建一种具体产品

Spring中的工厂模式

工厂模式重点就是适用于 构建同产品类型(同一个接口 基类)的不同对象时,这些对象new很复杂,需要很多的参数,而这些参数中大部分都是固定的,so,懒惰的程序员便用工厂模式封装之。
设计模式 - 图6

2.单例模式

单例模式包含如下角色:

  • Singleton:单例

优缺点

  • 优点:全局只有一个实例,便于统一控制,同时减少了系统资源开销。
  • 缺点:没有抽象层,扩展困难。

应用场景
适合需要做全局统一控制的场景,例如:全局唯一的编码生成器。
注意事项
只对外提供公共的getInstance方法,不提供任何公共构造函数。
windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
网站的计数器,一般也是采用单例模式实现,否则难以同步。
应用程序的日志应用,一般都用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
设计模式 - 图7
単例模式实现了,那Springboot为什么要用単例模式来创建我们的业务层实例呢?
1.在程序中这些类是被频繁使用的,让它们保持在内存中,避免了频繁创建、销毁的性能损耗(当然除了业务类,日志类,配置类等都是单例的)。
2.内存中只有一个实例,这样请求过来就会快速的找到有且仅有的一个,节省内存、避免对资源的多重占用和便于管理。

spring中的单例模式

向一级缓存中加入单例对象,同时,移除二级缓存,三级缓存中的单例对象。
并向注册登记表registeredSingletons中,记录单例的名称beanName。由于容器对象都是map对象,所以能自动保存通一个beanName保存的对象唯一。
查阅资料,发现是不可以的,因为map是无序的,它的查询需要通过key的值来查找,如果你定义两个同样的key,那么一个key就对应了多个值,这样就违背了java对map的定义,键和值是一一对应的。所以key不可以重复。

②.SqlSessionFactiory是单例的,作用就是创建SqlSession,每次访问数据库都需要一个SqlSession,所以SqlSessionFactiory的生命周期是贯穿整个Mybatis生命周期的,SqlSessionFactiory采用单例的原因是减少数据库连接资源。
设计模式 - 图8

三级缓存
通过分析源码:
单例的获取顺利是singletonObjects ——》earlySingletonObjects ——》singletonFactories 这样的三级层次。
我们发现,在singletonObjects 中获取bean的时候,没有使用synchronized关键字,而在singletonFactories 和earlySingletonObjects 中的操作都是在synchronized代码块中完成的,正好和他们各自的数据类型对应,singletonObjects 使用的使用ConcurrentHashMap线程安全,而singletonFactories 和earlySingletonObjects 使用的是HashMap,线程不安全。
从字面意思来说:singletonObjects指单例对象的cache,singletonFactories指单例对象工厂的cache,earlySingletonObjects指提前曝光的单例对象的cache。以上三个cache构成了三级缓存,Spring就用这三级缓存巧妙的解决了循环依赖问题。

单例模式的线程安全问题!!

首先看是否为全局变量
然后看有没有写操作,如果都有就是线程不安全的
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

有几种解决方法:
1、在Controller中使用ThreadLocal变量2、在spring配置文件Controller中声明 scope=”prototype”,每次都创建新的controller
设计模式 - 图9
设计模式 - 图10

Spring 框架中用到了哪些设计模式?

关于下面一些设计模式的详细介绍,可以看笔主前段时间的原创文章《面试官:“谈谈Spring中都用到了那些设计模式?”。》 。
工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
代理设计模式 : Spring AOP 功能的实现。
单例设计模式 : Spring 中的 Bean 默认都是单例的。

Spring 中的 bean 生命周期?

还有上边的spring工厂模式中的图片一起了解
Bean 容器找到配置文件中 Spring Bean 的定义。
Bean 容器利用 Java Reflection API 创建一个Bean的实例。
如果涉及到一些属性值 利用 set()方法设置一些属性值。
如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。
如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
如果Bean实现了 BeanFactoryAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoade r对象的实例。
与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。
如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法
当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

1 代理模式(Proxy)

具体细节相关实际博客可参考
(75条消息) java设计模式—代理模式—三种代理方式的区别_qq_27886997的博客-CSDN博客
代理模式结构
代理模式包含如下角色:

  • Subject: 抽象接口
  • Proxy: 代理对象
  • RealSubject: 目标对象

设计模式 - 图11
代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。(比如,同学代买火车票,寻求律师打官司)
优缺点

  • 优点:代理可以协调调用方与被调用方,降低了系统的耦合度。根据代理类型和场景的不同,可以起到控制安全性、减小系统开销等作用。
  • 缺点:增加了一层代理处理,增加了系统的复杂度,同时可能会降低系统的相应速度。

符合: 开放封闭原则(ASD)
里氏代换原则:
所有引用基类(父类)的地方必须能透明地使用其子类的对象
单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
接口隔离原则:使用多个专门的接口
静态代理总结:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
一个原类与一个代理类要一一对应。如果多个目标类就要相应的创建多个代理类,维护不方便
两者都实现共同的接口或继承相同的抽象类;
只是在代理类中,实例化原类,在原类方法的前后添加新的逻辑。
2.缺点:
如果多个目标类就要相应的创建多个代理类,维护不方便
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式
设计模式 - 图12
设计模式 - 图13
设计模式 - 图14
动态代理
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理 jdk动态代理是由java内部的反射机制来实现的
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
总结:代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
设计模式 - 图15
设计模式 - 图16
设计模式 - 图17

为何jdk动态代理一定要实现接口?

因为代理类生成的class文件,可以明显的看到class文件已经继承了Proxy,所以不能继承目标对象,只能实现目标对象,所以jdk动态代理是基于接口而不是继承实现的。
执行速度较慢

适用场景

如果你的程序需要频繁、反复地创建代理对象,则JDK动态代理在性能上更占优。

Cglib代理:可以代理类

但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
cglib实现动态代理的逻辑是使用子类继承代理类,就没有单继承的限制了。
Cglib子类代理实现方法:
由于是动态生成字节码实现代理,因此代理对象的执行速度较快, 约为JDK动态代理的1.5 ~ 2倍
可以代理没有实现接口的对象

1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

适用场景

不需要频繁创建代理对象的应用,如Spring中默认的单例bean,只需要在容器启动时生成一次代理对象。
设计模式 - 图18
创建一个子类去继承你创建的类,然后反射invoke去重写扩展子类,实现扩展功能

注意:单例bean与单例模式不是一个概念,只是相同的名字可以拿到同一个对象

设计模式 - 图19
那有没有一种方案,既不需要跟Controller耦合,也可以将定义的 异常处理器 应用到所有控制器呢?所以注解@ControllerAdvice出现了,简单的说,该注解可以把异常处理器应用到所有控制器,而不是单个控制器。借助该注解,我们可以实现:在独立的某个地方,比如单独一个类,定义一套对各种异常的处理机制,然后在类的签名加上注解@ControllerAdvice,统一对 不同阶段的、不同异常 进行处理。这就是统一异常处理的原理。
注意到上面对异常按阶段进行分类,大体可以分成:进入Controller前的异常 和 Service 层异常,具体可以参考下图:
这样一来,之前缺点:就必须在每一个Controller类都定义一套这样的异常处理方法,因为异常可以是各种各样。这样一来,就会造成大量的冗余代码,而且若需要新增一种异常的处理逻辑,就必须修改所有Controller类了,很不优
个人理解:
ResponseBodyAdvice 接口是在 Controller 执行 return 之后,在 response 返回给客户端之前,执行的对 response 的一些处理,可以实现对 response 数据的一些统一封装或者加密等操作。
对controller返回的数据进行增强,加入了错误码,错误类型,成功或失败,返回json数据供前端解析,用map集合接收!
该接口一共有两个方法:
(1)supports —— 判断是否要执行beforeBodyWrite方法,true为执行,false不执行 —— 通过supports方法,我们可以选择哪些类或哪些方法要对response进行处理,其余的则不处理。
(2)beforeBodyWrite —— 对 response 处理的具体执行方法。
设计模式 - 图20
SpringAop默认是使用Jdk动态代理的方式实现Aop,如果满足上列任意一个条件,则使用Cglib的
什么时候使用Cglib代理的方式呢?
(1) 满足下列3个条件之一
① optimize标志已设置(也就是为true)
② 设置proxyTargetClass(目标代理类)标志
③ 没有指定代理接口
上述三点在标题一中有详解
(2) 同时满足下列两个条件
① 要代理的目标类不是接口
② 要代理的目标类不是代理类

SpringAOP应用场景

定义全局异常使用切面类加日志记录

设计模式 - 图21

观察者模式

(一)什么是观察者模式(相当于实时监控)
观察者模式就如一个聊天室,当你需要收到聊天室的消息时,你就注册成为聊天室的成员,当聊天室有信息更新时,就会传到你那去。当你不需要接收聊天室的信息时,可以注销掉,退出聊天室。
又例如,天气观测站和气象报告板的关系。但报告板想获取观测站的数据,可以注册加入到观测站的观察者列表中,这就可以使观测站有数据更新时,自动传给气象报告板。

六大原则

六大设计原则的作用:解耦合和增加高扩展性!!!
尤其是线上的项目!!
1. 单一职责原则(SRP)
定义:就一个类而言,应该仅有一个引起它变化的原因。
从这句定义我们很难理解它的含义,通俗讲就是我们不要让一个类承担过多的职责。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。解耦合!
2. 开放封闭原则(ASD)
定义:类、模块、函数等等等应该是可以拓展的,但是不可修改。
开放封闭有两个含义,一个是对于拓展是开放的,另一个是对于修改是封闭的。
也就是不修改源代码而修改子类或者接口

3.里氏替换原则(LSP)
里氏代换原则是实现开闭原则的重要方式之一
子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里氏代换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。
我们在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,

4.依赖倒置原则(DIP)
定义:高层模块不应该依赖低层模块,两个都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
在Java中,抽象就是指接口或者抽象类
5.迪米特原则(LOD)
定义:一个软件实体应当尽可能少地与其他实体发生相互作用。
也称为最少知识原则。如果一个系统符合迪米特法则,那么当其中某一个模块发生修改时,就会尽量少地影响其他模块,。迪米特法则可降低系统的耦合度,使类与类之间保持松散的耦合关系。
6.接口隔离原则(ISP)
定义:一个类对另一个类的依赖应该建立在最小的接口上。
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。 接口细化单一!!

我的技术选型

为何用Redis?
NoSql(非关系型数据库)
1.访问速度要快,不然我直接去数据库查岂不是更好,毕竟就是为了提升某些常态化数据的2.提取速度和减轻数据库压力,才去使用的缓存模块。
3.支持的存储方式要符合多方面的系统需要,比如不同语言编写的系统交互,是不是要去查询同一个缓存。Springboot支持
4.要比单纯读数据库更加节约成本。
5.能保存的数据量要够多,这样才能放得下足够多的缓存,不然我放上十条八条的数据就满了,这成什么样子。
6.开源,c或c++;有问题可以去源码
mysql 单机支撑到 2000QPS就容易报警了
为什么不用 MongoDB 呢?(存储结构是树)
设计模式 - 图22
首先 Redis 完全符合上面我们说的几个特征,而且还是基于内存、可持久化的数据库,并且是非常友好的 Key-Value 键值对的方式存储。
而 MongoDB 数据库作为非结构化的文件数据库,其功能和特性并不能支持大多数的缓存数据的存放,只会徒劳增加成本。存放数据结构更复杂,没必要
存储量更大
MongoDB 的更多使用方式还是存储一些大文件之类的数据,不是专注于去实现一些缓存数据的存储。
Redis 就不一样了,他本身就是应运而生的,除了做缓存数据库虽然还能去实现消息队列之类的功能,但是那都是一些附属价值;Redis 最核心的价值还是缓存数据库,多种数据结构、
数据结构高效优化!!
设计模式 - 图23

Mongdb有bug容易,MongoDB:主要解决海量数据的访问效率问题
为什么要用 redis 而不用 map/guava 做缓存?
缓存分为本地缓存和分布式缓存。以 Java 为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特点是轻量以及快速,生命周期随着 jvm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
使用 redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持 redis 或 memcached服务的高可用,整个程序架构上较为复杂。

1.3.2 Redis为什么没有“锁”

  在关系型数据库中,会通过加锁来保证数据的一致性,这种锁被称为悲观锁。Redis为了近可能的减少客户端等待,使用WATCH命令对数据加锁,只会在数据被其他客户端修改时,才会通知执行WATCH的客户端,之后的事务不会执行。这种加锁方式被称为乐观锁,极大的提升了Redis的性能
2、Redis和memcache应该如何选择?
分布式缓存
redis 和 memcached 的区别
(1)对数据类型的支持
redis 支持丰富的数据结构,String,list,map,set,sorted set等。redis 相比 memcached 来说,拥有更多的数据类型,能支持更丰富的数据操作。如果需要通过缓存来支持复杂的数据结构和操作, redis 是很好的选择。
(2)对集群的支持(高可用性)
redis在 redis3.x 版本中便能支持cluster模式,而 memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。
(3)性能对比(性能略低但是线程安全,只考虑分布式事务就OK了)
由于 redis 只使用单核,而 memcached 可以使用多核,所以平均每一个核上 redis 在存储小数据时比 memcached 性能更高。而在 100k 以上的数据中,memcached 性能要高于 redis。虽然 redis 也在存储大数据的性能上进行优化,但是比起 memcached,还是稍有逊色。
A. Redis支持五种数据类型,而memcached只有简单的KV数据
B. 用Redis可以实现分布式锁机制
C. Redis支持持久化,memcache不支持持久化
D. Redis单线程,memcache多线程
E. Redis有事务机制,memcache不支持事务

与mongodb对比

设计模式 - 图24
没有事务,偶尔有两个没有也无所谓
设计模式 - 图25
1.什么是MongoDB
MongoDB:是一个数据库 ,高性能、无模式、文档性,目前nosql中最热门的数据库
,开源产品,基于c++开发。是nosql数据库中功能最丰富,最像关系数据库的。
2.MongoDB特性:
 面向集合文档的存储:适合存储Bson(json的扩展)形式的数据相当于比json多了些语法,可以类似于json;比如数值:shell默认使用64为浮点型数值。{“x”:3.14}或{“x”:3}。对于整型值,可以使用 NumberInt(4字节符号整数)或NumberLong(8字节符号整数{“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)} 格式自由,数据格式不固定,生产环境下修改结构都可以不影响程序运行;
强大的查询语句,面向对象的查询语言,基本覆盖sql语言所有能力;
完整的索引支持,支持查询计划;
支持复制和自动故障转移;
支持二进制数据及大型对象(文件)的高效存储;
使用分片集群提升系统扩展性;
使用内存映射存储引擎,把磁盘的IO操作转换成为内存的操作;
向用户的,用户使用 MongoDB 开发应用程序使用的就是逻辑结构。
(1)MongoDB 的文档(document),相当于关系数据库中的一行记录。
2)多个文档组成一个集合(collection),相当于关系数据库的表。
(3)多个集合(collection),逻辑上组织在一起,就是数据库(database)。
4)一个 MongoDB 实例支持多个数据库(database)。
相当主键,名字设成下划线 _id
Use 数据库名字,直接创建并使用
数据结构很松散
文档(document)、集合(collection)、数据库(database)的层次结构如下图:
db.spit.insert({_id:”1”,content:”我还是没有想明白到底为啥出
错”,userid:”1012”,nickname:”小明”,visits:NumberInt(2020)});
db.spit.insert({_id:”2”,content:”加班到半夜”,userid:”1013”,nickname:”凯
撒”,visits:NumberInt(1023)});
db.spit.insert({_id:”3”,content:”手机流量超了咋
办?”,userid:”1013”,nickname:”凯撒”,visits:NumberInt(111)});
db.spit.insert({_id:”4”,content:”坚持就是胜利”,userid:”1014”,nickname:”诺
诺”,visits:NumberInt(1223)});
指定表名称
设计模式 - 图26
注意:
id主键 + 内容 + uerid:序号 +name:用户名 形成集合
注意要转成同一个类型 {“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}
Java封装的查询条件,大于就是 “$gt” : 1000
设计模式 - 图27
MongoDB 是一个跨平台的,面向文档的数据库,是当前 NoSQL 数据库产品中最热门的一种。
3.MongoDB的不足:
 MongoDB对事物的支持较弱: 高度事务性系统,例如银行、财务等系统不适合。
 涉及到复杂 的、高度优化的查询方式:传统的商业智能应用,特定问题的数据分析,多数据实体关联等不适合;
 数据结构相对固定,使用关系型数据库更好合理,使用sql进行查询统计更加便利的 时候等不适合;
3.1 希望速度快的时候,选择mongodb或者redis
3.2 数据量过大的时候,选择频繁使用的数据存入redis,其他的存入mongodb
3.3 mongodb不需要提前建数据库建表,使用比较方便,字段数量不确定的时候使用mongodb
json格式的直接写入方便。(如日志之类) json的存储格式。mongodb的json与bson格式很适合文档格式的存储与查询。
文档是mongoDB中数据的基本单元,类似关系数据库的行,多个键值对有序地放置在一起便是文档,语法有点类似javascript面向对象的查询语言,它是一个面向集合的,模式自由的文档型数据库

数据处理:

数据是存储在硬盘上的,只不过需要经常读取的数据会被加载到内存中,将数据存储在物理内存中,从而达到高速读写。
缺点:
不支持事务,而且开发文档不是很完全,完善

Redis数据结构详解

1)String
常用命令:set/get/decr/incr/mget等;
应用场景:String是最常用的一种数据类型,普通的key/value存储都可以归为此类;
实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

2)Hash
常用命令:hget/hset/hgetall等
应用场景:我们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,通过用户ID我们希望获取该用户的姓名或者年龄或者生日;
实现方式:Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。如图所示,Key是用户ID, value是一个Map。这个Map的key是成员的属性名,value是属性值。这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据。当前HashMap的实现有两种方式:当HashMap的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,这时对应的value的redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

3)List
常用命令:lpush/rpush/lpop/rpop/lrange等;
应用场景:Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;
实现方式:Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

4)Set
常用命令:sadd/spop/smembers/sunion等;
应用场景:Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;
实现方式:set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
(4)set
set 是无序集合,自动去重。直接基于 set 将系统里需要去重的数据扔进去,自动就给去重了,如果需要对一些数据进行快速的全局去重,你当然也可以基于 jvm 内存里的 HashSet 进行去重,但是如果你的某个系统部署在多台机器上呢?得基于 redis 进行全局的 set 去重。

5)Sorted Set
常用命令:zadd/zrange/zrem/zcard等;
应用场景:Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
实现方式:Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。
设计模式 - 图28