首页 > 技术文章 > e3mall商城的归纳总结6之redis

xiaofeng88 2019-01-19 16:41 原文

一、说在前面的话

前面几节我们主要对该项目的后端进行了增删改查,但是所有的数据都是存放在数据库中,这样的话数据库的压力显而易见是很大的,因此本节学习nosql的缓存,也就是redis的使用,在使用之前,我们来想一想为什么要用redis、redis的内存是否有限制,redis的存储方式。那么让我们带着这些问题去学习redis。
Redis服务器的搭建和使用。

二、redis

1、redis的安装

Redis是c语言开发的。
安装redis需要c语言的编译环境。如果没有gcc需要在线安装。(上一节nginx也是c开发的,因此也安装了c++编译环境)
yum install gcc-c++
安装步骤:
第一步:redis的源码包上传到linux系统。
第二步:解压缩redis。
第三步:编译。进入redis源码目录。make
第四步:安装。make install PREFIX=/usr/local/redis
PREFIX参数指定redis的安装目录。一般软件安装到/usr目录下

2、redis的启动

前端启动:在redis的安装目录下直接启动redis-server(一般不采取此方法)
[root@localhost bin]# ./redis-server
后台启动:
把/root/redis-3.0.0/redis.conf复制到/usr/local/redis/bin目录下
[root@localhost redis-3.0.0]# ==cp redis.conf /usr/local/redis/bin/ == (为了启动的后面直接跟配置文件)
修改配置文件:daemonize: yes

daemonize:yes:
redis采用的是单进程多线程的模式。当redis.conf中选项daemonize设置成yes时,代表开启守护进程模式。在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。
daemonize:no:
当daemonize选项设置成no时,当前界面将进入redis的命令行界面,exit强制退出或者关闭连接工具(putty,xshell等)都会导致redis进程退出。
老版本默认是no,新版本现在默认是yes。

最后我们启动的时候在后面跟上配置文件即可
[root@localhost bin]# ./redis-server redis.conf
redis启动命令
查看redis进程:
[root@localhost bin]# ps aux|grep redis
root 5190 0.1 0.3 33936 1712 ? Ssl 18:23 0:00./redis-server :6379
root 5196 0.0 0.1 4356 728 pts/0 S+ 18:24 0:00 grep redis
redis启动查询

3、redis的命令行连接和图形化连接

a、命令连接
[root@localhost bin]# ./redis-cli
默认连接localhost运行在6379端口的redis服务。
[root@localhost bin]# ./redis-cli -h 192.168.25.153 -p 6379
-h:连接的服务器的地址(host)
-p:服务的端口号(port)
b、图形化连接
使用软件RedisDesktopManager进行连接redis
redis连接界面
redis图形化界面
c、redis的存储数据类型
redis有5中存储数据类型,每一种类型的数据都以String的形式进行保存到redis缓存中的。
——1、String:key-value(做缓存、可以使用ttl过期时间,因此经常被用到用户登录、商品缓存(使用TTL有效的使热门数据存放到redis缓存中,从而避免内存浪费的可能。)、订单提交等模块中)故经常使用
get、set、
incr:加一(生成id)
Decr:减一
key-value测试
当然只有value为数值才可以增减,若为字母,则报错
字母增减报错
接下来看看图形化界面是什么样的?
在这里插入图片描述
——2、Hash:key-fields-values(做缓存)也经常使用(这里因为hash没法使用ttl过期,所以一般用在用户的购物车等模块中。)
相当于一个key对于一个map,map中还有key-value
使用hash对key进行归类。
Hset:向hash中添加内容
Hget:从hash中取内容
hash测试数据
在图形化界面我们再看看什么样的
在这里插入图片描述
3、list 有顺序可重复
lrange list1 0 -1 代表输出list1集合,从第0个开始,一直到最后一个(0代表第一个、-1代表最后一个)。

192.168.25.153:6379> lpush list1 a b c d
(integer) 4
192.168.25.153:6379> lrange list1 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
192.168.25.153:6379> rpush list1 1 2 3 4
(integer) 8
192.168.25.153:6379> lrange list1 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
5) "1"
6) "2"
7) "3"
8) "4"
192.168.25.153:6379> 
192.168.25.153:6379> lpop list1 
"d"
192.168.25.153:6379> lrange list1 0 -1
1) "c"
2) "b"
3) "a"
4) "1"
5) "2"
6) "3"
7) "4"
192.168.25.153:6379> rpop list1
"4"
192.168.25.153:6379> lrange list1 0 -1
1) "c"
2) "b"
3) "a"
4) "1"
5) "2"
6) "3"

