Redis的常用命令与Java整合及高级应用篇
一,redis是什么?
 首先数据库分为关系型数据库和非关系型数据库,关系型数据库是采用关系模型来组织数据的数据库,简单来说就是二维表格模型,同时保证事务的一致性。
 相反非关系型数据库采用key-value形式进行存储,是一种数据结构化存储方法的集合,具有分布式性质。
 Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储。)遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
二,redis的优势
 1,性能快:redis读取的速度是110000次/s,写的速度是81000次/s。
 2,丰富的数据类型:string(字符串);list(列表);hash(哈希),set(集合);zset(有序集合)等。
 3,原子性:Redis的所有操作都是原子性的,且多个客户端同时访问redis客户端可获得更新后的值。
 4,持久化:集群(主从复制,分布式)。
三,redis与Memcached区别(经典面试题)
1 、redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储类型。memcache支持简单的数据类型,String,同时还可以缓存图片,视频。
2 、Redis支持数据的备份,即master-slave模式的数据备份(主从复制)。
3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
4、 redis的速度比memcached快很多
5、Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的IO复用模型。
6,数据安全性:memcache挂掉后,数据便消失;redis可以定期保存到磁盘(持久化)。
四,redis常用命令及springboot操作redis
4.1,引入依赖包。
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
4.2,常用命令与代码整合操作。
 注意:redis默认使用JDK序列化方式
 更多详细命令,请参考Redis中文网:https://www.redis.net.cn/
4.3,字符串操作类型。
/**
 * String - 字符串类型的操作方式
 * redisTemplate.opsForValue()
*/
	@Test
	public void stringType(){
		//	改为String序列化方式
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new StringRedisSerializer());
		//	redis命令:set key value
		redisTemplate.opsForValue().set("age", "19");
		// redis命令:get key
		String age = (String) redisTemplate.opsForValue().get("age");
		System.out.println("-->" + age);
		// redis命令:mset key value key value ...
		Map<String, Object> map = new HashMap<>();
		map.put("key1", "value1");
		map.put("key2", "value2");
		map.put("key3", "value3");
		redisTemplate.opsForValue().multiSet(map);
		// redis命令:mget key key key...
		List<String> keys = new ArrayList<>();
		keys.add("key1");
		keys.add("key2");
		keys.add("key3");
		List values = redisTemplate.opsForValue().multiGet(keys);
		System.out.println("mget -->" + values);
		// redis命令:del key
		Boolean boo = redisTemplate.delete("key1");
		// redis命令:strlen key - 可能会因为序列化的原因造成长度不准
		Long resultLong = redisTemplate.opsForValue().size("age");
		System.out.println("strlen --> " + resultLong);
		// redis命令:getset key value
		String oldValue = (String) redisTemplate.opsForValue().getAndSet("age", "25");
		System.out.println("getset --> " + oldValue);
		// redis命令:getrange key start end - 可能会因为序列化的原因造成长度不准
		String age1 = redisTemplate.opsForValue().get("age", 0, 1);
		System.out.println("getrange --> " + age1);
		// redis命令:append - 可能会因为序列化的原因造成长度不准
		Integer age2 = redisTemplate.opsForValue().append("age", "26");
		System.out.println("append --> " + age2);
		// redis命令:incr key - 自增 - 可能会因为序列化的原因造成长度不准
		Long age3 = redisTemplate.opsForValue().increment("age", 10);
		System.out.println("incr -->" + age3);
		// redis命令:decr key - 自减
		redisTemplate.opsForValue().increment("age", -10);
		Long decr = redisTemplate.getConnectionFactory().getConnection().decr("age".getBytes());
		System.out.println("decr --> " + decr);
	}
