品优购前台首页轮播图广告
目前的系统已经实现了广告后台管理和广告前台展示,但是对于首页每天有大量的人访问,对数据库造成很大的访问压力,甚至是瘫痪。那如何解决呢?我们通常的做法有两种:一种是数据缓存、一种是网页静态化。
redis 是一款开源的 Key-Value 数据库,运行在内存中,由 ANSI C 编写。企业开发通常采用 Redis 来实现缓存。同类的产品还有 memcache 、memcached 、MongoDB 等。
Redis的高并发和快速原因很多,总结一下几点:
1. Redis是纯内存数据库,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在IO上,所以读取速度快。
2. 再说一下IO,Redis使用的是非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争。
3. Redis采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
4. 另外,数据结构也帮了不少忙,Redis全程使用hash结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
5. 还有一点,Redis采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大。
Jedis 是 Redis 官方推出的一款面向 Java 的客户端,提供了很多接口供 Java 语言调用。可以在 Redis 官网下载,当然还有一些开源爱好者提供的客户端,如 Jredis、SRP 等等,推荐使用 Jedis。
Spring-data-redis 是 spring 大家族的一部分,提供了在 srping 应用中通过简单的配置访问 redis 服务,对 reids 底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate 提供了 redis 各种操作、异常处理及序列化,支持发布订阅,并对 spring 3.1 cache 进行了实现。
spring-data-redis 针对 jedis 提供了如下功能:
1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类
2.针对 jedis 客户端中大量 api 进行了归类封装,将同一类型操作封装为 operation 接口
ValueOperations:简单 K-V 操作
SetOperations:set 类型数据操作
ZSetOperations:zset 类型数据操作
HashOperations:针对 map 类型的数据操作
ListOperations:针对 list 类型的数据操作
(注:若配置了依赖版本控制,则不用再写
redis.clients
jedis
2.8.1
org.springframework.data
spring-data-redis
1.7.2.RELEASE
maxIdle:最大空闲数
maxWaitMillis:连接时的最大等待毫秒数
testOnBorrow:在提取一个 jedis 实例时,是否提前进行验证操作;如果为 true,则得到的 jedis实例均是可用的;
# Redis settings
# server IP
redis.host=127.0.0.1
# server port
redis.port=6379
# server pass
redis.pass=
# use dbIndex
redis.database=0
# \u63A7\u5236\u4E00\u4E2Apool\u6700\u591A\u6709\u591A\u5C11\u4E2A\u72B6\u6001\u4E3Aidle(\u7A7A\u95F2\u7684)\u7684jedis\u5B9E\u4F8B
redis.maxIdle=300
# \u8868\u793A\u5F53borrow(\u5F15\u5165)\u4E00\u4E2Ajedis\u5B9E\u4F8B\u65F6\uFF0C\u6700\u5927\u7684\u7B49\u5F85\u65F6\u95F4\uFF0C\u5982\u679C\u8D85\u8FC7\u7B49\u5F85\u65F6\u95F4(\u6BEB\u79D2)\uFF0C\u5219\u76F4\u63A5\u629B\u51FAJedisConnectionException\uFF1B
redis.maxWait=3000
# \u5728borrow\u4E00\u4E2Ajedis\u5B9E\u4F8B\u65F6\uFF0C\u662F\u5426\u63D0\u524D\u8FDB\u884Cvalidate\u64CD\u4F5C\uFF1B\u5982\u679C\u4E3Atrue\uFF0C\u5219\u5F97\u5230\u7684jedis\u5B9E\u4F8B\u5747\u662F\u53EF\u7528\u7684
redis.testOnBorrow=true
在对数据库中的广告进行CRUD操作之后,都应该更新缓存数据!!!
package com.pinyougou.content.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.dubbo.config.annotation.Service;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.pinyougou.mapper.TbContentMapper;
import com.pinyougou.pojo.TbContent;
import com.pinyougou.pojo.TbContentExample;
import com.pinyougou.pojo.TbContentExample.Criteria;
import com.pinyougou.content.service.ContentService;
import entity.PageResult;
import org.springframework.data.redis.core.RedisTemplate;
/**
* 服务实现层
*/
@Service
public class ContentServiceImpl implements ContentService {
@Autowired
private TbContentMapper contentMapper;
/**
* 查询全部
*/
@Override
public List findAll() {
return contentMapper.selectByExample(null);
}
/**
* 按分页查询
*/
@Override
public PageResult findPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
Page page = (Page) contentMapper.selectByExample(null);
return new PageResult(page.getTotal(), page.getResult());
}
/**
* 增加
*/
@Override
public void add(TbContent content) {
//新增广告后清除缓存
redisTemplate.boundHashOps("content").delete(content.getCategoryId());
contentMapper.insert(content);
redisTemplate.boundHashOps("content").delete(content.getCategoryId());
}
/**
* 修改
*/
@Override
public void update(TbContent content) {
/**
* 修改广告后清除缓存,考虑到用户可能会修改广告的分类,
* 这样需要把原分类的缓存和新分类的缓存都清除掉
*/
Long categoryId = contentMapper.selectByPrimaryKey(content.getId()).getCategoryId();
redisTemplate.boundHashOps("content").delete(categoryId);
contentMapper.updateByPrimaryKey(content);
//如果分类 ID 发生了修改,清除修改后的分类 ID 的缓存
if (categoryId.longValue() != content.getCategoryId().longValue()) {
redisTemplate.boundHashOps("content").delete(content.getCategoryId());
}
}
/**
* 根据ID获取实体
*
* @param id
* @return
*/
@Override
public TbContent findOne(Long id) {
return contentMapper.selectByPrimaryKey(id);
}
/**
* 批量删除
*/
@Override
public void delete(Long[] ids) {
for (Long id : ids) {
//删除广告清除缓存,根据id查询缓存需要在数据库删除数据之前
Long categoryId = contentMapper.selectByPrimaryKey(id).getCategoryId();
redisTemplate.boundHashOps("content").delete(categoryId);
contentMapper.deleteByPrimaryKey(id);
}
}
@Override
public PageResult findPage(TbContent content, int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
TbContentExample example = new TbContentExample();
Criteria criteria = example.createCriteria();
if (content != null) {
if (content.getTitle() != null && content.getTitle().length() > 0) {
criteria.andTitleLike("%" + content.getTitle() + "%");
}
if (content.getUrl() != null && content.getUrl().length() > 0) {
criteria.andUrlLike("%" + content.getUrl() + "%");
}
if (content.getPic() != null && content.getPic().length() > 0) {
criteria.andPicLike("%" + content.getPic() + "%");
}
if (content.getStatus() != null && content.getStatus().length() > 0) {
criteria.andStatusLike("%" + content.getStatus() + "%");
}
}
Page page = (Page) contentMapper.selectByExample(example);
return new PageResult(page.getTotal(), page.getResult());
}
@Autowired
private RedisTemplate redisTemplate;
@Override
public List findByCategoryId(Long categoryId) {
List list = (List) redisTemplate.boundHashOps("content").get(categoryId);
if (list == null) {
System.out.println("从数据库查询数据");
TbContentExample contentExample = new TbContentExample();
Criteria criteria = contentExample.createCriteria();
criteria.andCategoryIdEqualTo(categoryId);//指定条件:id
criteria.andStatusEqualTo("1");//指定开启状态
contentExample.setOrderByClause("sort_order");//排序
list = contentMapper.selectByExample(contentExample);
} else {
System.out.println("从缓存中查数据");
}
return list;
}
}
效果如下:
管理员后台添加一条新广告,排序为1
前台页面实时更新,因为排序为1,所有第一张就是新添加的广告:
修改广告信息:
将一张广告状态改为有效
前台出现这张广告