4、set 元素无顺序,不能重复

192.168.25.153:6379> sadd set1 a b c c c d
(integer) 4
192.168.25.153:6379> smembers set1
1) "b"
2) "c"
3) "d"
4) "a"
192.168.25.153:6379> srem set1 a
(integer) 1
192.168.25.153:6379> smembers set1
1) "b"
2) "c"
3) "d"

5、SortedSet(zset):有顺序,不能重复

192.168.25.153:6379> zadd zset1 2 a 5 b 1 c 6 d
(integer) 4
192.168.25.153:6379> zrange zset1 0 -1
1) "c"
2) "a"
3) "b"
4) "d"
192.168.25.153:6379> zrem zset1 a
(integer) 1
192.168.25.153:6379> zrange zset1 0 -1
1) "c"
2) "b"
3) "d"
192.168.25.153:6379> zrevrange zset1 0 -1
1) "d"
2) "b"
3) "c"
192.168.25.153:6379> zrange zset1 0 -1 withscores
1) "c"
2) "1"
3) "b"
4) "5"
5) "d"
6) "6"
192.168.25.153:6379> zrevrange zset1 0 -1 withscores
1) "d"
2) "6"
3) "b"
4) "5"
5) "c"
6) "1"

d、key命令(不可以对hash的key值设置过期时间)
设置key的过期时间。
Expire key second:设置key的过期时间
Ttl key:查看key的有效期
Persist key:清除key的过期时间。Key持久化。

192.168.25.153:6379> expire Hello 100
(integer) 1
192.168.25.153:6379> ttl Hello
(integer) 77
在这里插入图片描述
只能对整个hash设置过期时间。
在这里插入图片描述
消除key的过期时间:
PERSIST hstr
在这里插入图片描述

6、redis在项目中的使用

我们在项目需要使用到redis,那么我们该如何去连接呢,redis给我们提供了连接的jar包jedis,我们需要把它引入到其中,添加依赖。建议添加到服务层中。
由于redis含有单机版和集群版(集群版搭建链接),因此这里创建了一个接口,把单机版和集群版都实现这个接口(JedisClient ),若我们使用单机版,则在配置文件中配置单机版的设置,反之配置集群版的设置。单机版的实现类和集群版的实现类内容不一样。但是我们都是基于接口进行操作的。这样的话开发和上线就不需要更换代码了,只需要将配置文件单机版和集群版修改即可。
JedisClient 接口:

package cn.tsu.e3mall.jedis;
import java.util.List;
public interface JedisClient {
	String set(String key, String value);
	String get(String key);
	Boolean exists(String key);
	Long expire(String key, int seconds);
	Long ttl(String key);
	Long incr(String key);
	Long hset(String key, String field, String value);
	String hget(String key, String field);
	Long hdel(String key, String... field);
	Boolean hexists(String key, String field);
	List<String> hvals(String key);
	Long del(String key);
}

JedisClientPool(单机版)
单机版的获取Pool后,需要getResource,然后就可以得到jedis,就可以操作set,get命令。

package cn.tsu.e3mall.jedis;

import java.util.List;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;


public class JedisClientPool implements JedisClient {
	private JedisPool jedisPool;

	public JedisPool getJedisPool() {
		return jedisPool;
	}

	public void setJedisPool(JedisPool jedisPool) {
		this.jedisPool = jedisPool;
	}

	@Override
	public String set(String key, String value) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.set(key, value);
		jedis.close();
		return result;
	}

	@Override
	public String get(String key) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.get(key);
		jedis.close();
		return result;
	}

	@Override
	public Boolean exists(String key) {
		Jedis jedis = jedisPool.getResource();
		Boolean result = jedis.exists(key);
		jedis.close();
		return result;
	}

	@Override
	public Long expire(String key, int seconds) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.expire(key, seconds);
		jedis.close();
		return result;
	}

	@Override
	public Long ttl(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.ttl(key);
		jedis.close();
		return result;
	}

	@Override
	public Long incr(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.incr(key);
		jedis.close();
		return result;
	}

	@Override
	public Long hset(String key, String field, String value) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hset(key, field, value);
		jedis.close();
		return result;
	}

	@Override
	public String hget(String key, String field) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.hget(key, field);
		jedis.close();
		return result;
	}

	@Override
	public Long hdel(String key, String... field) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hdel(key, field);
		jedis.close();
		return result;
	}

	@Override
	public Boolean hexists(String key, String field) {
		Jedis jedis = jedisPool.getResource();
		Boolean result = jedis.hexists(key, field);
		jedis.close();
		return result;
	}

	@Override
	public List<String> hvals(String key) {
		Jedis jedis = jedisPool.getResource();
		List<String> result = jedis.hvals(key);
		jedis.close();
		return result;
	}

	@Override
	public Long del(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.del(key);
		jedis.close();
		return result;
	}

}

