若使用redis,如何保证redis和数据库同步问题
配置redis
redis:
url: redis://iampw@192.168.198.129:6379
@cacheable
用于查询数据时,先从缓存中查找数据,如果缓存中有数据,则直接返回缓存中的数据,不会再去查询数据库;如果缓存中没有数据,则去数据库中查询数据,并将查询结果存入缓存中。
@cacheput
用于更新数据时,先更新数据库中的数据,然后再将更新后的数据存入缓存中,以便下次查询时可以直接从缓存中获取更新后的数据,提高查询效率。
@Override
@Cacheable(cacheNames = "find",key = "0")
public JSONResult find() {
log.info("数据库启动了...");
List<Parking> list =list();
//将查询到的小区名称赋给车位表
list.forEach(parking-> {
Integer community_id = parking.getCommunity_id();
String name = communityFegin.findNameById(community_id).getData().toString();
System.out.println("----------"+name);
parking.setCommunity_name(name);
});
return new JSONResult(true,null,200,list);
}
@Override
@CachePut(cacheNames = "find" ,key = "0")
public JSONResult delete(int id){
removeById(id);
return find();
}
将索引存放在一个地方(list,zset),具体对象存放在一个地方(string)
具体思路
判断缓存中是否存在数据
- 如果不存在数据
- 从数据库中查询数据,并分别将索引和具体对象放到缓存中
- 如果存在数据
- 根据索引获取对应具体对象
- 如果对应对象过期了,根据对应索引查询该条对象重新存放到redis中
- 如果对应对象没过期,直接从redis中查询该条数据
@Slf4j
@Service
public class ParkingServiceImpl extends ServiceImpl<ParkingMapper, Parking>
implements ParkingService {
@Resource
private CommunityFegin communityFegin;
@Resource
private StringRedisTemplate stringRedisTemplate;
//所有索引的前缀
private static final String ALL_PARKING_KEY = "ALL_PARKING_KEY";
//每个索引的前缀
private static final String PREFIX_PARKING = "PREFIX_PARKING::";
@Override
public JSONResult find() {
//索引存放到list中
ListOperations<String, String> keysOps = stringRedisTemplate.opsForList();
//具体对象存放到string中
ValueOperations<String, String> valuesOps = stringRedisTemplate.opsForValue();
//遍历索引的集合
List<String> keys = keysOps.range(ALL_PARKING_KEY, 0, -1);
//1.如果redis中没有数据
if (keys == null || keys.size() == 0) {
log.info("数据库启动了...");
//1.1从数据库中获取数据
List<Parking> list = list();
//将查询到的小区名称赋给车位表
list.forEach(parking -> {
//1.2 将索引存放到list中
keysOps.leftPush(ALL_PARKING_KEY, PREFIX_PARKING + parking.getId());
//1.3 将具体对象存放到string中,并设置过期时间
valuesOps.set(PREFIX_PARKING + parking.getId(), JSONArray.toJSONString(parking), 1, TimeUnit.DAYS);
});
return new JSONResult<>(true, null, 200, list);
}
//2.如果redis中有数据
List<Parking> parkings = new ArrayList<>();
keys.forEach(key -> {
//2.1 根据索引获得索引对应的对象
String value = valuesOps.get(key);
//2.1.1 对应对象为空的话
if (value == null) {
//单独查询该对象并放到redis中
// 获取key对应的id,并转为int型
int id = Integer.parseInt(key.replace(PREFIX_PARKING, ""));
//根据id查询车位
Parking parking = baseMapper.selectById(id);
//根据小区id查询小区
Integer community_id = parking.getCommunity_id();
String name = communityFegin.findNameById(community_id).getData().toString();
parking.setCommunity_name(name);
parkings.add(parking);
//存放到redis中
valuesOps.set(PREFIX_PARKING + parking.getId(), JSONArray.toJSONString(parking), 1, TimeUnit.DAYS);
} else {//2.1.2对应对象不为空的话
//直接查询
Parking parking = JSONArray.parseObject(value, Parking.class);
parkings.add(parking);
}
});
return new JSONResult<>(true, null, 200, parkings);
}
@Override
public JSONResult delete(int id) {
//索引存放到list中
ListOperations<String, String> keysOps = stringRedisTemplate.opsForList();
//具体对象存放到string中
ValueOperations<String, String> valuesOps = stringRedisTemplate.opsForValue();
//数据库如果有数据从数据库删除
boolean flag = removeById(id);
//从redis中删除
if (flag) {
// 更新redis
keysOps.remove(ALL_PARKING_KEY, 1, PREFIX_PARKING + id);
valuesOps.getAndDelete(PREFIX_PARKING + id);
}
return new JSONResult<>(true, null, 200, "删除成功!");
}
}
添加以下代码:
PageHelper.startPage(page, 3);
问题1:当分页查询时,redis会缓存首次查询的数据,此时出现问题:无论查询第几页都是最先开始缓存到redis中的那一页,该如何解决?
答: 此问题是因为判断缓存中数据时,缓存中的数据为所有页数据;
- 解决:判断时,判断查询时的数据为目标页数据即可
//本页前面页的数据
int begin =(page-1)*3;
//本页数据
int end =page*3;
//遍历索引的集合
List<String> keys = keysOps.range(ALL_PARKING_KEY, begin, end);
if (keys == null || keys.size() == 0)
问题2: 如果当第一次查询的数据从第二页开始,redis会缓存第二页数据为第一页,此时该如何解决?
初始化一个跟记录总数一样大小的数组,设置对应的值都null,查询某一页时,看该页对应记录数redis的值是否null,如果为null,从数据库查询,并替换为数据对应Id…
具体思路
- 如果是第一次查询
- 进行初始化数组操作,只在第一次查询时执行
- 将初次查询的数据,填充id和具体对象到具体位置
- 不是第一次查询
- 判断该页对应的值是否有null
- 如果有null:连接数据库填充数据
- 如果没有null:直接查询数据即可
如果是第一次查询
//判断是否为第一次查询
if (keyList == null || keyList.size() == 0) {
//分页查询
PageHelper.startPage(page, 3);
List<Parking> parkings = list();
PageInfo<Parking> parkingPageInfo = new PageInfo<>(parkings);
//总条数
long total = parkingPageInfo.getTotal();
//填充
for (int i = 0; i < total; i++) {
keysOps.leftPush(ALL_PARKING_KEY, "null");
}
//将初次查询的数据,填充进具体对象中
int begin = (page - 1) * 3;
for (Parking parking: parkings){
//将查询到的小区名称赋给车位表
Integer community_id = parking.getCommunity_id();
String name = communityFegin.findNameById(community_id).getData().toString();
parking.setCommunity_name(name);
//改变对应id
keysOps.set(ALL_PARKING_KEY,begin,PREFIX_PARKING+parking.getId());
//填充value
valuesOps.set(PREFIX_PARKING+parking.getId(),JSONArray.toJSONString(parking),1,TimeUnit.DAYS );
begin++;
}
return new JSONResult<>(true, null, 200, parkings);
如果不是第一次查询
//不是第一次查询的话,定位到此次查询的位置
int begin = (page-1)*3;
List<String> keyList2 = keysOps.range(ALL_PARKING_KEY, begin, begin + 2);
//查询对应的数据是否为null
for (String s : keyList2) {
if ("null".equals(s)) {
//连接数据库填充数据
log.info("数据库启动了...");
//1.1从数据库中获取数据
PageHelper.startPage(page, 3);
List<Parking> parkings = list();
for (Parking parking: parkings){
//将查询到的小区名称赋给车位表
Integer community_id = parking.getCommunity_id();
String name = communityFegin.findNameById(community_id).getData().toString();
parking.setCommunity_name(name);
//改变对应id
keysOps.set(ALL_PARKING_KEY,begin,PREFIX_PARKING+parking.getId());
//填充value
valuesOps.set(PREFIX_PARKING+parking.getId(),JSONArray.toJSONString(parking),1,TimeUnit.DAYS );
begin++;
return new JSONResult<>(true, null, 200, parkings);
}
}
List<Parking> parkings = new ArrayList<>();
keyList2.forEach(key -> {
//2.1 根据索引获得索引对应的对象
String value = valuesOps.get(key);
//2.1.1 对应对象为空的话
if (value == null) {
// 单独查询该对象并放到redis中
// 获取key对应的id,并转为int型
int id = Integer.parseInt(key.replace(PREFIX_PARKING, ""));
//根据id查询车位
Parking parking = baseMapper.selectById(id);
//根据小区id查询小区
Integer community_id = parking.getCommunity_id();
String name = communityFegin.findNameById(community_id).getData().toString();
parking.setCommunity_name(name);
parkings.add(parking);
//存放到redis中
valuesOps.set(PREFIX_PARKING + parking.getId(), JSONArray.toJSONString(parking), 1, TimeUnit.DAYS);
} else {//2.1.2对应对象不为空的话
//直接查询
Parking parking = JSONArray.parseObject(value, Parking.class);
parkings.add(parking);
}
});
return new JSONResult<>(true, null, 200, parkings);
总代码
public JSONResult find(int page) {
//索引存放到list中
ListOperations<String, String> keysOps = stringRedisTemplate.opsForList();
//具体对象存放到string中
ValueOperations<String, String> valuesOps = stringRedisTemplate.opsForValue();
//遍历索引
List<String> keyList = keysOps.range(ALL_PARKING_KEY, 0, -1);
//判断是否为第一次查询
if (keyList == null || keyList.size() == 0) {
PageHelper.startPage(page, 3);
List<Parking> parkings = list();
PageInfo<Parking> parkingPageInfo = new PageInfo<>(parkings);
//总条数
long total = parkingPageInfo.getTotal();
//填充
for (int i = 0; i < total; i++) {
keysOps.leftPush(ALL_PARKING_KEY, "null");
}
//将初次查询的数据,填充进具体对象中
int begin = (page - 1) * 3;
for (Parking parking: parkings){
//将查询到的小区名称赋给车位表
Integer community_id = parking.getCommunity_id();
String name = communityFegin.findNameById(community_id).getData().toString();
parking.setCommunity_name(name);
//改变对应id
keysOps.set(ALL_PARKING_KEY,begin,PREFIX_PARKING+parking.getId());
//填充value
valuesOps.set(PREFIX_PARKING+parking.getId(),JSONArray.toJSONString(parking),1,TimeUnit.DAYS );
begin++;
}
return new JSONResult<>(true, null, 200, parkings);
}
//不是第一次查询的话,定位到此次查询的位置
int begin = (page-1)*3;
List<String> keyList2 = keysOps.range(ALL_PARKING_KEY, begin, begin + 2);
//查询对应的数据是否为null
for (String s : keyList2) {
if ("null".equals(s)) {
//连接数据库填充数据
log.info("数据库启动了...");
//1.1从数据库中获取数据
PageHelper.startPage(page, 3);
List<Parking> parkings = list();
for (Parking parking: parkings){
//将查询到的小区名称赋给车位表
Integer community_id = parking.getCommunity_id();
String name = communityFegin.findNameById(community_id).getData().toString();
parking.setCommunity_name(name);
//改变对应id
keysOps.set(ALL_PARKING_KEY,begin,PREFIX_PARKING+parking.getId());
//填充value
valuesOps.set(PREFIX_PARKING+parking.getId(),JSONArray.toJSONString(parking),1,TimeUnit.DAYS );
begin++;
}
return new JSONResult<>(true, null, 200, parkings);
}
}
List<Parking> parkings = new ArrayList<>();
keyList2.forEach(key -> {
//2.1 根据索引获得索引对应的对象
String value = valuesOps.get(key);
//2.1.1 对应对象为空的话
if (value == null) {
//单独查询该对象并放到redis中
// 获取key对应的id,并转为int型
int id = Integer.parseInt(key.replace(PREFIX_PARKING, ""));
//根据id查询车位
Parking parking = baseMapper.selectById(id);
//根据小区id查询小区
Integer community_id = parking.getCommunity_id();
String name = communityFegin.findNameById(community_id).getData().toString();
parking.setCommunity_name(name);
parkings.add(parking);
//存放到redis中
valuesOps.set(PREFIX_PARKING + parking.getId(), JSONArray.toJSONString(parking), 1, TimeUnit.DAYS);
} else {//2.1.2对应对象不为空的话
//直接查询
Parking parking = JSONArray.parseObject(value, Parking.class);
parkings.add(parking);
}
});
return new JSONResult<>(true, null, 200, parkings);
}