Java面试题 非关系型数据库nosql

9 篇文章 0 订阅

四:非关系型数据库(nosql)

1:Redis

(1)Redis介绍
是一个高性能的(key/value)分布式内存数据库,基于内存运行并支持内存的NoSql数据库之一,数据结构服务器。
(2)特点
Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据。同时提供了String,list,set,zset,hash等数据结构的存储
Redis支持数据的备份,主从。

(3)能干什么
内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务。
取最新的N条数据
(4)知识讲解
1:单进程
2:默认16个数据库,类似数组下标从零开始,初始默认使用零号库
3:select命令切换数据库
4:dbsize查看当前数据库的key的数量;
5:flushall:通杀全部库
6:统一密码管理,16个库都是同样的密码
7:Redis索引从0开始
8:默认端口号为6379
(5)数据类型

1):五大数据类型

String(字符串)
Hash (哈希,类似java中的map)
List (列表):链表
Set (集合)
Zset (sorted set:有序集合)

2):Redis常用命令

1:keys * :查看所有的key
2:exists key的名字。如果返回1则key存在,返回0则key不存在;
3:move key db :移动到其他库(move k3 2:将k3移动到2号索引的库)
4:expire key 秒钟:为给定的key设置过期时间;
5:ttl key :查看还有多少秒过期,-1表示永不过期。-2表示已过期
6:type key :查看你的key是什么类型

3):Redis字符串(String)

常用命令:
(1)set/get/del/append/strlen
赋值/取值/删除/拼接/长度
(2)incr/decr/incrby n/decrby n(要求是数字类型)
+1/ -1/ +n/ -n
(3)getrange/setrange
区间查询
getrange k1 0 2 获取k1的值,从0到2
setrange k1 0 xx 赋值k1的值 ,从0开始替换两位内容为xx
(4)setex 键秒值:添加并设置过期时间。
例:setex k1 10 v1 设置k1 的值为v1,存在时间为10秒
(5)setnx (set if not exist )
set k1 aaa;
Set k1 bbb;如果k1已经存在,再次赋值则之前的值覆盖
setnx k1 ccc; 使用该命令,会先进行判断是否为null,如果不为null则不赋值;
(6)mset/mget/msetnx
mset k1 v1 k2 v2 k3 v3:赋值多个
mget k1 k2 k3 :获取多个值
msetnx :设置多个是进行判断,只有一个key已经存在,则全部不能赋值成功。
应用场景:
String是最常用的一种数据类型,普通的key/ value 存储都可以归为此类.即 可以完全实现目前 Memcached 的功能,并且效率更高。还可以享受Redis的定时持久化,操作日志及 Replication等功能。
实现方式:
String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

4):redis 列表(list)

常用命令
(1)lpush/rpush/lrange
lpush list1 1 2 3 :给list1赋值为1、2、3
rpush list2 1 2 3 :给list2赋值为1、2、3
list1和list2都是赋值,只是赋值的顺序不同,rpush是正序的赋值,lpush是反序的赋值;
lrange list1 0 n :取出list1中0到n的值;如果n为-1,则查询全部。
(2)lpop/rpop
lpop:移除第一个的值
rpop:移除最后一个的值
(3)lindex
按照索引下标获得元素(从上到下)
(4)llen
获取list的长度
(5)lrem key 删除N个value
lrem list3 2 3:在list3集合中删除2个3;
(6)ltrim key 开始index 结束index,截取指定范围的值后再赋值给key;
(7)lset key index value
例:lset list1 1 x;将key为list1的集合的1下标值设为x;
(8)linsert key before/after 值1 值2
在key的值1之前/之后插入值2。
应用场景
Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。

Lists 就是链表,相信略有数据结构知识的人都应该能理解其结构。使用Lists结构,我们可以轻松地实现最新消息排行等功能。Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作Lists中某一段的api,你可以直接查询,删除Lists中某一段的元素。
实现方式
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

5):redis 集合(set)

常用命令
(1)sadd/smembers/sismember
赋值/查看set集合的值/判断值是否存在集合中
(2)scard:获取集合里面的元素个数
(3)srem key value 删除集合中元素
(4)srandmember key n(随机出n个数)
(5)spop key:随机出栈
应用场景
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
Sets 集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构,可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。
实现方式
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