JedisClientCluster(集群版)JedisClientCluster可以直接set、get

package cn.tsu.e3mall.jedis;

import java.util.List;

import redis.clients.jedis.JedisCluster;

public class JedisClientCluster implements JedisClient {

	private JedisCluster jedisCluster;
	

	public JedisCluster getJedisCluster() {
		return jedisCluster;
	}

	public void setJedisCluster(JedisCluster jedisCluster) {
		this.jedisCluster = jedisCluster;
	}

	@Override
	public String set(String key, String value) {
		return jedisCluster.set(key, value);
	}

	@Override
	public String get(String key) {
		return jedisCluster.get(key);
	}

	@Override
	public Boolean exists(String key) {
		return jedisCluster.exists(key);
	}

	@Override
	public Long expire(String key, int seconds) {
		return jedisCluster.expire(key, seconds);
	}

	@Override
	public Long ttl(String key) {
		return jedisCluster.ttl(key);
	}

	@Override
	public Long incr(String key) {
		return jedisCluster.incr(key);
	}

	@Override
	public Long hset(String key, String field, String value) {
		return jedisCluster.hset(key, field, value);
	}

	@Override
	public String hget(String key, String field) {
		return jedisCluster.hget(key, field);
	}

	@Override
	public Long hdel(String key, String... field) {
		return jedisCluster.hdel(key, field);
	}

	@Override
	public Boolean hexists(String key, String field) {
		return jedisCluster.hexists(key, field);
	}

	@Override
	public List<String> hvals(String key) {
		return jedisCluster.hvals(key);
	}

	@Override
	public Long del(String key) {
		return jedisCluster.del(key);
	}
}

applicationContext-redis.xml关于redis的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
	<!-- 1、搭建单机版redis -->
	<bean id="jedisClientPool" class="cn.tsu.e3mall.jedis.JedisClientPool"> 
		<property name="jedisPool" ref="jedisPool" />
	</bean>
	<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
		<constructor-arg name="host" value="192.168.25.110" />
		<constructor-arg name="port" value="6379" />
	</bean>
	
	<!--2、搭建集群版redis -->
	<!-- <bean id="jedisClientCluster" class="cn.tsu.e3mall.jedis.JedisClientCluster">
		<property name="jedisCluster" ref="jedisCluster" /> 
	</bean>
	<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
	<!--构造方法注入 -->
		<constructor-arg>
			<set>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.110" />
					<constructor-arg name="port" value="7001" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.110" />
					<constructor-arg name="port" value="7002" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.110" />
					<constructor-arg name="port" value="7003" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.110" />
					<constructor-arg name="port" value="7004" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.110" />
					<constructor-arg name="port" value="7005" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.110" />
					<constructor-arg name="port" value="7006" />
				</bean>
			</set>
		</constructor-arg>
	</bean> -->
</beans>

service层:(首页大广告的缓存(应该内容很少,故设置为永存,无TTL过期时间))
业务逻辑:
先从缓存中查询,若有返回数据
若缓存中没有,则去数据库查询,
然后再保存到缓存中。
添加缓存不能影响正常的逻辑,
因此都需要try{} catch()

// 首页回显广告
	@Override
	public List<TbContent> findad1List(Long category_id) {
		try {
			//先从redis中调取
			String jsonlist = jedisClient.hget(Commons.CONTENT_LIST, category_id+"");
			if (jsonlist !=null ) {
				List<TbContent> list = JsonUtils.jsonToList(jsonlist, TbContent.class);
				return list;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		TbContentExample example = new TbContentExample();
		example.createCriteria().andCategoryIdEqualTo(category_id);
		List<TbContent> list = contentMapper.selectByExample(example);
		try {
			jedisClient.hset(Commons.CONTENT_LIST, category_id+"", JsonUtils.objectToJson(list));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}

controller层:

	@Controller
	public class ContentController {
	@Autowired
	private ContentService contentService;
	@RequestMapping("/index")
	public String showIndex(Model model) {
		//回显大广告
		List<TbContent> ad1list = contentService.findad1List(89l);
		model.addAttribute("ad1List", ad1list);
		return "index";
		}
	}

推荐阅读