4.4,Hash操作类型。
/**
 * Hash数据类型的操作
 */
	@Test
	public void hashType(){
		// redis命令:mset key field value
		redisTemplate.opsForHash().put("person", "name", "张三");
		redisTemplate.opsForHash().put("person", "age", 19);
		// redis命令:mget key field
		String value = (String) redisTemplate.opsForHash().get("person", "name");
		System.out.println("mget-->" + value);
		// redis命令:hmset key field1 value1 field2 value2 ...
		Map<String, String> map = new HashMap<>();
		map.put("bookname", "Java精通之路");
		map.put("price", "100.99");
		redisTemplate.opsForHash().putAll("book", map);
		// redis命令:hmget key field1 field2 ...
		List<String> list = new ArrayList<>();
		list.add("bookname");
		list.add("price");
		List books = redisTemplate.opsForHash().multiGet("book", list);
		System.out.println("hmget-->" + books);
		// redis命令:del key
		redisTemplate.delete("book");
		// redis命令:hdel key field1 field2...
		redisTemplate.opsForHash().delete("book", "bookname", "price");
		// redis命令:hexists key field
		Boolean bool = redisTemplate.opsForHash().hasKey("book", "bookname");
		System.out.println("hexists-->" + bool);
		// redis命令:hlen key
		Long length = redisTemplate.opsForHash().size("book");
		System.out.println("hlen-->" + length);
		// redis命令:hkeys key - 展示key对应的所有字段名称
		Set set = redisTemplate.opsForHash().keys("book");
		System.out.println("hkeys-->" + set);
		// redis命令:hvals key - 展示key对应的所有字段的值
		List values = redisTemplate.opsForHash().values("book");
		System.out.println("hvals-->" + values);
		// redis命令:hgetall key - field and value
		Map bookmap = redisTemplate.opsForHash().entries("book");
		System.out.println("hgetall-->" + bookmap);
	}
4.5,链表数据结构。
/**
 * 链表数据结构
 */
	@Test
	public void linkedType(){
		// redis命令:lpush key value1 value2...
		redisTemplate.opsForList().leftPush("book", "c++");
		redisTemplate.opsForList().leftPushAll("book", "c", "java");
		// redis命令:rpush key value1 value2
		redisTemplate.opsForList().rightPush("book", "mysql");
		redisTemplate.opsForList().rightPushAll("book", "oracle", "sqlserver");
		// redis命令:lindex key index
		String book0 = (String) redisTemplate.opsForList().index("book", 0);
		System.out.println("lindex-->" + book0);
		// redis命令:llen key
		Long bookLen = redisTemplate.opsForList().size("book");
		System.out.println("llen-->" + bookLen);
		// redis命令:lpop key
		String leftBook = (String) redisTemplate.opsForList().leftPop("book");
		System.out.println("lpop-->" + leftBook);
		// redis命令:rpop key
		String rightBook = (String) redisTemplate.opsForList().rightPop("book");
		System.out.println("rpop-->" + rightBook);
		// redis命令:linsert key before|after oldnode newnode
		redisTemplate.opsForList().leftPush("book", "java", "pythod");
		redisTemplate.opsForList().rightPush("book", "java", "jquery");
		// redis命令:lrange key start end
		List rangeList = redisTemplate.opsForList().range("book", 0, redisTemplate.opsForList().size("book") - 1);
		System.out.println("lrange-->" + rangeList);
		// redis命令:lset key index value
		redisTemplate.opsForList().set("book", 0, "db");
		// redis命令:ltrim key start end
		redisTemplate.opsForList().trim("book", 1, 3);
		// redis命令:lrange key start end
		List rangeList2 = redisTemplate.opsForList().range("book", 0, redisTemplate.opsForList().size("book") - 1);
		System.out.println("lrange-->" + rangeList2);
	}
4.6,集合操作类型。
/**
* 集合操作
 */
	@Test
	public void setType(){
		// redis命令:sadd
		redisTemplate.opsForSet().add("person", "小明","小红","小刚");
		// redis命令:scard
		Long person = redisTemplate.opsForSet().size("person");
		System.out.println("scard-->" + person);
		// redis命令:smembers
		Set set = redisTemplate.opsForSet().members("person");
		System.out.println("smembers-->" + set);
	}
4.7,有序集合操作类型
/**
* 有序集合
*/
	@Test
	public void zsetType(){
		redisTemplate.opsForZSet().add("book", "mysql", 1.5);
		redisTemplate.opsForZSet().add("book", "java", 8.5);
		redisTemplate.opsForZSet().add("book", "html", 10.5);
		Set set = redisTemplate.opsForZSet().range("book", 0, redisTemplate.opsForZSet().size("book") - 1);
		System.out.println(set);
	}