6):redis 哈希 (hash)

常用命令
(1)hset/hget/hmset/hmget/hgetall/hdel
赋值/取值/赋多个值/取多个值/去所有值/删除指定的值
(2)hlen 长度
(3)hexists key 在key中是否存在某个key(返回1则存在,0不存在)
(4)hkeys/hvals
hkeys:获取所有key
hvals:获取所有value
(5)hsetnx
赋值之前进行判断,只有不存在才能赋值
应用场景
在Memcached中,我们经常将一些结构化的信息打包成HashMap,在客户端序列化后存储为一个字符串的值,比如用户的昵称、年龄、性别、积分等,这时候在需要修改其中某一项时,通常需要将所有值取出反序列化后,修改某一项的值,再序列化存储回去。 这样不仅增大了开销,也不适用于一些可能并发操作的场合 (比如两个并发的操作都需要修改积分)。而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。
我们简单举个实例来描述下Hash的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息:用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:
第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
那么Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口,也就是说,Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题。
这里同时需要注意,Redis提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到遍历整个内部Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。
实现方式
上面已经说到Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

7):redis 有序集合 zset(sorted set)

zadd,zrange,zrem,zcard等
应用场景
Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
另外还可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
实现方式
Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

(6) 持久化

1):rdb(Redis DataBase)

1:介绍
介绍:在指定的时间间隔内将内存中的数据集快照写入磁盘,(snapshot快照),它恢复时是将快照文件直接读到内存里。
Redis会单独创建一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束时,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据恢复,且对于数据恢复的完整性不是非常敏感,那rdb方式要比AOF方式更加高效,RDB的缺点是最后一次持久化后的数据可能丢失。
2:默认保存的文件是:dump.rdb文件
3:默认的保存策略

4:可以通过save命令实时保存数据。

2):aof(Append Only File)

1:介绍
以日志的形式来记录每个写的操作;将redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话,就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
2: aof保存的是appendonly.aof文件
3:appendonly no:默认是关闭的。修改为yes;
4:dump.rdb和appendonly.aof
可以同时存在,先会加载appendonly.aof文件。
5:注意
如果出现断电等特殊情况,appendonly.aof文件中可能会出现错误的内内容,
当redis启动时,会先加载appendonly.aof文件,但是文件内容错误,则造成的结果就是启动失败。
可以通过下面的方法先将appendonly.aof文件进行清理,然后再启动、
E:\mingrui\sx\redis\Redis-x64-3.2.100\redis-check-aof.exe --fix C:\Users\Administrator\Desktop\appendonly.aof
6:持久化策略
(1)Appendfsync
i.always:同步持久化,每次发生数据变更会被立即记录到磁盘中,性能较差但数据完整性比较好。
ii.everysec:出厂默认推荐,异步操作,每秒记录,如果一秒内宕机,有数据丢失
iii.no:不适用
7:重写rewrite
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的值时,redis就会启动AOF文件的内容压缩。只保留可以恢复数据的最小指令。
重写的触发机制:
Redis会记录上次重写时的aof大小,默认配置是当aof文件大小是上次rewrite后大小的一倍,且文件大于64M是触发。

8:缺点
相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢与rdb;
Aof运行效率要慢与rdb,每秒同步策略效率较好,不同步效率和rdb相同。
3):总结
RDB持久化方式能够在指定的时间间隔能对你的数据记性快照存储。
AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候,会重新执行哪些命令来恢复原始的数据,AOF命令一redis协议追加保存每次写的操作到文件末尾,redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
只做缓存,如果你值希望你的数据在服务器运行的时候存在,你也可以不适用任何持久化的方式。
同时开启两种持久化的方式:第一、这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整,第二、RDB的数据不实时,同时使用两者服务器重启也只会找AOF文件,那要不要只使用aof呢?建议不要,因为RDB更适合用于备份数据库(aof在不断变化不好备份),快速重启,而且不会有AOF可能存在的bug。建议两者都开启。

(7)事务

