1、redis基本数据类型
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象
类似Java里面的Map<String,Object>
List(列表)Redis 列表是简单的字符串列。可以只作为队列使用。按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。它的底层实际是个链表。
Set(集合)Redis的Set是string类型的无序集合。它是通过HashTable实现实现的,相对于List,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
zset(sorted set:有序集合)Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。Redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
2、 mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
相关知识: redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:
volatile-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(驱逐):禁止驱逐数据
3、redis应用场景
1、缓存热点数据,既经常被查询和不容易修改的数据
2、分布式锁与单线程机制
3、队列
4、位操作(大数据处理)
用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等。
想想一下腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,你能怎么做?千万别说给每个用户建立一个key,然后挨个记(你可以算一下需要的内存会很恐怖,而且这种类似的需求很多,腾讯光这个得多花多少钱。。)好吧。这里要用到位操作——使用setbit、getbit、bitcount命令。
原理是:
redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标index用来表示我们上面例子里面的用户id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统,上面我说的几个场景也就能够实现。用到的命令是:setbit、getbit、bitcount
5:最新列表
例如新闻列表页面最新的新闻列表,如果总数量很大的情况下,尽量不要使用select a from A limit 10这种low货,尝试redis的 LPUSH命令构建List,一个个顺序都塞进去就可以啦。不过万一内存清掉了咋办?也简单,查询不到存储key的话,用mysql查询并且初始化一个List到redis中就好了。
6:排行榜
谁得分高谁排名往上。命令:ZADD(有续集,sorted set)
最近在研究股票,发现量化交易是个非常好的办法,通过臆想出来规律,用程序对历史数据进行验证,来判断这个臆想出来的规律是否有效,这玩意真牛!有没有哪位玩这个的给我留个言,交流一下呗。
4、redis持久化方式
1、RDB持久化可以在指定的时间间隔内生成数据集的时间点快照
详解:
Redis 会单独的创建(fork) 一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程结束了,再用这个临时文件替换上次持久化还的文件。整个过程总,主进程是不进行任何 IO 操作,这就确保了极高的性能,如果需要进行大规模的数据恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方法要比 AOF 方式更加的高效。RDB 的缺点是最后一次持久化后的数据可能丢失。
Fork 的作用是复制一个与当前进程一样的进程,新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
隐患:若当前的进程的数据量庞大,那么 fork 之后数据量*2,此时就会造成服务器压力大,运行性能降低。
2、AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
3、Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。
你甚至可以关闭持久化功能,让数据只在服务器运行时存在。
区别:AOF文件更小,效率更高,更容易丢失数据
5、缓存穿透、缓存击穿和缓存雪崩
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
解决方案:
- 利用互斥锁。缓存失效的时候,不能直接访问数据库,而是要先获取到锁,才能去请求数据库。没得到锁,则休眠一段时间后重试。
- 提供一个能迅速判断请求是否有效的拦截机制。最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,内部维护一系列合法有效的key,迅速判断出请求所携带的Key是否合法有效。如果不合法,则直接返回。一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层数据库的查询压力。
- 另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存雪崩是指在设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,导致所有的查询都落在数据库上,造成了缓存雪崩。
解决方案:
- 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
- 可以通过缓存reload机制,预先去更新缓存,在即将发生大并发访问前手动触发加载缓存。
- 不同的key,设置不同的过期时间(随机),让缓存失效的时间点尽量均匀。
- 做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
缓存击穿 对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。 缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决方案:目的:尽量少的线程构建缓存(甚至是一个) + 数据一致性 + 较少的潜在危险
1、 使用互斥锁(mutex key): 这种解决方案思路比较简单,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据就可以了
2、提前使用互斥锁 在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中
3、 "永远不过期":这里的“永远不过期”包含两层意思:
(1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。
(2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期
4、采用netflix的hystrix,可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。
四种解决方案:没有最佳只有最合适
| 解决方案 | 优点 | 缺点 |
| 简单分布式互斥锁(mutex key) |
1. 思路简单 2. 保证一致性 |
1. 代码复杂度增大 2. 存在死锁的风险 3. 存在线程池阻塞的风险 |
| “提前”使用互斥锁 | 1. 保证一致性 | 同上 |
| 不过期(本文) |
1. 异步构建缓存,不会阻塞线程池 |
1. 不保证一致性。 2. 代码复杂度增大(每个value都要维护一个timekey)。 3. 占用一定的内存空间(每个value都要维护一个timekey)。 |
| 资源隔离组件hystrix(本文) |
1. hystrix技术成熟,有效保证后端。 2. hystrix监控强大。
|
1. 部分访问存在降级策略。 |
6、如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:

1、存储方式
- Memcache把数据全部存在内存之中,断电后会挂掉,无法做到数据的持久化,且数据不能超过内存大小。
- Redis有一部分数据存在硬盘上,可以做到数据的持久性。重启的时候可以再次加载进行使用。
2、数据支持类型
- Memcache对数据类型支持相对简单,只支持String类型的数据结构。
- Redis有丰富的数据类型,包括:String、List、Hash、Set、Zset。
3、存储值大小
- Redis最大可以存储1GB,而memcache只有1MB。
4、使用的底层模型
- 它们之间底层实现方式以及与客户端之间通信的应用协议不一样。
- Redis直接自己构建了VM机制 ,因为一般的系统调用系统函数,会浪费一定的时间去移动和请求。
7、Redis缓存过期策略
Redis采用的是定期删除和惰性删除的内存淘汰机制。
定期删除
定期删除和定时删除是有区别的:
- 定时删除是必须严格按照设定的时间去删除缓存,这就需要我们设置一个定时器去不断地轮询所有的key,判断是否需要进行删除。但是这样的话cpu的资源会被大幅度地占据,资源的利用率变低。所以我们选择采用定期删除,。
- 定期删除是时间由我们定,我们可以每隔100ms进行检查,但还是不能检查所有的缓存,Redis还是会卡死,只能随机地去检查一部分缓存,但是这样会有一些缓存无法在规定时间内删除。这时惰性删除就派上用场了。Redis默认每隔100ms随机抽取一些设置了过期时间的key,检查是否过期,如果过期就删除。
惰性删除
在获取某个key的时候,Redis会进行检查,如果key设置了过期时间,并且已经过期,则删除这个key,释放内存空间。
// Redis的过期策略是什么? 惰性删除 + 定期删除 // Redis怎么实现定期删除的呢? Redis默认每隔100ms随机抽取一些设置了过期时间的key,检查是否过期,如果过期就删除。 // 为什么要随机抽取呢? 假设Redis中有大量的key,并且都设置了过期时间,如果全量检查的话,将会耗费大量的CPU时间,这样有损Redis对外的服务性能。 // 随机抽取会带来什么问题呢? 可能导致已经过期的key还滞留在内存中,占用着内存空间。 // 如何解决随机抽取带来的问题呢? 为了解决这个问题,Redis引入了惰性删除。 // 什么是惰性删除呢? 在获取某个key的时候,Redis会进行检查,如果key设置了过期时间,并且已经过期,则删除这个key,释放内存空间。 // 如果某些key没有触发惰性删除,也就是说经过惰性删除 + 定期删除两轮清理,依旧存在,该如何解决呢? 这种情况,在内存不足的时候,Redis的内存淘汰策略就派上用场了。
淘汰策略基本上分两大类:内存不足(新写、最少使用、随机) 根据过期时间(最少使用,随机,过期更早的)

浙公网安备 33010602011771号