前言
传统(企业内部)软件: 比如工单系统、OA、CRM、ERP、CMDB、大学选课系统等,主要服务于企业内部用户群体;
互联网软件: 比如 微信、QQ、今日头条、抖音、优酷、百度、微信、京东、天猫、12306等,可服务于广大互联网用户群体;
互联网软件有以下3大突出特点:
- 高并发(同时访问量大)
- 高可用(网站任何时候都能访问)
- 海量数据(动态数据量大)
MySQL支持最大并发是2000/秒,如果我们把所有数据存储在MySQL中,单靠关系型数据库,无法支撑起1个互联网软件;
由于互联网的这些特点,单纯的关系型数据库是扛不住的,那么这个时候就需要非关系型数据库的加入;
非关系型数据库是为了配合关系型数据库共同应对互联网产品问题而存在的,他们之间的关系是合作,而不是替代;
常见的NoSQL产品:
- Redis
- Mongodb
Redis(Remote Dictionary Server)是用C语言开发的一个开源的高性能的键值对数据库。
它的所有数据都是保存在内存中的,这也就决定了其读写速度之快,是其它硬盘保存数据的系统所无法匹敌的。
官方曾经给出过一组测试数据,50个并发执行100000个请求: 读的速度是110000次/s,写的速度是81000次/s。
正是由于其有着如此高的性能,所以Redis在企业中最大的一个应用是作为缓存服务器使用,当然它也可以作为数据存储和消息中间件来用。
二、Redis常用命令
Redis采用键值对存储数据,键的类型只能是字符串,值支持5种数据类型;
- 字符串: 普通字符串
- hash: 适合存储对象
- 列表: 有序可以重复
- 集合: 无序不重复
- 有序集合:有序不重复
1.
增加数据:set key value
127.0.0.1:6379> set name zhanggen OK
获取数据:get key
127.0.0.1:6379> get name "zhanggen" 127.0.0.1:6379>
删除数据:del key
127.0.0.1:6379> del name (integer) 1 127.0.0.1:6379> get name (nil)
增加数据的时候设置有效时间:setex key 存活时间(单位是s) value
127.0.0.1:6379> setex name 5 zhanggen OK 127.0.0.1:6379> get name "zhanggen" 127.0.0.1:6379> get name (nil) 127.0.0.1:6379>
增加的时候判断key是否存在:setnx key value
127.0.0.1:6379> setnx name Martin (integer) 1 127.0.0.1:6379> get name "Martin" 127.0.0.1:6379>
自增(减)操作:
incr/decr key
127.0.0.1:6379> set age 19 OK 127.0.0.1:6379> incr age (integer) 20 127.0.0.1:6379> decr age (integer) 19 127.0.0.1:6379>
incrby/decrby key step
127.0.0.1:6379> incrby age 3 (integer) 22 127.0.0.1:6379> decrby age 3 (integer) 19 127.0.0.1:6379>
增加数据:hset key hkey hvalue
127.0.0.1:6379> hset student1 name zhanggen (integer) 1 127.0.0.1:6379> hset student1 age 18 (integer) 1 127.0.0.1:6379> hset student1 sex man (integer) 1
获取数据:hget key hkey
127.0.0.1:6379> hget student1 name "zhanggen" 127.0.0.1:6379> hget student1 age "18" 127.0.0.1:6379> hget student1 sex "man" 127.0.0.1:6379>
获取hash的所有键hkey:hkeys key
127.0.0.1:6379> hkeys student1 1) "name" 2) "age" 3) "sex"
获取hash所有值hvalue: hvals key
127.0.0.1:6379> hvals student1 1) "zhanggen" 2) "18" 3) "man" 127.0.0.1:6379>
删除数据(单个): hdel key hkey
127.0.0.1:6379> hdel student1 sex (integer) 1 127.0.0.1:6379> hkeys student1 1) "name" 2) "age"
删除数据(所有): del key
127.0.0.1:6379> del student1 (integer) 1 127.0.0.1:6379> hkeys student1 (empty array) 127.0.0.1:6379>
添加数据:lpush(rpush) key value
127.0.0.1:6379> lpush list zhanggen (integer) 1 127.0.0.1:6379> lpush list lisi (integer) 2 127.0.0.1:6379> rpush list wangwu
查询数据:lrange key [开始索引 结束索引]
127.0.0.1:6379> lrange list 0 -1 1) "lisi" 2) "zhanggen" 3) "wangwu"
列表长度:llen key
127.0.0.1:6379> llen list (integer) 1
删除数据:lpop(rpop) key value
127.0.0.1:6379> lpop list "lisi" 127.0.0.1:6379> rpop list "wangwu"
移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止:BRPOP key timeout
添加数据:sadd key value
127.0.0.1:6379> sadd userList zhangsan (integer) 1 127.0.0.1:6379> sadd userList lisi (integer) 1 127.0.0.1:6379> sadd userList wangwu
查看数据:smembers key
127.0.0.1:6379> smembers userList 1) "zhangsan" 2) "wangwu" 3) "lisi"
获取元素数量:scard key
127.0.0.1:6379> scard userList (integer) 3 127.0.0.1:6379>
移除集合元素:srem key value
127.0.0.1:6379> srem userList lisi (integer) 1 127.0.0.1:6379> smembers userList 1) "zhangsan" 2) "wangwu" 127.0.0.1:6379>
求集合的交集(intersection):sinter key1 key2
127.0.0.1:6379> sadd list1 1 2 3 4 (integer) 4 127.0.0.1:6379> sadd list2 1 2 5 6 (integer) 4127.0.0.1:6379> SINTER list1 list2 1) "1" 2) "2"
求集合的并集:sunion key1 key2
127.0.0.1:6379> SUNION list1 list2 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" 6) "6"
求集合的差集:sdiff key1 key2
127.0.0.1:6379> sdiff list1 list2 1) "3" 2) "4" 127.0.0.1:6379>
添加数据:zadd key score value
127.0.0.1:6379> zadd manList 10 zhanggen (integer) 1 127.0.0.1:6379> zadd manList 20 zhanggen1 (integer) 1 127.0.0.1:6379> zadd manList 30 zhanggen2 (integer) 1
查询数据:zrange key [开始索引 结束索引]
127.0.0.1:6379> zrange manList 0 -1 1) "zhanggen" 2) "zhanggen1" 3) "zhanggen2"
查询数据并根据score排序
127.0.0.1:6379> zrange manList 0 -1 withscores 1) "zhanggen" 2) "10" 3) "zhanggen1" 4) "20" 5) "zhanggen2" 6) "30" 127.0.0.1:6379>
删除数据:zrem key value
127.0.0.1:6379> zrem manList zhanggen2 (integer) 1
6.
1. 模糊查询键:keys 模糊匹配规则
127.0.0.1:6379> keys list* 1) "list1" 2) "list2" 3) "list"
2. 根据键判断记录是否存在:exists key
127.0.0.1:6379> exists a (integer) 0 127.0.0.1:6379> exists list1 (integer) 1 127.0.0.1:6379>
3. 根据键判断值类型:type key
127.0.0.1:6379> type list1 set 127.0.0.1:6379>
4. 返回key的剩余生存时间:TTL key
返回-2表示当前key已经过期;
127.0.0.1:6379> setex name 5 wangge OK 127.0.0.1:6379> ttl name (integer) 3 127.0.0.1:6379> ttl name (integer) 2 127.0.0.1:6379> ttl name (integer) 1 127.0.0.1:6379> ttl name (integer) -2
5. 选择数据库: select 库索引[从0开始]
127.0.0.1:6379> select 3 OK 127.0.0.1:6379[3]> setex name 3 zhangyu OK
6. 清空当前数据库: flushdb
127.0.0.1:6379[3]> flushdb OK127.0.0.1:6379[3]> keys * (empty array)
7. 清空所有数据库: flushall
127.0.0.1:6379[3]> flushall OK 127.0.0.1:6379[3]> keys * (empty array) 127.0.0.1:6379[3]> select 1 OK127.0.0.1:6379[1]> keys * (empty array) 127.0.0.1:6379[1]>
1.Jedis
1.1.引入Pom依赖
<dependencies> <!--加入jedis依赖--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.3</version> </dependency> <!--加入单元测试依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
1.2.代码测试
package com.zhanggen.test; import org.junit.After; import org.junit.Test; import redis.clients.jedis.Jedis; import java.util.List; import java.util.Set; public class JedisTest1 { //1.创建Jedis客户端 private Jedis jedis = new Jedis("192.168.56.18", 6379); @Test public void testString() { //2.使用Jedis客户端操作String jedis.setex("name", 6, "张根"); String name = jedis.get("name"); System.out.println(name); } @Test public void testList() { //2.使用Jedis客户端操作list jedis.lpush("userList", "张弢", "张启樵", "张三丰", "张无忌"); List<String> userList = jedis.lrange("userList", 0, -1); for (String user : userList) { System.out.println(user); } } @Test public void testHash() { //2.使用Jedis客户端操作hash jedis.hset("user", "name", "张根"); jedis.hset("user", "age", "18"); String name = jedis.hget("user", "name"); String age = jedis.hget("user", "age"); System.out.println(name); System.out.println(age); } @Test public void testSet() { //2.使用Jedis客户端操作集合set jedis.sadd("name", "Martin"); jedis.sadd("name", "Jack"); jedis.sadd("name", "Tom"); Set<String> userList = jedis.smembers("name"); for (String user : userList) { System.out.println(user); } } @Test public void testZset() { //2.使用Jedis客户端操作有序集合Zset jedis.zadd("programingLanguages", 1, "C"); jedis.zadd("programingLanguages", 2, "C++"); jedis.zadd("programingLanguages", 3, "Java"); jedis.zadd("programingLanguages", 0, "Python"); Set<String> programingLanguages = jedis.zrange("programingLanguages", 0, -1); for (String language : programingLanguages) { System.out.println(language); } } @After public void colseRedis() { ; //3.关闭客户端 jedis.close(); System.out.println("关闭Redis成功"); } }
2.SpringDataRedis
Spring是用于开发企业级Web应用的框架,这个框架由很多子项目组成,之前我们学习了Spring框架中的JDBC、MVC和Boot;
SpringData也是Spring框架下的1个子项目,主要封装了持久层的解决方案;
-
ValueOperations:简单K-V操作
-
SetOperations: set类型数据操作
-
ZSetOperations: zset类型数据操作
-
HashOperations: 针对hash类型的数据操作
-
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.5</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.5</version> </plugin> </plugins> </build>
2.1.2.SpringBoot配置文件
spring:
redis:
host: 192.168.56.18 #redis服务器的IP
port: 6379
database: 0 # 操作的是0号数据库
jedis: #Redis连接池配置
pool:
max-active: 8 #最大连接数
max-wait: 1ms #连接池最大阻塞等待时间
max-idle: 4 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接
2.1.3.创建主类
package com.zhanggen; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RedisApplication { public static void main(String[] args) { SpringApplication.run(RedisApplication.class, args); } }
2.1.4.提供配置类
package com.zhanggen.config; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; //Redis配置类 //当前配置类不是必须的,因为 Spring Boot 框架会自动装配 RedisTemplate 对象, //但是默认的key序列化器为JdkSerializationRedisSerializer,导致我们存到Redis中后的数据和原始数据有差别 @Configuration public class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); //设置Key的序列化器 默认是JdkSerializationRedisSerializer redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); //设置value的序列化器 默认是JdkSerializationRedisSerializer //redisTemplate.setValueSerializer(new StringRedisSerializer()); //redisTemplate.setHashValueSerializer(new StringRedisSerializer()); //为了解决数字自增问题 //redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class)); redisTemplate.setConnectionFactory(connectionFactory); return redisTemplate; } }
2.1.5.提供测试类
package com.zhanggen.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.TimeUnit; @SpringBootTest @RunWith(SpringRunner.class) public class SpringDataRedisTest { @Autowired private RedisTemplate redisTemplate; @Test public void testString() { ValueOperations ops = redisTemplate.opsForValue(); //增加数据 ops.set("1001", "张三"); ops.set("1002", "李四"); //获取 System.out.println(ops.get("1001")); //设置有效时间 ops.set("1003", "王五", 60, TimeUnit.SECONDS); //如果不存在,再新增 ops.setIfAbsent("1004", "赵六"); ops.setIfAbsent("1004", "钱八"); // //自增 自减 // ops.set("age", 18); // ops.increment("age", 3); // ops.decrement("age", 2); //删除 redisTemplate.delete("1001"); } }
//测试字符串 @Test public void testString() { ValueOperations ops = redisTemplate.opsForValue(); //新增数据 ops.set("name", "张根"); //获取数据 String name = (String) ops.get("name"); System.out.println(name); //设置有效时间6秒 ops.set("1003", "王五", 60, TimeUnit.SECONDS); //如果不存在再新增 ops.setIfAbsent("1004", "赵六"); //自增/自减 ops.set("age", 18); ops.increment("age", 6); ops.decrement("age", 3); System.out.println(ops.get("age")); //删除 redisTemplate.delete("name"); }
//测试Hash @Test public void testHash() { HashOperations ops = redisTemplate.opsForHash(); //1.新增数据 ops.put("user", "name", "张根"); ops.put("user", "age", "18"); ops.put("user", "sex", "Man"); //获取 user的 name属性 Object userName = ops.get("user", "name"); System.out.println(userName); //获取所有的key Set keys = ops.keys("user"); for (Object key : keys) { System.out.println(key); } //获取所有的values List values = ops.values("user"); for (Object value : values) { System.out.println(value); } //删除某1属性 ops.delete("user", "name"); //删除整个hash ops.delete("user"); }
//测试list @Test public void testList() { ListOperations ops = redisTemplate.opsForList(); ops.leftPush("userList", "张根"); ops.leftPushAll("userList", "b", "C", "D"); //获取list的长度 System.out.println(ops.size("userList")); //遍历 List userList = ops.range("userList", 0, -1); for (Object o : userList) { System.out.println(o); } //删除 ops.rightPop("userList"); }
//测试set @Test public void testSet() { SetOperations ops = redisTemplate.opsForSet(); //添加 ops.add("501", "A", "b", "c"); //遍历查询 Set members = ops.members("501"); for (Object member : members) { System.out.println(member); } //移除集合元素 ops.remove("501", "A", "b"); //获取set长度 Long size = ops.size("501"); System.out.println(size); //求2个集合的交集 ops.add("502", "A", "B", "c"); Set intersect = ops.intersect("501", "502"); System.out.println(intersect); //求2个集合的差集 Set difference = ops.difference("501", "502"); System.out.println(difference); }
//测试有序集合Zset @Test public void testZset() { ZSetOperations ops = redisTemplate.opsForZSet(); //添加 ops.add("5001", "Tom", 10); ops.add("5001", "Jack", 20); ops.add("5001", "Albert", 30); //遍历查询 Set userSet = ops.range("5001", 0, -1); for (Object user : userSet) { System.out.println(user); } //修改有序集合中的元素的分数 +16分 ops.incrementScore("5001", "Jack", 16); //删除有序集合中的元素 ops.remove("5001", "Albert"); }
//测试通用API @Test public void testGenericOperation() { //模糊查询redis数据库中所有key Set keys = redisTemplate.keys("*"); for (Object key : keys) { System.out.println(key); } //判断key是否存在 Boolean isExist = redisTemplate.hasKey("5001"); System.out.println(isExist); //判断key的类型 DataType type = redisTemplate.type("5001"); System.out.println(type.name()); //判断key剩余存活时间 Long expire = redisTemplate.getExpire("5001"); System.out.println(expire); //删除key redisTemplate.delete("5001"); }
参考