(1)介绍
可以一次执行多个命令,本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序的串行化执行而不会被其它命令插入,不许加塞。
(2)作用:
一个队列中,一次性,顺序性,排他性的执行一系列命令。
(3)常用命令:
1)MULTI:开启事务块。
2)EXEC :提交事务
3)DISCARD :放弃事务

(8)使用redis有哪些好处?

(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
(5)性能极高 Redis能读的速度是110000次/s,写的速度是81000次/s

(9)redis相比memcached有哪些优势?

(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
(2) redis的速度比memcached快很多
(3) redis可以持久化其数据
(10)redis常见性能问题和解决方案
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

(11)mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据

相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:
voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据

(12)Memcache与Redis的区别都有哪些?

1)、存储方式
Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis有部份存在硬盘上,这样能保证数据的持久性。
2)、数据支持类型
Memcache对数据类型支持相对简单。
Redis有复杂的数据类型。
3)、使用底层模型不同
它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。
Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4),value大小
redis最大可以达到1GB,而memcache只有1MB

(13)高可用分布式集群

1)高可用
高可用(High Availability),是当一台服务器停止服务后,对于业务及用户毫无影响。 停止服务的原因可能由于网卡、路由器、机房、CPU负载过高、内存溢出、自然灾害等不可预期的原因导致,在很多时候也称单点问题。
2)解决单点问题主要有2种方式
主备方式
这种通常是一台主机、一台或多台备机,在正常情况下主机对外提供服务,并把数据同步到备机,当主机宕机后,备机立刻开始服务。 Redis HA中使用比较多的是keepalived,它使主机备机对外提供同一个虚拟IP,客户端通过虚拟IP进行数据操作,正常期间主机一直对外提供服务,宕机后VIP自动漂移到备机上。
优点是对客户端毫无影响,仍然通过VIP操作。缺点也很明显,在绝大多数时间内备机是一直没使用,被浪费着的。
主从方式
这种采取一主多从的办法,主从之间进行数据同步。 当Master宕机后,通过选举算法(Paxos、Raft)从slave中选举出新Master继续对外提供服务,主机恢复后以slave的身份重新加入。主从另一个目的是进行读写分离,这是当单机读写压力过高的一种通用型解决方案。 其主机的角色只提供写操作或少量的读,把多余读请求通过负载均衡算法分流到单个或多个slave服务器上。
缺点是主机宕机后,Slave虽然被选举成新Master了,但对外提供的IP服务地址却发生变化了,意味着会影响到客户端。 解决这种情况需要一些额外的工作,在当主机地址发生变化后及时通知到客户端,客户端收到新地址后,使用新地址继续发送新请求。
3)数据同步
无论是主备还是主从都牵扯到数据同步的问题,这也分2种情况:
同步方式:
当主机收到客户端写操作后,以同步方式把数据同步到从机上,当从机也成功写入后,主机才返回给客户端成功,也称数据强一致性。 很显然这种方式性能会降低不少,当从机很多时,可以不用每台都同步,主机同步某一台从机后,从机再把数据分发同步到其他从机上,这样提高主机性能分担同步压力。 在Redis中是支持这杨配置的,一台master,一台slave,同时这台salve又作为其他slave的master。
异步方式:
主机接收到写操作后,直接返回成功,然后在后台用异步方式把数据同步到从机上。 这种同步性能比较好,但无法保证数据的完整性,比如在异步同步过程中主机突然宕机了,也称这种方式为数据弱一致性。
Redis主从同步采用的是异步方式,因此会有少量丢数据的危险。还有种弱一致性的特例叫最终一致性,这块详细内容可参见CAP原理及一致性模型。
4)Redis哨兵机制
为了达到redis的高可用,有两种部署方式:主从复制+哨兵机制;集群模式。哨兵机制是redis2.8开始支持。集群模式是redis3.0开始支持。
有了主从复制的实现以后,如果想对主服务器进行监控,那么在redis2.6以后提供了一个"哨兵"的机制。顾名思义,哨兵的含义就是监控Redis系统的运行状态。可以启动多个哨兵,去监控Redis数据库的运行状态。其主要功能有两点:
监控主数据库和从数据库是否正常运行。
数据库出现故障时,可以自动将从数据库转换为主数据库,实现自动切换。
5)方案选择
keepalived方案配置简单、人力成本小,在数据量少、压力小的情况下推荐使用。 如果数据量比较大,不希望过多浪费机器,还希望在宕机后,做一些自定义的措施,比如报警、记日志、数据迁移等操作,推荐使用主从方式,因为和主从搭配的一般还有个管理监控中心。