五,redis高级应用
5.1,事务
 与其他NoSQL不同,Redis是存在事务的,尽管没有数据库那么强大,但是还是非常有用,尤其是在高并发的情况中,使用redis的事务可以保证数据一致性的同时,大幅度提高数据读写的响应速度。
 redis的事务是使用multi-exec的命令组合,使用它可以提供两个重要保证:
 1、事务是一个被隔离的操作,事务中的方法都会被redis进行序列化并按顺序执行,事务在执行的过程中不会被其他客户端的发出的命令所打断。
 2、事务是一个原子性操作,它要么全部执行、要么全部不执行。
 事务的常用命令:
 multi:开启事务,之后的命令就会进入队列,而不是马上执行。
 watch key1 [key2]...:监听某些键,当被监听的键在提交事务前被修改,则事务会回滚 (基于乐观锁机制)。
 unwatch key1 [key2]...:取消监听。
 exec:执行事务,如果被监听的键没有被修改,则采用提交命令,否则就执行回滚命令。
 discard:回滚事务。
 事务的开启及提交如下图:
	    
 从上图中看出,当开始事务时进行操作,命令并不会马上执行,而是放在队列中,只有在事务提交后才会执行。
 但是,要注意注意:redis中,如果遇到格式正确而数据类型不符合的情况时,不会进行事务回滚。这是什么意思,如下图操作所示:
	
 问题描述:比如我要保存1000金额到内存中,但是我不小心将金额输入成1000a,后面多了一个a。但是同样保存到了队列中。最后当提交事务的时候便会报错,可是1000a还是保存到了内存,证明事务并没有回滚。
 redis中存在监听机制,可以监听某一个key。
	
/**
* redis的事务管理,要保证事务的开启和提交是同一条连接。
*/
	@Test
	public void transcation(){
		List results = (List) redisTemplate.execute(new SessionCallback() {
			@Override
			public Object execute(RedisOperations redisOperations) throws DataAccessException {
		//开启事务
		redisOperations.multi();
		//进行操作
		redisOperations.opsForValue().set("name", "张三");
		redisOperations.opsForValue().get("name");
		//提交事务
		List result = redisOperations.exec();
		return result;
	}
});
	System.out.println("-->" + results);
}
5.2,流水线
 在现实情况中,redis的读写速度十分快,而系统的瓶颈往往是在网络通信中的延迟。redis可能会再很多时候处于空闲状态而等待命令的到达。为了解决这个问题,可以使用redis的流水线,流水线是一种通讯协议,类似一个队列批量执行一组命令。
 由于这种情况在实际工作中较少使用,所以就简短介绍一下。
/**
* 流水线
*/
	@Test
	public void pipelined(){
		//流水线
		long begin = System.currentTimeMillis();
		redisTemplate.executePipelined(new SessionCallback() {
			@Override
			public Object execute(RedisOperations redisOperations) throws DataAccessException {
				for (int i = 0; i < 100000; i++) {
					redisOperations.opsForValue().set("key" + i, "value" + i);
					redisOperations.opsForValue().get("key" + i);
				}
				return null;
			}
		});
		long end = System.currentTimeMillis();
		System.out.println("耗时:" + (end - begin));
	}
5.3,发布订阅
 说到发布订阅是否会想到RabbitMQ等消息中间件?
 但是redis的发布订阅具有实时性,当发布者改变数据时,订阅者便会接收到更改后的消息。
 使用命令:
 subscribe chat:订阅chat渠道。
 publish chat "message:发布消息到chat渠道。
5.3.1,定义监听类:
/**
 * Redis消息监听器
 */
