redis进阶和spring boot redis 基础命令 weir 2016-10-10 00:17:19.0 redis 6628 先说spring boot+redis pom.xml文件: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.weir</groupId> <artifactId>spring-boot-redis</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-boot-redis</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.1.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <profiles> <profile> <id>production</id> <dependencies> <!-- This sample is a test for the autoconfig when commons-pool is *absent*. In production it would be useful to enable pooling by using this dependency. --> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <type>pom.lastUpdated</type> </dependency> </dependencies> </profile> </profiles> </project> app类: package com.weir.spring_boot_redis; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.ListOperations; import org.springframework.data.redis.core.SetOperations; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.core.ZSetOperations; /** * Hello world! * */ @SpringBootApplication public class App implements CommandLineRunner { @Autowired private StringRedisTemplate template; public static void main( String[] args ) { SpringApplication.run(App.class, args).close(); } @Override public void run(String... args) throws Exception { ValueOperations<String, String> ops = this.template.opsForValue(); String key = "weir.redis.t1"; if (!this.template.hasKey(key)) { ops.set(key, "weir");//新增 } System.out.println("Found key " + key + ", value=" + ops.get(key)); // 输出系统中所有的key Set<String> set = this.template.keys("*"); for (String string : set) { System.out.println(string); } System.out.println("判断key1键是否存在:"+this.template.hasKey("key1"));//判断key1键是否存在 this.template.delete("key1");//删除某个key,若key不存在,则忽略该命令 this.template.expire("ket1", 5, TimeUnit.MINUTES);//设置 key001的过期时间为5秒 this.template.getExpire("ket1");//查看某个key的剩余生存时间,单位【秒】.永久生存或者不存在的都返回-1 this.template.persist("ket1");//移除某个key的生存时间 this.template.type("ket1");//查看key所储存的值的类型 this.template.rename("oldKey", "newKey");//修改键名 this.template.renameIfAbsent("oldKey", "newKey");//修改键名 返回Boolean this.template.move("key1", 1);//将当前db的key移动到给定的db当中 ListOperations<String, String> opslist = this.template.opsForList(); //list add opslist.leftPush("w1", "w1"); opslist.leftPush("w1", "w2"); opslist.leftPush("w1", "w3"); opslist.leftPush("w1", "w4"); //获取list w1 的所有元素 opslist.range("w1", 0, -1); // 删除列表指定的值 ,第二个参数为删除的个数(有重复时),后add进去的值先被删,类似于出栈 opslist.remove("w1", 2, "w2"); //删除下标0-2区间之外的元素 opslist.trim("w1", 0, 2); //列表元素出栈 从左 从右 opslist.leftPop("w1"); opslist.rightPop("w1"); //修改列表中指定下标的值 opslist.set("w1", 1, "w22"); //长度 opslist.size("w1"); SetOperations<String, String> opsSet = this.template.opsForSet(); //set 新增 opsSet.add("q1", "q1"); opsSet.add("q1", "q2"); opsSet.add("q1", "q3"); opsSet.add("q1", "q4"); //查看 opsSet.members("q1"); //集合sets中删除元素q2 opsSet.remove("q1", "q2"); //判断q2是否在集合sets中 opsSet.isMember("q1", "q2"); //遍历set Set<String> qq = opsSet.members("q1"); for (String string : qq) { System.out.println(string); } opsSet.add("q2", "qq1"); opsSet.add("q2", "q2"); opsSet.add("q2", "qq3"); opsSet.add("q2", "q4"); //交集 opsSet.intersect("q1", "q2"); //并集 opsSet.union("q1", "q2"); //差集 q1中有,q2中没有的元素 opsSet.difference("q1", "q2"); ZSetOperations<String, String> zset = this.template.opsForZSet(); // zset.add("a1", "a1", 7.0); zset.add("a1", "a2", 2.0); zset.add("a1", "a4", 8.0); zset.add("a1", "a6", 4.0); zset.range("a1", 0, -1);//按照权重值排序 zset.remove("a1", "a2");//删除指定元素 zset.removeRange("a1", 2, 3);//用键从排序集合中移除开始和结束之间的范围内的元素。 zset.removeRangeByScore("a1", 2.0, 5.0);//用键从排序集中删除最小和最大值之间的元素 zset.size("a1");//集合个数 //权重某个范围内(2.0——7.0),元素的个数 zset.count("a1", 2.0, 7.0); //查看zset集合中a4的权重 zset.score("a1", "a4"); //查看下标1到2范围内的元素值 zset.range("a1", 1, 3); HashOperations<String, Object, Object> hash = this.template.opsForHash(); hash.put("h1", "hh001", "hq1"); hash.put("h1", "hh002", "hq2"); hash.put("h1", "hh003", "hq3"); hash.increment("h1", "hh004", 22); //查看所有值 hash.values("h1"); hash.delete("h1", "hh002"); //hh004整型键值的值增加11 hash.increment("h1", "hh004", 11); hash.hasKey("h1", "hh003");//判断hh003是否存在 hash.get("h1", "hh003");//获取hh003对应的值 List<Object> hk = new ArrayList<>(); hk.add("hh002"); hk.add("hh003"); hash.multiGet("h1", hk);//批量获取hh002和hh003对应的值 hash.keys("h1");//获取hashs中所有的key } } 这些都是一下很基本的操作,以后再补充。 还有最重要的application.properties 差点忘了: # REDIS (RedisProperties) #spring.redis.cluster.max-redirects= # Maximum number of redirects to follow when executing commands across the cluster. #spring.redis.cluster.nodes= # Comma-separated list of "host:port" pairs to bootstrap from. # Redis数据库索引(默认为0) spring.redis.database=0 spring.redis.host=192.168.38.13 spring.redis.password= # 连接池最大连接数(使用负值表示没有限制) spring.redis.pool.max-active=8 # 连接池中的最大空闲连接 spring.redis.pool.max-idle=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 连接池中的最小空闲连接 spring.redis.pool.min-idle=0 spring.redis.port=6379 #spring.redis.sentinel.master= # Name of Redis server. #spring.redis.sentinel.nodes= # Comma-separated list of host:port pairs. # 连接超时时间(毫秒) spring.redis.timeout=0 # Connection timeout in milliseconds. 时隔一段时间我们再聊聊现在比较火的redis。Redis的优势我们就不说太多了,在集群和数据类型上面明显优于memcached,其他的也就不多说了,还是我之前说的在高可用和性能上做出选择那肯定是高可用。最近我还看了一下CAP所谓的分布式三原则Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),好像并不提性能这方面的事情。也就是说一旦进入分布式领域,性能好像就不是最重要的事情了,在这一点上也能够理解,言外之意就是说:分布式就是性能的代名词。说实话想想也是分布式它本身的复杂度就决定了我们不能对问题面面俱到做的很完美。 回到正题我们说会redis,之前我们聊了一些基础的内容,今天在深入一点研究。 我们先说redis的持久化 这也是redis的一大亮点,对于内存缓存来说能做到这一点那绝对是没的说,Redis持久化分成两种方式:RDB(Redis DataBase)和AOF(Append Only File)先解释一下: 1:RDB是在不同的时间点,将Redis某一时刻的数据生成快照并存储到磁盘上 2:AOF是只允许追加不允许改写的文件,是将Redis执行过的所有写指令记录下来,在下次 Redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了 3:RDB和AOF两种方式可以同时使用,在这种情况下,如果Redis重启的话,则会优先采用AOF 方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高 4:可以关闭RDB和AOF,这样的话,Redis将变成一个纯内存数据库,就像Memcache一样 5:通过配置redis.conf中的appendonly为yes就可以打开AOF功能 从上面的解释来看已经很清楚了。我们可以发挥一下想象,对于内存中的数据库怎么保存到磁盘上面,而redis的做法有什么出乎意外的么?在我看来没有什么出乎意外的,我们知道持久化数据库是要IO操作的,IO操作是非常损耗性能的,那redis是怎么做的呢? RDB方式,Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。 看完这段话大家有什么想法,假如我们当前的缓存内容是2G,用RDB方式就会占用4G的内存,那如果是20G缓存内容呢?在RDB持久化过程中所用的时间和IO操作还有CPU的使用那可不是闹着玩的。还有最后一次的快照能保障不丢失么?显然不能。 我们再看看AOF,AOF就像是日志文件一样记录着每一次我们的写命令,也就是说一旦机器挂了也就是丢失一条命令,对于数据完整性来说这已经是很不错了。 如果系统运行了几年那AOF的文件也小不了,那么在数据恢复速度来说也快不了,当然对于文件不断增长redis搞了一个AOF文件重写(rewrite)机制,其实就是AOF文件的压缩,有时候我就搞不明白,这么直接的表达不好么,重写和压缩哪个更容易理解,那么他为什么还叫重写而不是叫压缩呢?举个例子: RPUSH list 1 2 3 4 // [1, 2, 3, 4] RPOP list // [1, 2, 3] LPOP list // [2, 3] LPUSH list 1 // [1, 2, 3] 这是对一个list的四次操作,最后的结果是 [1, 2, 3] 对吧,那我们就可以这样做 RPUSH 1 2 3 命令来代替前面的四条命令,大家明白了重写的真正意思了吧,就这一点来说有没有惊艳到你,觉得这样做很高明,其实并没有,这都是非常容易想到的,这就像是分布式事务的最终一致性,我只要保证最后的数据没问题,过程可以省略的嘛,是不是这个道理,这样至少可以去掉很多命令,自然就使AOF文件变小了嘛。 那么在实际引用中我们用哪一个持久化,这要看你们对数据要求的程度,具体的经验说实话我没有,但是我们可以推想呀,你们的系统有淘宝大么,用户量多少,日访问量多少,每季度,每年的用户增量多少,数据需要用redis缓存的数量是多少等等 这些都考虑进去,对性能要求和对数据完整性的要求是怎么样的,因为redis是要代替后台关系型数据库的,把数据放到内存中是为了提升性能的,你们的网站到底有多少数据可以放在redis里面,这都是需要考虑的问题,而且这些是完全可以预估出来的。对于大型互联网公司来说redis可能都满足不了他们的需求了,内存数据可能已经到了几十G甚至是百G的量级,为了高可用redis也是有问题的对不对,当然对于绝大多数的公司来说redis完全可以胜任的。 上面说了redis持久化 接下来看看redis事务,不知道以后redis会不会有真正事务但是就目前来说说redis事务其实是自欺欺人,如果真的想在redis中使用真正的事务作用目前比较有效的做法是结合lua脚本来完成,有了lua不但可以解决redis的事务问题还可以提高redis的性能,那么lua脚本是个啥东东呢,大家可以先了解一下在以后我们再详细说说。所以这里说redis事务我也不打算说太多,因为她真的称不上事务这个概念。 这里想初步聊聊redis的复制和集群,可以说如果没有复制和集群redis不会这么火,这个也是它优于memcached的原因,还是我们刚开始说的CAP可用性对于分布式架构系统来说是非常重要的,数据你可以丢失但是必须保证可用不能挂了对不对,redis3之后在复制和集群方面又做的进了一步,这里面的操作和知识点也很多 但是都很容易实现,这里先提一下,我会单独拿出来包括命令操作再写一篇博客来说清楚复制和集群的问题。