一:redis是什么?
1)、redis是一个基于内存的key-value数据库(存储系统)。
2)、Redis 是一个高性能的key-value数据库。
3)、redis的存储分为内存存储、磁盘存储和log文件三部分,配置文件中有三个参数对其进行配置。
二:redis和Memcached的对比?
1):redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。
2):redis支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。
3):memecached仅支持key-value,这种hash结构的数据。在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果。
4):redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。也就是Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,断电后重启服务部分数据还是可以追回恢复,但是memcached是全部消失。
5): Redis支持数据的备份,即master-slave模式的数据备份,(主从复制原则),由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
6):可靠性(持久化)
对于数据持久化和数据恢复,redis支持(快照、AOF):依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响;
memcache不支持,通常用在做缓存,提升性能;
MongoDB从1.8版本开始采用binlog方式支持持久化的可靠性;
7):数据一致性(事务支持)
Memcache 在并发场景下,用cas保证一致性
redis事务支持比较弱,只能保证事务中的每个操作连续执行
mongoDB不支持事务
8):应用场景
redis:数据量较小的性能操作和运算上
memcache:用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能(适合读多写少,对于数据量比较大,可以采用sharding)
MongoDB:主要解决海量数据的访问效率问题
三:redis分布式锁的实现
1):Redis来实现简单的分布式锁的模拟的原因在于Redis的一个命令相关,该命令是setnx key value;
该命令的作用是,当往Redis中存入一个值时,会先判断该值对应的key是否存在,如果存在则返回0,如果不存在,则将该值存入Redis并返回1;
根据这个特性,我们在程序中,每次都调用setIfAbsent(该方法是setnx命令的实现)方法,来模拟是否获取到锁,如果返回true,则说明该key值不存在,表示获取到锁,如果返回false,则说明该key值存在,已经有程序在使用这个key值了,从而实现了类似加锁的功能。(部分参考==原文:https://blog.csdn.net/liuchuanhong1/article/details/54668460 )
2):分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
1、互斥性。在任意时刻,只有一个客户端能持有锁。
2、不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
3、具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
4、解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time)
,这个set()方法一共有五个形参:
-
第一个为key,我们使用key来当锁,因为key是唯一的。
-
第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用
UUID.randomUUID().toString()
方法生成。 -
第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
-
第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
-
第五个为time,与第四个参数相呼应,代表key的过期时间。
总的来说,执行上面的set()方法就只会导致两种结果:
1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。
2. 已有锁存在,不做任何操作。
我们的加锁代码满足我们可靠性里描述的三个条件。首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。由于我们只考虑Redis单机部署的场景,所以容错性我们暂不考虑。
四:redis缓存
1、redis作为缓存的作用:
就是减少对数据库的访问压力,当我们访问一个数据的时候,首先我们从redis中查看是否有该数据,如果没有,则从数据库中读取,将从数据库中读取的数据存放到缓存中,下次再访问同样的数据的是,还是先判断redis中是否存在该数据,如果有,则从缓存中读取,不访问数据库了。
2、redis出现的问题
我们后台数据库中内容修改之后,因为缓存中的内容没有修改,我们访问的时候都是先访问缓存,所以即使数据库中的内容修改了,但是页面的显示还是不会改变的。因为缓存没有更新,所以这就涉及到缓存同步的问题:即数据库修改了内容与缓存中对应的内容同步。
缓存同步的原理:就是将redis中的key进行删除,下次访问的时候,redis中没有改数据,则从DB进行查询,再次更新到redis中。
3、redis什么情况下访问数据库?
1)、redis挂掉了 2)、redis中缓存的数据丢失了
这种情况就是出现了缓存雪崩,缓存失效的问题,会出现2中情况:
1)、局部高峰期热点数据丢失。
2)、高峰期大面积缓存key失效。(不同的key设置不同的失效时间)
这样缓存服务挂掉,热点数据失效,导致所有的请求都去请求数据库,会导致数据库查询紧张,查询缓慢。
解决缓存雪崩的办法:
1:减少并发量(限流,同步),通过增加互斥锁(jvm锁),有lock,synchronized,ConcurrentHashMap等。
1)trylock(),有返回值,可以设置过期时间,不用无限的等待下去,
2)ConcurrentHashMap中有个putifabsent(),类同与redis,有值不放直接返回,没值才存放。
假如有两千的并发,可以让一个先先获取锁,之后读取放入到缓存中,重建缓存,其余的在访问缓存。
加锁的优点:简单有效,使用范围广
缺点:线程阻塞,用户体验不好,锁的粒度太大。
2:缓存降级,
根据业务需求,采用次要缓存(备份数据)采取不同的实现策略,也可以直接返回固定值。
优点:灵活,根据业务需要做响应的调整。
缺点:对开发人员的要求较高,保证备份数据的一致性,维护难度加大。
后期有增加在补充,先写到这,仅此作为学习内容的记录,如果有错误请大家指正,谢谢,