public class RedisMessageListener implements MessageListener{
	private RedisTemplate template;
	@Override
	public void onMessage(Message message, byte[] pattern) {
		//获取渠道名称
		System.out.println("渠道名称:" + new String(pattern));
		//获得消息
		byte[] body = message.getBody();
		//获得值序列化转换器
		String msg = (String) template.getValueSerializer().deserialize(body);
		System.out.println("消息为:" + msg);
	}
	public RedisTemplate getTemplate() {
		return template;
	}
	public void setTemplate(RedisTemplate template) {
		this.template = template;
	}
}
5.3.2,配置文件配置监听
<!-- 配置监听器 -->
   	<bean id="redisMsgListener" class="com.yx.redis.RedisMessageListener">
   		<!-- 注入redis模板 -->
   		<property name="template" ref="template"></property>
   	</bean>
   	<!-- 配置监听容器 -->
   	<bean id="topicContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer" destroy-method="destroy">
   		<!-- redis连接工厂 -->
   		<property name="connectionFactory" ref="connectionFactory"/>
   		<!-- 配置连接池,连接池生存才能持续监听 -->
   		<property name="taskExecutor">
   			<bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
   				<property name="poolSize" value="3"/>
   			</bean>
   		</property>
   		<!-- 配置消息监听 -->
   		<property name="messageListeners">
   			<map>
   				<!-- key-ref和监听器的id保持一致 -->
   				<entry key-ref="redisMsgListener">
   					<bean class="org.springframework.data.redis.listener.ChannelTopic">
   						<!-- 定义渠道 -->
   						<constructor-arg value="yx"/>
   					</bean>
   				</entry>
   			</map>
   		</property>
   	</bean>
5.3.3,发布消息
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-*.xml");
RedisTemplate redistemp = context.getBean(RedisTemplate.class);
redistemp.convertAndSend("yx", "Hello");
5.4,超时时间
 在redis中的超时时间时非常重要的,因为我们的内存时有限的,在一段时间内如果没有对一些数据进行处理。那便会产生很多的垃圾数据,因此对数据进行时间 上的设置是一种较好的习惯。
 这里先暂时不讲述过期时间的原理,后面会与大家分享,还请关注哦~~~
 超时时间的命令:
 persist key:持久化key,即得永生(移除key的超时时间)。
 expire key seconds:设置超时时间,单位为秒。
 ttl key:查看key的超时时间,单位为秒,返回-1表示没有超时时间,如果key不存在或者已经超时,则返回-2。
 pttl key:查看key的超时时间,单位为毫秒。
 pexpire key milliseconds:设置key的超时时间,以毫秒为单位。
 关于超时时间需要有几点注意:当一个key过了超时时间以后,并不会立刻从内存中移除。在以下情况下数据会被清除。
 1、当要获得key的值的时候,比如执行了get key命令。
 2、系统自己会有一个定时器,每隔1秒,扫描一次内存。清除超时的key(不会完全扫描所有的key,不会完全的移除所有超时的key)。
 3、内存已满,就会根据配置文件进行内存数据的清理。
@Test
	public void expire() throws ParseException {
		redisTemplate.opsForValue().set("name", "小明");
		//设置超时时间 - 5 ~ 10分钟
		redisTemplate.expire("name", 10, TimeUnit.SECONDS);
		//设置超时时间到指定的时间
		String time = "2019-08-23 12:00:00";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date date = sdf.parse(time);
		redisTemplate.expireAt("name", date);
		//移除超时时间
		redisTemplate.persist("name");
		//获得还能活多久
		redisTemplate.getExpire("name");
		String name = (String) redisTemplate.opsForValue().get("name");
		System.out.println(name);
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	 	System.out.println(name);
	}
六,总结
 写到这里redis的基本使用也差不多了,但是仍有很多技术点没有记录到。比如与Lua语言的结合使用,redis的持久化,内存的淘汰策略,读写分离(哨兵模式),集群等。
 如果你看到这篇博客,以上没有分享的内容会在后续发布的,还请关注~
 最后,以上内容均是自主学习的总结,如有错误或者不合适的地方欢迎留言(或者邮箱)指教。
 感谢观看!
Redis的常用命令与Java整合及高级应用篇的更多相关文章
- Jedis对Redis的常用命令操作
		本篇主要总结一些Jedis对Redis的常用命令操作: 1.对key操作命令 2.对String操作命令 3.对List操作命令 4.对Set操作命令 5.对Hash操作命令 6.排序操作指令 一.项 ... 
- Linux下安装redis以及常用命令
		https://blog.csdn.net/zgf19930504/article/details/51850594 安装: 1.获取redis资源 wget http://download.redi ... 
- maven3常用命令、java项目搭建、web项目搭建详细图解(转)
		转自:http://blog.csdn.net/edward0830ly/article/details/8748986 maven3常用命令.java项目搭建.web项目搭建详细图解 2013-0 ... 
