无风无影

   ::  :: 新随笔  ::  ::  :: 管理

  

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的内存淘汰策略就派上用场了。

淘汰策略基本上分两大类:内存不足(新写、最少使用、随机) 根据过期时间(最少使用,随机,过期更早的)

 

posted on 2018-06-13 16:04  NWNS-无风无影  阅读(240)  评论(0)    收藏  举报