6)分布式
分布式(distributed), 是当业务量、数据量增加时,可以通过任意增加减少服务器数量来解决问题。
7)集群时代
至少部署两台Redis服务器构成一个小的集群,主要有2个目的:
高可用性:在主机挂掉后,自动故障转移,使前端服务对用户无影响。
读写分离:将主机读压力分流到从机上。可在客户端组件上实现负载均衡,根据不同服务器的运行情况,分担不同比例的读请求压力。

8)总结
分布式缓存再向后是云服务缓存,对使用端完全屏蔽细节,各应用自行申请大小、流量方案即可,如淘宝OCS云服务缓存。分布式缓存对应需要的实现组件有:
一个缓存监控、迁移、管理中心。
一个自定义的客户端组件。
一个无状态的代理服务。
N台服务器。

(14)为什么redis需要把所有数据放到内存中?

Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天,redis将会越来越受欢迎。
如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。

(15)Redis如何进行模糊查询?

redis中允许模糊查询的有3个通配符,分别是:*,?,[]
*:通配任意多个字符
?:通配单个字符
[]:通配括号内的某一个字符

(16)Redis集群方案什么情况下会导致整个集群不可用?

有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。

(17)说说Redis哈希槽的概念?

Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。

(18)在项目中如何使用?

第一种方式:
前提: 使用ssm+maven框架.
重点:加入redis技术点.
1.使用jedis操作redis,首先在pom文件中引入jedis的坐标,下载相关依赖的jar包.
2.开启redis服务.导入jedis接口和jedispool实现类.
3.在resources中添加redis.xml文件
通过连接池链接redis
在标签内,通过构造函数,添加属性 host:ip
port:端口号
通过spring创建 JedisClientPool 对象

4.在service业务处理层注入jedispool,在查询方法中查询redis是否存在数据,如果存在数据,则返回数据,如果数据不存在,则去查询数据库,将查询到的数据,放到redis中.(注意:redis中的数据存放是Key,value的形式).最后将查找到的对象返回.
第二种方式:
前提:使用springboot框架.
重点:加入redis技术点.
1.在pom文件中引入redis的坐标,下载相关的jar包.
2.在appliaction.properties中定义redis所需要的配置文件spring.redis.port=6379 spring.redis.host=127.0.0.1
3.在springboot自带的启动类中.添加开启缓存注解@EnableCaching
4.在sevice业务处理层的方法中添加
在查询方法中添加
@Cacheable(value = “userList” , key = “1”)
注解代表从缓存中查询指定的key,如果有,从缓存中取,不再执行方法.如果没有则执行方 法,并且将查询数据库中的数据和指定的key关联起来.
在增删改方法中添加
@CacheEvict(value = “userList” , allEntries = true)
放入到缓存中.而@CacheEvict则是从缓存中清除指定的key对应的数据.

(19)redis缓存雪崩和穿透

1:缓存穿透,击穿,雪崩解决方案分析

前言 : 设计一个缓存系统,不得不考虑的问题就是:缓存穿透,缓存击穿和失效时的雪崩效应。
缓存穿透:
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
解决方案:
有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存雪崩
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
解决方案
缓存失效时的雪崩效应对底层系统的冲击非常可怕。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。这里分享一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
缓存击穿
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

