Redis概述
简单来说,redis是一个数据库,但与传统数据库不同的是,redis数据存储在内存中,使得保存和写入都非常快。
这个Redis广泛应用于缓存方向。此外,Redis 经常用于分布式锁定。 redis提供不同的数据类型来支持不同的行业
业务场景。此外,redis 支持事务、持久性、LUA 脚本、LRU 驱动的事件和各种集群解决方案。
为什么要使用Redis?
看待这个问题主要有两个方面:高性能和高并发。
让我们来看看它的高性能吧!
假设用户第一次访问数据库中的数据。此过程需要时间,因为它是从硬盘读取的。将用户访问的数据存储在数据缓存中,以便用户下次访问时可以直接从缓存中检索数据。缓存操作相当快,因为它们直接在内存上操作。
如果数据库中对应的数据发生变化,只需同步更改缓存中对应的数据即可。
接下来是高并发
因为直接操作缓存可以承受比直接访问数据库大得多的请求,所以可以将数据库中的一些数据传输到缓存中,这样用户的一些请求就可以直接进入缓存,而不需要经过缓存。考虑允许将其发送给您。数据库。
为什么使用Redis 而不是Maps/Guava 进行缓存?
缓存分为本地缓存和分布式缓存。以Java为例,本地缓存是使用内置的map或者guava来实现的。主要特点是生命周期随着jvm的销毁而结束。如果有多个实例,则每个实例都会被销毁。你需要为每个实例存储一个缓存,但是缓存并不一致。
使用redis或memcached称为分布式缓存。对于多个实例,每个实例共享数据的缓存,并且缓存是一致的。缺点是需要保持redis或者memcached服务的高可用,整体程序架构比较复杂。
4.redis和memcached的区别
关于redis和memcached我总结了以下四点。如今,企业普遍使用Redis来实现缓存,而且Redis本身也变得越来越强大。
1、Redis支持更丰富的数据类型(支持更复杂的应用场景):Redis不仅支持简单的k/v类型数据,还支持list、set、zset、hash等数据结构的存储。 memcache 支持一种简单的数据类型:String。
2、Redis支持数据持久化。这允许数据保存在磁盘上的内存中并重新加载以供重新启动时使用。
Memecache 将所有数据存储在内存中。
3.集群模式:memcached没有原生集群模式,必须依赖客户端将数据写入集群内的分片。不过,目前它依赖于redis。
原生支持集群模式。
4、Memcached是多线程、非阻塞IO复用网络模型,而Redis则采用单线程、多通道IO复用模型。
分析常见Redis数据结构及使用场景
1. 字符串
常用命令包括: set、get、decr、incr 和mget。
字符串数据结构是一种简单的键值类型,实际上值可以是数字也可以是字符串。
传统的key-value缓存应用,传统的统计:微博发帖数、粉丝数等。
2.哈希值
常用命令:hget、hset、hgetall等。
哈希是一个字段和字符串类型值的映射表,适合在后续操作时直接只改变这个对象的某些字段的值。 例如,可以使用哈希数据结构来存储用户信息、产品信息等。例如,下面我使用哈希类型来存储我的一些信息。
key=JavaUser293847value={“id”: 1,“姓名”:“SnailClimb”,“年龄”:22,“位置”:“湖北省武汉市”}3.列表
常用命令: lpush、rpush、lpop、rpop、lrange 等列表就是链表,应用场景很多,也是微博关注列表、粉丝列表、Redis中最重要的数据结构之一。这可以使用Redis 列表结构来实现。
Redis列表被实现为双向链表,可以支持向后搜索和遍历,使它们使用起来更方便,但代价是额外的内存开销。此外,您可以使用lrange 命令读取从特定元素开始的元素数。这是一个很棒的功能,可以让你基于redis实现简单的高性能分页,并进行持续拉取。 -像微博这样的分页(每页后向下)功能具有高性能。
4.设置
常用命令:sadd、spop、smembers、sunion等。
set提供的外部功能与list类似,但特殊的是set可以自动去除重复项。
如果需要存储数据列表并且不想重复数据,set是一个不错的选择。集合提供了一个重要的接口,用于确定某个成员是否包含在集合集合中,这是列表无法提供的。基于集合可以方便地实现交、并、差运算。
示例:在微博应用程序中,您可以将用户的所有关注者存储在一个集合中,并将所有粉丝存储在一个集合中。 Redis可以让你非常方便的实现常见关注、常见粉丝、常见配置等功能。这个过程也是一个寻找交集的过程。具体命令为:
sinterstore key1 key2key3 将公共部分存储在key1中。 5. 排序集
常用命令:zadd、zrange、zrem、zcard等。
相比于set,有序集合增加了权重参数的分数,因此可以根据分数对集合中的元素进行排序。
举例:在直播系统中,实时排名信息包括直播间在线用户列表、各种礼物排名、集中消息(可以理解为按照消息维度的消息排名)等。使用Redis的SortedSet结构进行存储。
6.redis设置过期日期
Redis 具有过期功能,允许您为Redis 数据库中存储的值设置过期时间。这作为缓存数据库非常有用。例如,典型项目中的令牌和一些登录信息,尤其是短信验证码,都有一个过期日期,而传统的数据库处理方法通常需要您自己确定过期日期,这对于项目性能至关重要。毫无疑问,它会产生影响。
设置密钥时,您可以指定到期日期。通过过期,您可以指定密钥的有效期。
假设我设置了一批只能持续1小时的key,那么Redis如何在下一小时后删除这批key呢?从名字上你大概就能猜到这两种删除方法的含义了。
定期删除:默认情况下,Redis 每100 毫秒随机选择一个有过期日期的key,检查它是否过期,如果过期则将其删除。
请注意,这是随机选择的。为什么是随机的?如果你想一想,如果Redis存储了数十万个key,并且每100ms就扫描一次它们的过期时间,这会给CPU带来很大的压力。
延迟删除:定期删除可能会导致许多过期密钥在过期时未被删除。因此,出现延迟删除。如果过期的键没有通过正常删除删除并保留在内存中,Redis 会将其删除,除非系统检查该键。这就是所谓的懒删除,已经够懒了,但是仅仅设置一个过期日期还是有问题的。我们想一想。如果定期删除漏掉了很多过期的key,而因为没有及时检查而无法进行惰性删除怎么办?我怎么解决这个问题?
Redis内存删除机制。
7.redis内存删除机制(MySQL有2000万条数据,
如果你有20万条数据,如何保证Redis中的数据是热数据?
Redis配置文件redis.conf中有相关注释,这里就不贴出来了。亲自检查或通过以下网站检查:
http://download.redis.io/redis-stable/redis.conf
Redis提供了六种数据删除策略。
5. volatile-lru:从过期数据集(server.db[i].expires)中选择最近最少使用的数据进行删除。
6. volatile-ttl:从具有过期日期(server.db[i].expires)的数据集中选择并删除即将过期的数据。
7. volatile-random:设置一个过期时间,可以任意选择数据从数据集中删除(server.db[i].expires)。
8. allkeys-lru:如果内存不足以容纳新写入的数据,则删除键空间中最近最少使用的键(也是最常用的)。
9. allkeys-random:从数据集中任意选择要删除的数据(server.db[i].dict)
10. 不驱逐:防止数据驱逐。这意味着如果没有足够的内存来容纳新写入的数据,新的写入操作将报告错误。没有人应该使用这个!
8.redis持久化机制(如何保证redis挂掉重启后数据可以恢复)
数据通常需要持久化。即内存中的数据必须写入硬盘。这样做的原因大多是为了以后重用数据(例如重启机器、机器故障后恢复数据),或者备份。远程存储数据以防止系统故障。
Redis 与Memcached 的一个重要区别在于Redis 支持持久化,并且支持两种不同的持久化操作。 Redis 中的一种持久化方法称为快照(RDB),另一种是仅追加(append-only)。
fifile、AOF)这两种方法各有优点,我们将详细说明这两种持久化方法是什么、如何使用它们以及如何选择适合您的持久化方法。
快照和持久化(RDB)
Redis 可以创建快照来获取特定时间点存储在内存中的数据的副本。 Redis 创建快照后,可以将快照复制到另一台服务器上,以创建包含相同数据的服务器副本(Redis 的主/从结构;主要用于提高Redis 性能)。部署快照以在使用时重新启动服务器。
快照持久化是Redis默认采用的持久化方式。 redis.conf配置文件默认配置如下:
save 9001 命令创建快照。 #900 几秒(15 分钟)后,如果至少有一个键发生变化,Redis 会自动触发BGSAVE 命令save 300 10 创建快照。 #300秒(5分钟)且至少10个key发生变化后,Redis自动触发BGSAVE命令save 60 10000创建快照。 #60秒(1分钟)且至少10000个key发生变化后,Redis自动触发BGSAVE命令AOF(append-only fifile)持久化。
相比快照持久化,AOF持久化具有更好的实时性,成为主流的持久化方案。默认情况下不启用Redis
可以使用appendonly参数启用AOF(仅追加文件)持久性。
appendonly 是启用AOF 持久化后,每次运行修改Redis 中数据的命令时,Redis 都会将该命令写入硬盘上的AOF 文件中。 AOF 文件保护
存储位置与RDB文件位置相同,均由dir参数设置。默认文件名是appendonly.aof。
Redis配置文件中存在三种不同的AOF持久化方式:
appendfsync #每次发生数据变化时都会写入AOF文件,这会显着减慢Redis的速度。 appendfsync Everysec #1 每秒同步一次,并显式地将多个写入命令同步到硬盘。 appendfsync no # 让操作系统决定何时运行在同步数据和写入性能时,用户可以允许Redis 每秒同步一次AOF 文件,对Redis 性能影响很小,可以考虑使用appendfsync Everysec 选项来使其发挥作用。此外,如果系统崩溃,用户最多只会丢失一秒内生成的数据。当硬盘忙于执行写入操作时,Redis 会优雅地放慢速度以适应硬盘的最大写入速度。
优化Redis 4.0持久化机制
Redis 4.0开始支持混合RDB和AOF持久化(默认关闭,但可以通过配置项打开)。
开启混合持久化后,重写AOF 时,RDB 内容会直接写入到AOF 文件的开头。这样做的好处是结合了RDB和AOF的优点,可以快速加载,同时避免过多的数据丢失。当然,AOF的RDB部分是压缩格式,而不是AOF格式,这样阅读起来比较困难。
补充内容:AOF重写
重写AOF会生成一个新的AOF文件。这个新的AOF 文件与原始AOF 文件具有相同的数据库状态,但大小更小。
AOF 重写这个名字是有歧义的。该功能是通过读取数据库中的键值对来实现的。该程序不需要读取、分析和写入现有的AOF 文件。
当您运行BGREWRITEAOF 命令时,Redis 服务器会维护一个AOF 重写缓冲区。该缓冲区记录子进程创建新AOF 文件时服务器执行的所有写入命令。当子进程完成创建新的AOF 文件时,服务器将重写缓冲区中的所有内容追加到新AOF 文件的末尾,确保新旧AOF 文件中的数据库状态匹配。最后,服务器用新的AOF文件替换旧的AOF文件,完成AOF文件重写操作。
9.redis事务
Redis 通过MULTI、EXEC 和WATCH 等命令实现事务功能。事务提供了一种打包多个命令请求并按顺序一次执行多个命令的机制。当事务正在运行时,服务器不会中止事务并执行事务内的所有命令。在处理来自其他客户端的命令请求之前执行。传统关系数据库经常使用ACID 属性来测试事务功能的可靠性和安全性。在Redis中,事务总是原子的、一致的、隔离的,如果Redis运行在某种持久模式下,事务也是持久的。
10.Redis常见异常及解决方案
使用缓存时常遇到的一些问题可以总结为四点:
10.1 缓存入侵
分布式锁可以防止不同进程重复相同的工作,减少资源浪费。 同时,分布式锁定可以避免损害数据完整性。
多个流程处理同一订单等不准确情况可能会覆盖订单状态中的错误。应用场景如下。
11.1.1 时序
任务重复执行
随着业务的发展,业务系统势必发展为集群分布式模式。如果我们需要一个定时任务来进行订单状
态的统计。比如每 15 分钟统计一下所有未支付的订单数量。那么我们启动定时任务的时候,肯
定不能同一时刻多个业务后台服务都去执行定时任务, 这样就会带来重复计算以及业务逻辑混乱
的问题。
这时候,就需要使用分布式锁,进行资源的锁定。那么在执行定时任务的函数中,首先进行分布式
锁的获取,如果可以获取的到,那么这台机器就执行正常的业务数据统计逻辑计算。如果获取不到
则证明目前已有其他的服务进程执行这个定时任务,就不用自己操作执行了,只需要返回就行了。
如下图所示:
11.1.2 避免用户重复下单
分布式实现方式有很多种:
1. 数据库乐观锁方式
2. 基于 Redis 的分布式锁
3. 基于 ZK 的分布式锁
咱们这篇文章主要是讲 Redis,那么我们重点介绍基于 Redis 如何实现分布式锁。
分布式锁实现要保证几个基本点。
1. 互斥性:任意时刻,只有一个资源能够获取到锁。
2. 容灾性:能够在未成功释放锁的的情况下,一定时限内能够恢复锁的正常功能。
3. 统一性:加锁和解锁保证同一资源来进行操作。
加锁代码演示:
public static boolean tryGetDistributedLock( Jedis jedis, String lockKey, String traceId, int expireTime ){SetParams setParams = new SetParams(); setParams.ex( expireTime ); setParams.nx(); String result = jedis.set( lockKey, traceId, setParams ); if ( LOCK_SUCCESS.equals( result ) ){return(true);}return(false);}解锁代码演示:
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String traceId) {String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) elsereturn 0 end”;Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(traceId));if (RELEASE_SUCCESS.equals(result)) {return true;}return false;}11.2 分布式自增 ID
应用场景
随着用户以及交易量的增加, 我们可能会针对用户数据,商品数据,以及订单数据进行分库分表
的操作。这时候由于进行了分库分表的行为,所以 MySQL 自增 ID 的形式来唯一表示一行数据
的方案不可行了。 因此需要一个分布式 ID 生成器,来提供唯一 ID 的信息。
实现方式
通常对于分布式自增 ID 的实现方式有下面几种:
1. 利用数据库自增 ID 的属性
2. 通过 UUID 来实现唯一 ID 生成
3. Twitter 的 SnowFlake 算法
4. 利用 Redis 生成唯一 ID
在这里我们自然是说 Redis 来实现唯一 ID 的形式了。使用 Redis 的 INCR 命令来实现唯一ID。
Redis 是单进程单线程架构,不会因为多个取号方的 INCR 命令导致取号重复。因此,基于 Redis
的 INCR 命令实现序列号的生成基本能满足全局唯一与单调递增的特性。
代码相对简单, 不做详细的展示了
12.Redis 集群模式
作为缓存数据库,肯定要考虑缓存服务稳定性相关的保障机制。
持久化机制就是一种保障方式。持久化机制保证了 Redis 服务器重启的情况下也不会损失(或少
量损失)数据,因为持久化会把内存中数据保存到硬盘上,重启会从硬盘上加载数据。
随着 Redis 使用场景越来越多,技术发展越来越完善,在 Redis 整体服务上的容错、扩容、稳
定各个方面都需要不断优化。 因此在 Redis 的集群模式上也有不同的搭建方式来应对各种需求。
总结来说,Redis 集群模式有三种:
主从模式
哨兵模式
Cluster 集群模式
12.1 主从模式
为了 Redis 服务避免单点故障,通常的做法是将 Redis 数据复制多个副本以部署在不同的服务
器上。这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此,Redis 提供
了复制( replication )功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步
到其他数据库上。
Redis 服务器分为两类:一类是主数据库(Master),另一类是从数据库(Slave)。
主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。
从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,
而一个从数据库只能拥有一个主数据库。
如图所示:
优点
1. 一个主,可以有多个从,并以非阻塞的方式完成数据同步;
2. 从服务器提供读服务,分散主服务的压力,实现读写分离;
3. 从服务器之前可以彼此连接和同步请求,减少主服务同步压力。
缺点
1. 不具备容错和恢复功能,主服务存在单点风险;
2. Redis 的主从复制采用全量复制,需要服务器有足够的空余内存;
3. 主从模式较难支持在线扩容。
12.2 哨兵模式
Redis 提供的 sentinel(哨兵)机制,通过 sentinel 模式启动redis后,自动监控 Master/Slave
的运行状态,基本原理是:心跳机制 + 投票裁决。
简单来说,哨兵的作用就是监控 Redis 系统的运行状况。它的功能包括以下两个:
1. 监控主数据库和从数据库是否正常运行;
2. 主数据库出现故障时自动将从数据库转换为主数据库。
哨兵模式主要有下面几个内容:
监控( Monitoring ):Sentinel 会定期检查主从服务器是否处于正常工作状态。
提醒( Notification ):当被监控的某个 Redis 服务器出现异常时,Sentinel 可以通过
API 向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover):当一个主服务器不能正常工作时,Sentinel 会开
始一次自动故障迁移操作,它会将失效主服务器的其中一个从服务器升级为新的主服务器,
并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务
器时,集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效
服务器。
Redis Sentinel 是一个分布式系统,你可以在一个架构中运行多个 Sentinel 进程( progress )
优点
1. 哨兵模式主从可以切换,具备基本的故障转移能力;
2. 哨兵模式具备主从模式的所有优点。
缺点
1. 哨兵模式也很难支持在线扩容操作;
2. 集群的配置信息管理比较复杂。
12.3 Cluster 集群模式
Redis Cluster 是一种服务器 Sharding 技术,3.0 版本开始正式提供。采用无中心结构,每个节
点保存数据和整个集群状态,每个节点都和其他所有节点连接。如图所示:
Cluster 集群结构特点:
1. Redis Cluster 所有的物理节点都映射到 [ 0-16383 ] slot 上(不一定均匀分布),Cluster
负责维护节点、桶、值之间的关系;
2. 在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384 的值,从之前
划分的 16384 个桶中选择一个;
3. 所有的 Redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输效率;
4. 超过半数的节点检测到某个节点失效时则判定该节点失效;
5. 使用端与 Redis 节点链接,不需要中间 proxy 层,直接可以操作,使用端不需要连接集群
所有节点,连接集群中任何一个可用节点即可。
优点
1. 无中心架构,节点间数据共享,可动态调整数据分布;
2. 节点可动态添加删除,扩展性比较灵活;
3. 部分节点异常,不影响整体集群的可用性。
缺点
1. 集群实现比较复杂;
2. 批量操作指令( mget、mset 等)支持有限;
3. 事务操作支持有限。
13.如何解决 Redis 的并发竞争 Key 问题
所谓 Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作,但是最后执行
的顺序和我们期望的顺序不同,这样也就导致了结果的不同!
推荐一种方案:分布式锁(zookeeper 和 redis 都可以实现分布式锁)。(如果不存在 Redis
的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能)
基于zookeeper临时有序节点可以实现的分布式锁。大致思想为:每个客户端对某个方法加锁时,
在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。判断是否
获取锁的方式很简单,只需要判断有序节点中序号最小的一个。当释放锁的时候,只需将这个瞬
时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。完成业务
流程后,删除对应的子节点释放锁。
在实践中,当然是从以可靠性为主。所以首推Zookeeper。
14.如何保证缓存与数据库双写时的数据一致性?
你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致
性的问题,那么你如 何解决一致性问题?
一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据
库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里
去,这样就可以保证一定不会出现不一致 的情况串行化之后,就会导致系统的吞吐量会大幅度
的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
感谢阅读:最后祝程序员的你:一台电脑,一个键盘,尽情挥洒智慧的人生;几行数字,几个字母,认真编写生活的美好;一个灵感,一段程序,推动科技进步,促进社会发展。用心编写程序,用智慧照亮人生!
原创文章,作者:小条,如若转载,请注明出处:https://www.sudun.com/ask/84932.html