环境搭建与安装网上教程很多,这里不再赘述。1、什么是Redis?Redis(全称:RemoteDictionaryServerRemoteDictionaryService)是一个开源的、日志型、Key-Value数据库,用ANSIC语言编写,支持网络,可以是基于内存的,也可以是持久化的。您可能知道Redis用于缓存。它其实是一种数据库,可以存储经常使用的数据,也就是大家所说的缓存。官方数据是Redis可以达到10w+QPS(每秒查询速度)。为什么Redis比Mysql等数据快?因为Redis以key-values格式存储数据,时间复杂度为O(1),即直接通过key查询对应的value。比如Mysql数据库,底层实现是B+树,时间复杂度是O(logn)。最重要的一点是,数据库数据存储在磁盘上,而Redis存储在内存中,两者的速度差距不言而喻。但是Redis也支持持久化存储,这个在后面的FAQ中会提到。2.Redis数据类型Redis支持5种数据类型:string(字符串)、hash(散列)、list(列表、有序和可重复)、set(集合、无序和不可重复)、zset(有序集合、有序和非可重复)。Redis中的所有数据都是字符串,键是区分大小写的。String是最基本的类型,可以包含任何数据,但是string类型的值最多可以存储512MB。hash的value相当于一个map,value也有对应的key-value,特别适合存储对象。一个哈希可以存储2^32-1个键值对,基本上是无穷无尽的。并且可以修改某个属性值,所以一般用于存储用户或者其他实体类的值。列表中的值按插入顺序排序,新的元素可以添加到列表的头部和尾部。一般用于最新消息的排行榜或消息队列。集合存储唯一值的集合,并且是无序的。它还提供交、并、差等运算,因此一般用于统计等功能。与set不同的是,zset是按照分数(score)从小到大排序的,我们可以指定每个值的分数,分数可以重复。一般用于排名等功能。3、Redis常用命令在了解以上5种数据类型的基础上,再学习Redis常用命令。更多的命令学习,推荐大家看看官方文档http://www.redis.cn/commands.html(1)Redisstirng操作的命令是不区分大小写的。在下面的命令中,str是键,hello是值,append是追加命令。如果没有str,则创建一个新的。appendstrhello//给key为str的key添加hello字符串appendstrredis //str的值变成helloredissetstr11 //set命令设置一个key的值为str1iskey,1isvaluegetstr1 //get命令获取某个key的值incrstr1 //incr命令执行加1的操作,比如str1的值会变成2,如果specifiedkeycannotrepresentaninteger,会返回错误decrstr1 //减一操作(2)上面在hash的操作中提到,hash的值相当于一个map,所以当只设置value的时候,myhash是key,h1是key里面的值,hello是h1的值hsetmyhashh1hello //设置一个key的值hgetmyhashh1 //returnhello,myhash是key,h1是value中的key,两者都需要指定hlenmyhash //获取myhash的字段个数,这里返回1hkeysmyhash //获取myhash的所有字段,这里返回h1(3)的对mylst列表的操作是key,a,b,c,d都是value,并且有顺序,所以实际存储的是d,c,b,alpushmylistabcd //lpush,从左边入队一个或多个元素ofthequeuelrangemylist0-1 //获取指定范围的值,从0开始,-1表示全部,注意这里返回的是d,c,b,a。rpushmylist123 //从右边进入队列,如果再lrange,就是d,c,b,a,1,2,3lpopmylist //从队列中弹出一个元素left,这里弹出d,此时mylist没有d(4)如果我们在set操作中添加重复元素,不会报错,只会保存一个。比如abb只会存储ab,两个集合不受影响,即key为myset和myset2的集合都可以有absaddmysetabcd //向集合smembersmyset中添加一个或多个元素//获取集合中的所有元素,输出无序且随机。这里可能是b,d,c,asremmysetac //移除myset中的a和c元素,因为不可能重复且没有顺序,所以可以直接指定要移除的元素值(5)forzset将myzset作为key进行操作,abc前面的数字为scorezaddmyzset2b1a3c //添加一个或多个元素zrangemyzset0-1 //获取指定范围的值,从0开始,-1代表全部。更多返回a、b、c的命令可以参考上面网站的文档,写的很详细,后面的FAQ中会提到其中的一些。4、Redis常见问题(1)查询大量key中有固定前缀的key在实际业务中,key的命名是规范的,比如缓存用户信息,key的前缀可能是user。现在上千万条数据,如果查询user前缀的key,第一个想到的可能是keys命令keysuser* //user*是一个时间复杂度为O(n)的正则表达式,虽然性能可以接受,但是在查询千万级别的数据时显然太慢了。花费几分钟的时间并不少见,而且在查询完成之前,服务可能会冻结并占用大量内存,这显然是不可取的。.在这种情况下,您可以使用扫描命令。在下面的命令中,mathcount是可选的,可以用也可以不用,所以需要写出来。math表示稍后将匹配正则表达式。count代表一次10个查询。10项不是必须的,可以少于10项。scan0mathuser*count10 //从0开始,查询user前缀的key,一次查询10条,返回上面这句话。执行完上面这句话,会返回两个东西,一个游标,代表执行到哪里,比如执行到14325。返回的另一个是用户前缀的密钥。下次执行这条语句时,将0替换为14325,在上次的位置继续查询。但是游标不一定是递增的,可能下一个游标会比这次小,这样就有重复的隐患。我们可以在业务代码处循环查询,记录每次返回的cursor,将查询的key存入set中,达到去重的效果。scan其实是批量查询,速度明显没有keys快。在查询大量数据时,不会对服务器造成压力。当数据量不大的时候,还是推荐keys。(2)使用Redis实现分布式锁首先了解什么是分布式锁。即,一种控制对分布式系统中共享资源的访问的方法。例如,当系统(或主机)A和B都需要访问资源DataA时,当A先访问DataA时,需要分布式锁来阻塞B,防止A和B相互干扰,保证数据的一致性。补充一点就是Redis命令的操作是原子的,原子性体现在数据库的事务上。或者是更改数据的问题。要实现分布式锁,需要解决几个问题:1.互斥,即任何时候只有一个客户端可以获取锁。2.安全,锁只能被持有的客户端删除,其他客户端无法删除。3.死锁,即由于某些原因,某些客户端出现问题,不能及时释放锁,导致其他客户端无法获取到锁。4.容错性,当部分Redis节点出现问题时,客户端也应该能够获取到锁。我们可以使用setnx来实现锁功能。语法:setnx键值只有在键不存在时才会设置成功。成功返回1,否则返回0。1.在访问资源对应的业务代码处,为指定的key设置一个值。如果成功,说明没有其他线程执行过这段代码,也就是没有其他线程访问过这个资源。如果值设置失败,说明其他线程占用资源,等待setnx成功。2.还有一个问题就是key的有效期很长,所以需要用到expire命令。语法为:expirekeyseconds,秒的单位是秒,用于设置对应key的过期时间。上面两步看似实现了锁功能,但是缺陷也很明显。设置值成功后客户端在我设置时间之前出现问题怎么办?使用两个命令实现一个函数违背了Redis的原子性。从Redis2.6.12开始,set有两个参数,实现了以上两个功能。虽然上面两步分开是错误的,但是思路是一样的。具体语法:setkeyvalueex10nx。ex代表过期时间,这里设置过期时间为10秒,nx代表key必须唯一,即一条命令实现以上两步。最后还有一个小问题。如果同时为不同的资源设置lockkey,则过期时间相同。当Redis过期后同时删除大量key时,难免会出现卡顿。解决方法是在设置过期值的时候加上一个随机值。3、使用Redis实现消息队列消息队列,简称MQ,是消息和队列这两个词的首字母缩写。常见的消息队列有RabbitMQ、RocketMQ等,使用Redis实现消息队列只是熟悉其特性。在实践中,一般使用专门的消息队列中间件。如果之前没有学习过消息队列,建议搜索消息队列相关知识进行简单学习。简单的说,消息队列的作用就是接受客户端的请求,然后依次处理这些请求。一般在应用请求量特别大的时候,比如闪购等。上面介绍数据类型的时候提到了消息队列一般使用列表。看一下列表的常用操作。虽然叫链表,但它的特点和数据结构的队列基本相同。所以在用Redis实现消息队列的时候,首先要想到的是list。1、如果用列表,好像可以用rpush生产消息,lpop消费消息。但是有一个小问题,lpop不会等待rpush,当rpush还没来得及生成数据时,lpop会直接返回null。2、由于要等待rpush产生数据,难免会想到一个命令blpop,其语法为:blpopkeyseconds。它与lpop的功能相同,但它会等待指定的时间。如果在此期间rpush产生了数据,blpop会及时返回。3.但是blpop的缺点也很明显。当然lpop也有这个缺点,就是blpop执行完后,代表出队列了,rpush产生的消息也没有了,而且消息队列中有些需求需要多个消费者接收。这时候就可以使用Redis的订阅者模式。Redis客户端可以订阅任意数量的频道(主题)。在Redis中,使用subscribe命令订阅一个频道。语法是订阅主题。主题是自定义频道名称。注意topic不是key,不需要提前定义,直接订阅即可。然后使用publish产生消息,语法是publishtopicvalue,topic是你要发布到的频道,value是数据内容,所有订阅这个频道的消费者都会收到消息。注意是及时收到的,不需要手动使用命令获取。订阅者模式确实解决了以上两种方式的缺点,但是它的缺点也很明显,就是只有在订阅者模式下,也就是监听状态下,消费者才会收到生产者的消息,也就是在发送timeReceived,一旦Redis客户端下线,就永远不会收到这条消息。这又回到了上面提到的那句话。在实践中,会使用专门的消息队列中间件来实现这些功能。以上三种方式都或多或少的实现了消息队列的功能,但是缺陷也很明显。4、Redis如何持久化?Redis是基于内存的,所以肯定会有疑问。当我关闭主机或关闭Redis时,Redis的数据会不会消失?持久化的作用是将Redis数据存储在磁盘上,防止Redis数据丢失。Redis有两种持久化机制,默认的一种是RDB,另一种是AOF。1、RDB(快照)持久化会保存某个时间点的全量数据,快照是一种内存快速读取技术。而这个时间点可以根据我们实际的业务时间策略来配置。RDB会根据时间段策略将数据以快照的形式保存到磁盘,并生成一个dump.rdb二进制文件。我们可以在redis.conf配置文件中的save参数中查看和配置时间策略。dump.rdb文件是如何创建的?rdb文件可以通过两个命令创建,一个是save,一个是bgsave。需要注意的是,这里的save是redis命令,上面说的save是配置文件中的一个参数。save命令会阻塞Redis服务器进程,直到rdb文件创建完成,一般很少使用。bgsave命令将派生一个子进程来创建rdb文件,而不会阻塞服务器进程。Fork创建一个与父进程几乎相同的子进程。bgsave的基本原理:当我们使用bgsave命令时,首先会检查是否有RDB/AOF子进程在进行中,如果有则返回错误,即当我们第一次执行bgsave时次,其他bgsave会在执行完成前被执行,被拒绝执行。如果没有进行中的子进程,则会调用redis源码中的rdbSaveBackground方法,然后使用fork创建一个子进程。RDB的缺点:1.1。如前所述,在某个时间点会保存全量的数据。如果数据量很大,会因为I/O而严重影响性能。1.2.由于RDB是按照配置文件中的时间策略保存的,如果出现意外情况,上次保存到当前时间段的数据将会丢失。2、AOF(Append-Only-File)持久化会将查询指令以外的所有变化数据以追加方式(append)保存,默认文件名为appendonly.aof。AOF持久化默认是关闭的。我们可以在配置文件中找到appendonly参数,将其参数内容修改为yes。前面说过,AOF文件会记录所有非查询指令。最后,文件增长的问题肯定是难以避免的。主要问题是记录的很多数据是不必要的。比如循环更新一个数100次,AOF会记录这100个过程,我们只需要最后的结果即可。因此Redis提供了日志重写功能来解决文件体积增大的问题,可以通过BGREWRITEAOF命令手动执行。也可以在不中断服务的情况下执行日志重写。基本原则如下:1.使用fork创建子进程。2、子进程将新的AOF写入临时文件,不依赖已有的AOF文件,只需要读取内存中的数据即可。这里优化了很多不必要的数据。3、此时主进程仍然会将新的变化写入内存,同时也会写入已有的AOF文件。即使子进程重写失败,数据也不会丢失。4、主进程得到子进程AOF改写完成的信号后,会将新的改动追加到新的AOF文件中。5、最后用新的AOF文件替换原来的AOF文件。如果启用了AOF持久化,Redis在启动时会先检查AOF文件是否存在。如果存在,则直接加载AOF文件。如果不存在,则检查RDB文件是否存在。如果它存在,它将加载它。如果不存在,则直接启动Redis。Redis4.0之后引入了RDB-AOF混合持久化方式,并作为默认方式使用。RDB全量存储和AOF增量存储整合了各自的优势。5、SpringBoot集成Redis首先在dependencies中添加redisstarterspring-boot-starter-data-redis,然后在配置文件中进行相关配置。更多配置参见RedisProperties.java源码。spring.redis.host=127.0.0.1#redis地址spring.redis.port=6379#redis服务端口号最后注入相关类//操作为复杂类型,如各种实体类@AutowiredRedisTemplateredisTemplate//操作为String@AutowiredStringRedisTemplatestringRedisTemplateSpringBoot框架下的Redis操作不像Jedis,可以直接使用原生的Redis命令。具体的API可以自行搜索相关文档。不过还是推荐使用一些SpringBootRedis的工具类。工具类会封装RedisTemplate和StringRedisTemplate的方法,封装的方法名与Redis原生命令相同。在这里分享一篇Redis实战电子文章给大家,感兴趣的朋友可以转发+私信Redis获取
