第一个分布式项目总结(2)——Redis

作为一个处理高并发访问的项目,就如《大型网站技术架构 核心原理与案例分析》所说,加缓存是首选。而使用NoSQL数据库的中间件,有(MongoDB,Memcache,Redis。)在本项目中使用便是Redis
介绍下Redis
Redis是款开源的内存高速缓存数据库,由C语言编写(部署的时候需要gcc环境),本质上是一个K-V类型的内存数据库,具有非常出色的性能,每秒可以处理10w次读写操作。
相比于其他nosql:

优点: 具有支持多种数据结构(string,list,set,hash等),单value可以支持高达1GB的存储,支持持久化操作,性能优异。
缺点: Redis很容易受到物理内存的限制,不能用作海量数据的高性能读写。

本项目在选课这一高并发条件下,读写频率高,但数据量并不大,正是Redis最适合使用的一种情况。
有关Redis的其他详细信息网上有许多介绍,有的甚至深入底层,有兴趣的朋友可以搜搜看看,这里主要结合本项目的应用进行介绍。


结合项目,还是先上架构图。
第一个分布式项目总结(2)——Redis_第1张图片
在非选课这一具体操作下,对MySQL数据库压力最大的便是读操作,每次访问,浏览都需要对数据库进行读操作,从数据库获取数据。
但是创建数据表时放弃使用MyISAM这种对读操作更好的引擎。使用的InnoDB引擎,具有"行锁",所以须有对的写操作表现较好,其支持事务,对数据的质量保证较好。
所以首要就是对需要减轻数据库的读压力
思路: 通过Redis缓存需要的数据,每次数据读取向Redis获取,减少对数据库获取的频率,从而减少对数据库的读操作。即如架构图所示,所有服务层如需要获取数据都先对Redis请求。
实现: 需要连接Redis服务,本项目使用的Jedis,采用Jedis工具类,即通过创建Jedis对象(如果是集群版,请使用JedisPool,开发阶段本着方便,便宜,便宜,便宜的原则就用的单机版)对Redis进行操作。
连接Rides步骤如下:

(1)将安装redis服务的服务器的ip地址和redis的端口号作为构造参数传递给Jedis,用来创建一个Jedis对象
  Jedis jedis = new Jedis(ip,port);
(2)通过第一步创建的jedis对象,操作redis的5大数据类型(hash类型,string类型,list类型,set类型,zset类型,有序)
  jedis.set(string key,string value);
  jedis.get(string key);
(3)操作完成后关闭jedis连接
  jedis.close();

每用一次都得创建一次对象…这可不太好,而对对象的管理自然想到Spring,没错这东西能和Spring做整合,根据IOC控制反转,创建对象这种事情交给Spring就好了。


	<bean id="jedisClientPool" class="cn.eas.common.jedis.JedisClientPool">
		<property name="jedisPool" ref="jedisPool">property>
	bean>
	<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
		<constructor-arg name="host" value="Redis地址"/>
		<constructor-arg name="port" value="6379"/>
	bean>

OK,准备就绪看看我们的业务,首先查询缓存,缓存查询不到再查数据库。
可以表示成

1.查询缓存
2.缓存中没有,查询数据库-->2.1设置查询条件-->2.2执行查询获取查询结果
3.把结果添加到缓存-->3.1设置过期时间
4.返回查询结果

代码实现,课程查询为例

/**
	 * 课程查询
	 */
	@Override
	public TbCourse getCourseById(long courseId) {
		//查询缓存
				try {
					String json = jedisClient.get(REDIS_ITEM_PRE + ":" + courseId + ":BASE");
					if(StringUtils.isNotBlank(json)) {
						TbCourse tbCourse = JsonUtils.jsonToPojo(json, TbCourse.class);
						return tbCourse;
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
		//缓存中没有,查询数据库
		TbCourseExample example = new TbCourseExample();
		cn.eas.pojo.TbCourseExample.Criteria criteria = example.createCriteria();
		//设置查询条件
		criteria.andIdEqualTo(courseId);
		//执行查询
		List<TbCourse> list = courseMapper.selectByExample(example);
		if (list != null && list.size() > 0) {
			//把结果添加到缓存
			try {
				jedisClient.set(REDIS_ITEM_PRE + ":" + courseId + ":BASE", JsonUtils.objectToJson(list.get(0)));
				//设置过期时间
				jedisClient.expire(REDIS_ITEM_PRE + ":" + courseId + ":BASE", ITEM_CACHE_EXPIRE);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return list.get(0);
		}
		if(list != null && list.size() > 0){
			return list.get(0);
			}
		return null;
	}

这里将REDIS_ITEM_PRE这种比较常修改的参数,采用了软编码,通过加载resource.properties配置文件获取。
对于删除,修改这种"写"操作,就变现为先更新数据库,再将缓存的设置过期。后读取缓存时执行如上步骤。
这种使用方式我认为有如下
优点:
1.学习成本低…看看就会;
2.较为符合面向对象思想,且和Spring易于整合,连接Redis就去找Jedis对象…创建Jedis对象交给Spring来做。
缺点
1.代码量比较大…我这都是小功能好一些
2.需要导入Jar包,Maven构建的项目,依赖好使比较好管理的。

读操作解决了但是为了保证数据的质量,咋不能对缓存做写啊…所以如果是写操作,直接对MySQL数据库进行,缓存直接过期,重新获取,虽然表采用了InnoDB,但是在高频率写操作下依旧容易造成数据库压力过大 ,在本项目中后期部署时为解决该问题,采取了读写分离,进一步降低了数据库压力,在预期并发量下足够应对,如果并发量过大,可以采用kafka等中间的方式解决。如有兴趣网上资料也不少。当然选课这一高并发方式的解决办法不是这样,会在之后的博客进行介绍。

你可能感兴趣的:(项目总结)