- maven3常用命令、java项目搭建、web项目搭建详细图解
		http://blog.csdn.net/edward0830ly/article/details/8748986 ------------------------------maven3常用命令-- ... 
- memcached学习——常用命令+基于java客户端的3种简单实现(二)
		常用命令: memcached设计的原则就是简单,所以支持的命令也不是特别多~ 1.查看memcached的状态,主要用于分析内存的使用状况.优化内存分配等 stats 查看memcached的运行状 ... 
- 05_NoSQL数据库之Redis数据库:Redis的常用命令,键值相关命令和服务器相关命令
		 Redis常用命令 Redis提供了丰富的命令对数据库和各种数据库类型进行操作,这些命令可以再Linux终端使用. 键值相关命令: Keys:返回满足给定pattern的所有key 用表达式*表 ... 
- 【redis】常用命令
		三.常用命令 1)连接操作命令 quit:关闭连接(connection) auth:简单密码认证 help cmd: 查看cmd帮助,例如:help quit ... 
- maven3常用命令、java项目搭建、web项目搭建
		------------------------------maven3常用命令--------------------------- 1.常用命令 1)创建一个Project mvn archety ... 
- Redis(2):常用命令详解
		redis命令不区分大小写 通用命令:1. 获得符合规则的键名列表: keys pattern 其中pattern符合glob风格 ? (一个字符) * (任意个字符) [] (匹配其中的任意一 ... 
随机推荐
- while 循环,运算符,字符串的格式化
			1.while 关键字 (死循环) while 条件: 循环体 条件:只要条件是 Ture就可以循环. while 空格 条件 冒号 缩进 循环体 while else while 空格 条件 冒号 ... 
- 嵌入式web服务器BOA的移植及应用
			嵌入式web服务器子系统 一.嵌入式web服务器的控制流程 如下图所示,嵌入式web服务器可实现通过网络远程控制嵌入式开发板,便捷实用. 控制流程:浏览器 --->>>嵌入式开发板 ... 
- Excel催化剂开源第45波-按原图大小导出图片
			从Excel中导出图片,是一个很常规的需求,也有一些久旧不衰的界面操作法小技巧从OpenXml文件中批量导出,在VBA开发中,也会使用Chart对象的背景图的技巧来导出.总体来说,和真正想要的效果还是 ... 
- 自定义View之开关
			资源文件 首先我们需要有两个图片文件,分别是开关的背景和开关的滑块 自定义View 1.写一个类继承View 2.copy该类的全路径名在布局文件使用, 3.找到这个控件,设置初始信息 4.根据需求绘 ... 
- Flask项目常见面试问题
			一.你的项目中缓存粒度是如何选择的? 缓存粒度一共分为4种. 1.缓存某个数值:一个键只保存一个值,性价比较低,使用率低,如果存储的话我们使用redis的String 2.缓存数据对象:数据库记录对应 ... 
- 在WebApi项目里使用MiniProfiler并且分析 Entity Framework Core
			在WebApi项目里使用MiniProfiler并且分析 Entity Framework Core 一.安装配置MiniProfiler 在现有的ASP.NET Core MVC WebApi 项目 ... 
- gawk(awk)的用法案例
			gawk(awk)的用法案例 本文首先简单介绍一个gawk和awk的区别,然后是一点基本使用流程,最后是自己做的一个分析数据文件的脚本代码,供大家参考.另外想了解基本流程的入门知识的可以下载附件pdf ... 
- npm常用命令(原创)
			1.对于我们下载下来的node包,假设该包存在依赖情况执行: npm install(或者npm i) 下载依赖包: 下载依赖成功过后,文件夹内会产生package-lock.json文件: 2.下载 ... 
- php之布尔类型判断
			字符串只要不为空且不为0都为true 执行结果为 执行结果为false 因为===不仅比较值,还比较类型,所以输出为false.如果使用===号比较,最好先将变量强转为bool类型,不然可能得不到想要 ... 
- 基于ReentrantLock的非公平锁理解AQS
			AQS AQS概述  AbstractQueuedSynchronizer抽象队列同步器简称AQS,它是实现同步器的基础组件,juc下面Lock的实现以及一些并发工具类就是通过AQS来实现的,这里我 ... 