解决方案
1.使用互斥锁(mutex key)
业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果

  1. "提前"使用互斥锁(mutex key):
    在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。
  2. “永远不过期”:
    这里的“永远不过期”包含两层意思:
    (1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。
    (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期
    从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。
  3. 资源保护:
    采用netflix的hystrix,可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。
    四种解决方案:没有最佳只有最合适
    总结
    针对业务系统,永远都是具体情况具体分析,没有最好,只有最合适。
    最后,对于缓存系统常见的缓存满了和数据丢失问题,需要根据具体业务分析,通常我们采用LRU策略处理溢出,Redis的RDB和AOF持久化策略来保证一定情况下的数据安全。

2:MongoDB

(1)介绍
mongodb是非关系型数据库,他的存储数据可以超过上亿条(老版本的mongodb有丢数据的情况,新版本不会有,网上说的),mongodb适合存储 一些量大表关系较简单的数据
,例如用户信息,用户注册信息,公司注册信息,留言,评论,操作日志,mongodb还能用分布式文件存储信息,我们主要用mongodb来存储我们项目里面的操作日志(银行的付款转账记录,角色权限的变动日志),我们主要是结合aop来使用的,首先我们来配置一个aop的切面类,再给aop的使用规则,哪个类里面的哪个方法使用当前切面类,利用后置通知类获取当前方法的操作日志,将操作日志存储到mongodb,然后进行分类管理查看。利用后置通知传到数据库。
用户,商品评论,朋友圈,评论。

(2)aop日志存储到mongodb中,怎么存储的:
1首先我们在spring.xml配置文件中配置切面和自定义操作日志的类

aop:config
<aop:advisor pointcut=“execution(* com.jk..service….*(…))” advice-ref=“logAdvice”/>
</aop:config>
2在类中通过实现AfterReturningAdvice后置通知,并且重写里面的afterReturning方法,将方法中的参数包括
返回值,操作方法,方法的参数,操作的类放到一个log4j对象里.然后通过注入进来的MongoTemplate模板中的
insert()方法将日志对象和要操作的集合名传进去就可以了

(3)使用mongodb存储商品评论,怎么存的?
1我们主要用mongodb来存储我们项目里面的操作日志(银行的付款转账记录,角色权限的变动日志),我们主要是结合aop来使用的,首先我们来配置一个aop的切面类,再给aop的使用规则,哪个类里面的哪个方法使用当前切面类,利用后置通知类获取当前方法的操作日志,将操作日志存储到mongodb,然后进行分类管理查看。利用后置通知传到数据库。
2我们在项目中使用它来存储电商产品详情页的评论信息(评论id,商品id,标题,评分,内容,评论人信息,评论的发布时间,评论标签组)并且为了提高可用性和高并发用了3台服务器做了mongodb的副本集,其中一台作为主节点,另外两台作为副本节点,这样在任何一台mongodb服务器宕机时就会自动进行故障转移,不会影响应用程序对mongodb的操作,为了减轻主节点的读写压力过大的问题,我还对mongodb副本集做了读写分离,使写操作在主节点进行,读取操作在副本节点进行。为了控制 留言,我们留言的界面设置在了订单状态,只有状态为5,也就是交易成功收货后才能评论,并在评论成功后将订单状态改为6
(4)mongodb业务场景,下订单时,解决消息队列队列丢失问题?
使用消息队列下订单时为了防止订单丢失,
我们在发送订单消息的同时,
操作mongdb数据库,将订单消息保存
到mongdb数据库里,当确认订单处理成功后,
通过处理成功的订单id号,
在mongdb数据库中删除就可以了
案例1
用在应用服务器的日志记录,查找起来比文本灵活,导出也很方便。也是给应用练手,从外围系统开始使用MongoDB。 用在一些第三方信息的获取或者抓取,因为MongoDB的schema-less,所有格式灵活,不用为了各种格式不一样的信息专门设计统一的格式,极大的减少开发的工作。
案例2
mongodb之前有用过,主要用来存储一些监控数据,No schema 对开发人员来说,真的很方便,增加字段不用改表结构,而且学习成本极低。
案例
使用MongoDB做了O2O快递应用,·将送快递骑手、快递商家的信息(包含位置信息)存储在 MongoDB,然后通过 MongoDB 的地理位置查询,这样很方便的实现了查找附近的商家、骑手等功能,使得快递骑手能就近接单,目前在使用MongoDB 上没遇到啥大的问题,官网的文档比较详细,很给力。

从目前阿里云 MongoDB 云数据库上的用户看,MongoDB 的应用已经渗透到各个领域,比如游戏、物流、电商、内容管理、社交、物联网、视频直播等,以下是几个实际的应用案例。
游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
视频直播,使用 MongoDB 存储用户信息、礼物信息等

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值