文章目录

  • 使用Redis缓存数据
  • 管理员相关数据表

使用Redis缓存数据

使用Redis可以提高查询效率,一定程度上可以减轻数据库服务器的压力,从而保护了数据库。

通常,应用Redis的场景有:

  • 高频查询,例如:热搜列表、秒杀
  • 改变频率低的数据,例如:商品类别

一旦使用Redis,就会导致Redis和数据库中都存在同样的数据,当数据发生变化时,可能出现不一致的问题!

所以,还有某些数据在特定的场景中不能使用Redis:

  • 要求数据必须是准确的:下单购买时要求库存是准确的

    • 如果每次库存发生变化时都更新了Redis中的库存值,保证了Redis中的数据是准确的,也可以使用
  • 数据的修改频率很高,且对数据准确性有一定要求

需要学会评估是否要求数据一定保持一致!

要使用Redis缓存数据,至少需要:

  • 开发新的组件,实现对Redis中的数据访问

    • 此组件并不是必须的,因为访问Redis数据的API都非常简单,自定义组件时,组件中的每个方法可能也只有少量代码,甚至只有1行代码
    • 如果直接将访问Redis的代码写在Service中,首次开发时会更省事,但不利于维护
    • 【推荐】如果将访问Redis的代码写的新的组件中,首次开发时会更麻烦,但利于维护
  • 在Service中调用新的组件,在Service中决定何时访问MySQL,何时访问Redis

在使用Redis之前,还必须明确一些问题:

  • 哪些查询功能改为从Redis中获取数据
  • Redis中的数据从哪里来

暂定目标:

  • 根据类别的id查询类别详情,改为从Redis中获取数据
  • 优先从Redis中获取数据,如果Redis中没有,则从MySQL中获取,且获取到数据后,将数据存入到Redis中,所以,经过首次查询后,Redis中将存在此数据,后续每一次都可以直接从Redis中拿到必要的数据

cn.tedu.csmall.product.webapi.repository创建ICategoryRedisRepository接口,并在接口中添加抽象方法:

public interface ICategoryRedisRepository {String KEY_CATEGORY_ITEM_PREFIX = "categories:item:";// 将类别详情存入到Redis中void save(CategoryDetailsVO category);// 根据类别id获取类别详情CategoryDetailsVO getDetailsById(Long id);}

然后在cn.tedu.csmall.product.webapi.repository.impl创建CategoryRedisRepositoryImpl(接口的实现类),实现以上接口:

@Repository
public class CategoryRedisRepositoryImpl implements ICategoryRedisRepository {@Autowiredprivate RedisTemplate<String, Serilizalbe> redisTemplate;@Overridepublic void save(CategoryDetailsVO category) {String key = KEY_CATEGORY_ITEM_PREFIX + category.getId();redisTemplate.opsForValue().set(key, category);}@Overridepublic CategoryDetailsVO getDetailsById(Long id) {String key = KEY_CATEGORY_ITEM_PREFIX + id;Serializable result = redisTemplate.opsForValue().get(key);if (result == null) {return null;} else {CategoryDetailsVO category = (CategoryDetailsVO) result;return category;}}
}

完成后,测试:

package cn.tedu.csmall.product.webapi.repository;import cn.tedu.csmall.pojo.vo.CategoryDetailsVO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class CategoryRedisRepositoryTests {@AutowiredICategoryRedisRepository repository;@Testvoid testGetDetailsByIdSuccessfully() {testSave();Long id = 10L;CategoryDetailsVO category = repository.getDetailsById(id);Assertions.assertNotNull(category);}@Testvoid testGetDetailsByIdReturnNull() {Long id = -1L;CategoryDetailsVO category = repository.getDetailsById(id);Assertions.assertNull(category);}private void testSave() {CategoryDetailsVO category = new CategoryDetailsVO();category.setId(10L);category.setName("家用电器");Assertions.assertDoesNotThrow(() -> {repository.save(category);});}
}

然后,需要修改CategoryServiceImpl中的实现:

@Autowired
private ICategoryRedisRepository categoryRedisRepository;@Override
public CategoryDetailsVO getDetailsById(Long id) {// ===== 以下是原有代码,只从数据库中获取数据 =====// CategoryDetailsVO category = categoryMapper.getDetailsById(id);// if (category == null) {//     throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,//             "获取类别详情失败,尝试访问的数据不存在!");// }// return category;// ===== 以下是新的业务,将从Redis中获取数据 =====// 从repsotiroy中调用方法,根据id获取缓存的数据// 判断缓存中是否存在与此id对应的key// 有:表示明确的存入过某数据,此数据可能是有效数据,也可能是null// -- 判断此key对应的数据是否为null// -- 是:表示明确的存入了null值,则此id对应的数据确实不存在,则抛出异常// -- 否:表示明确的存入了有效数据,则返回此数据即可// 无:表示从未向缓存中写入此id对应的数据,在数据库中,此id可能存在数据,也可能不存在// 从mapper中调用方法,根据id获取数据库的数据// 判断从数据库中获取的结果是否为null// 是:数据库也没有此数据,先向缓存中写入错误数据(null),再抛出异常// 将从数据库中查询到的结果存入到缓存中// 返回查询结果
}

为了避免缓存穿透,需要在ICategoryRedisRepository中添加2个抽象方法:

/*** 判断是否存在id对应的缓存数据** @param id 类别id* @return 存在则返回true,否则返回false*/
boolean exists(Long id);/*** 向缓存中写入某id对应的空数据(null),此方法主要用于解决缓存穿透问题** @param id 类别id*/
void saveEmptyValue(Long id);

并在CategoryRedisRepositoryImpl中补充实现:

@Override
public boolean exists(Long id) {String key = KEY_CATEGORY_ITEM_PREFIX + id;return redisTemplate.hasKey(key);
}@Override
public void saveEmptyValue(Long id) {String key = KEY_CATEGORY_ITEM_PREFIX + id;redisTemplate.opsForValue().set(key, null);
}

业务中的具体实现为:

@Override
public CategoryDetailsVO getDetailsById(Long id) {// ===== 以下是原有代码,只从数据库中获取数据 =====// CategoryDetailsVO category = categoryMapper.getDetailsById(id);// if (category == null) {//     throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,//             "获取类别详情失败,尝试访问的数据不存在!");// }// return category;// ===== 以下是新的业务,将从Redis中获取数据 =====log.debug("根据id({})获取类别详情……", id);// 从repository中调用方法,根据id获取缓存的数据// 判断缓存中是否存在与此id对应的keyboolean exists = categoryRedisRepository.exists(id);if (exists) {// 有:表示明确的存入过某数据,此数据可能是有效数据,也可能是null// -- 判断此key对应的数据是否为nullCategoryDetailsVO cacheResult = categoryRedisRepository.getDetailsById(id);if (cacheResult == null) {// -- 是:表示明确的存入了null值,则此id对应的数据确实不存在,则抛出异常log.warn("在缓存中存在此id()对应的Key,却是null值,则抛出异常", id);throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"获取类别详情失败,尝试访问的数据不存在!");} else {// -- 否:表示明确的存入了有效数据,则返回此数据即可return cacheResult;}}// 缓存中没有此id匹配的数据// 从mapper中调用方法,根据id获取数据库的数据log.debug("没有命中缓存,则从数据库查询数据……");CategoryDetailsVO dbResult = categoryMapper.getDetailsById(id);// 判断从数据库中获取的结果是否为nullif (dbResult == null) {// 是:数据库也没有此数据,先向缓存中写入错误数据,再抛出异常log.warn("数据库中也无此数据(id={}),先向缓存中写入错误数据", id);categoryRedisRepository.saveEmptyValue(id);log.warn("抛出异常");throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"获取类别详情失败,尝试访问的数据不存在!");}// 将从数据库中查询到的结果存入到缓存中log.debug("已经从数据库查询到匹配的数据,将数据存入缓存……");categoryRedisRepository.save(dbResult);// 返回查询结果log.debug("返回查询到数据:{}", dbResult);return dbResult;
}

许多缓存数据应该是服务器刚刚启动就直接写入到Redis中的,当后续客户端访问时,缓存中已经存在的数据可以直接响应,避免获取数据时缓存中还没有对应的数据,还需要从数据库中查询。

在服务器刚刚启动时就加载需要缓存的数据并写入到Redis中,这种做法称之为缓存预热。

需要解决的问题有:

  • 需要实现开机启动时自动执行某个任务
  • 哪些数据需要写入到缓存中,例如全部“类别”数据

在Spring Boot中,可以自定义某个组件类,实现ApplicationRunner即可,例如:

package cn.tedu.csmall.product.webapi.app;import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;@Component
public class CachePreLoad implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("CachePreLoad.run()");}}

为了将全部“类别”写入到缓存中,首先,需要能够从数据库中查询到全部数据,则需要:

  • CategoryMapper接口中添加:List<CategoryDetailsVO> list();
  • CategoryMapper.xml中配置以上抽象方法映射的SQL语句

然后,还需要实现将查询到的List<CategoryDetailsVO>写入到Redis中,则需要:

  • ICategoryRedisRepository接口中添加:void save(List<CategoryDetailsVO> categories);
  • CategoryRedisRepositoryImpl中实现以上方法
    • 存入时,Key值可以是:categories:list

由于向Redis中存入列表数据始终是“追加”的,且Redis中的数据并不会因为项目重启而消失,所以,如果反复启动项目,会在Redis的列表中反复追加重复的数据!为了避免此问题,应该在每次缓存预热之间先删除现有数据,所以,还需要:

  • ICategoryRedisRepository接口中添加:Boolean deleteList();
  • CategoryRedisRepositoryImpl中实现以上方法

从设计的角度,Service是可以调用数据访问层的组件的,即可以调用Mapper或其它Repository组件,换言之,Mapper和其它Repository组件应该只被Service调用

所以,应该在ICategoryService中定义“预热类别数据的缓存”的抽象方法:

void preloadCache();

另外,在Redis中存入了整个“类别”的列表后,也只能一次性拿到整个列表,不便于根据“类别”的id获取指定的数据,反之,如果每个“类别”数据都独立的存入到Redis中,当需要获取整个列表时,也只能把每个数据都找出来,然后再在Java程序中存入到List集合中,操作也是不方便的,所以,当需要更加关注效率时,应该将类别数据存2份到Redis中,一份是整个列表,另一份是若干个独立的类别数据。

目前,在缓存中存入独立的各个类别数据,在预热时并没有清除这些数据,如果在数据库中删除了数据,但缓存中的数据仍存在,为了避免这样的错误,应该在预热时,补充“删除所有类别”的功能!

则在ICategoryRedisRepository中添加void deleteAllItem();方法,用于删除所有独立的类别数据。

相关代码:ICategoryRedisRepository

package cn.tedu.csmall.product.webapi.repository;import cn.tedu.csmall.pojo.vo.CategoryDetailsVO;import java.util.List;public interface ICategoryRedisRepository {/*** 类别数据的KEY的前缀*/String KEY_CATEGORY_ITEM_PREFIX = "categories:item:";/*** 类别列表的KEY*/String KEY_CATEGORY_LIST = "categories:list";/*** 判断是否存在id对应的缓存数据** @param id 类别id* @return 存在则返回true,否则返回false*/Boolean exists(Long id);/*** 向缓存中写入某id对应的空数据(null),此方法主要用于解决缓存穿透问题** @param id 类别id*/void saveEmptyValue(Long id);/*** 将类别详情存入到Redis中** @param category 类别详情*/void save(CategoryDetailsVO category);/*** 将类别的列表存入到Redis中** @param categories 类别列表*/void save(List<CategoryDetailsVO> categories);/*** 删除Redis中各独立存储的类别数据*/void deleteAllItem();/*** 删除Redis中的类别列表* @return 如果成功删除,则返回true,否则返回false*/Boolean deleteList();/*** 根据类别id获取类别详情** @param id 类别id* @return 匹配的类别详情,如果没有匹配的数据,则返回null*/CategoryDetailsVO getDetailsById(Long id);}

相关代码:CategoryRedisRepositoryImpl

package cn.tedu.csmall.product.webapi.repository.impl;import cn.tedu.csmall.pojo.vo.CategoryDetailsVO;
import cn.tedu.csmall.product.webapi.repository.ICategoryRedisRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Repository
public class CategoryRedisRepositoryImpl implements ICategoryRedisRepository {@Autowiredprivate RedisTemplate<String, Serializable> redisTemplate;@Overridepublic Boolean exists(Long id) {String key = KEY_CATEGORY_ITEM_PREFIX + id;return redisTemplate.hasKey(key);}@Overridepublic void saveEmptyValue(Long id) {String key = KEY_CATEGORY_ITEM_PREFIX + id;redisTemplate.opsForValue().set(key, null, 30, TimeUnit.SECONDS);}@Overridepublic void save(CategoryDetailsVO category) {String key = KEY_CATEGORY_ITEM_PREFIX + category.getId();redisTemplate.opsForValue().set(key, category);}@Overridepublic void save(List<CategoryDetailsVO> categories) {for (CategoryDetailsVO category : categories) {redisTemplate.opsForList().rightPush(KEY_CATEGORY_LIST, category);}}@Overridepublic void deleteAllItem() {Set<String> keys = redisTemplate.keys(KEY_CATEGORY_ITEM_PREFIX + "*");redisTemplate.delete(keys);}@Overridepublic Boolean deleteList() {return redisTemplate.delete(KEY_CATEGORY_LIST);}@Overridepublic CategoryDetailsVO getDetailsById(Long id) {String key = KEY_CATEGORY_ITEM_PREFIX + id;Serializable result = redisTemplate.opsForValue().get(key);if (result == null) {return null;} else {CategoryDetailsVO category = (CategoryDetailsVO) result;return category;}}
}

相关代码:缓存预热的业务代码(以下方法的声明在ICategoryService接口中,以下代码是CategoryServiceImpl中重写的方法):

@Override
public void preloadCache() {log.debug("删除缓存中的类别列表……");categoryRedisRepository.deleteList();log.debug("删除缓存中的各独立的类别数据……");categoryRedisRepository.deleteAllItem();log.debug("从数据库查询类别列表……");List<CategoryDetailsVO> list = categoryMapper.list();for (CategoryDetailsVO category : list) {log.debug("查询结果:{}", category);log.debug("将当前类别存入到Redis:{}", category);categoryRedisRepository.save(category);}log.debug("将类别列表写入到Redis……");categoryRedisRepository.save(list);log.debug("将类别列表写入到Redis完成!");
}

相关代码:缓存预热类(CachePreLoad):

package cn.tedu.csmall.product.webapi.app;import cn.tedu.csmall.product.service.ICategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;@Component
@Slf4j
public class CachePreLoad implements ApplicationRunner {@Autowiredprivate ICategoryService categoryService;@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("CachePreLoad.run()");log.debug("准备执行缓存预热……");categoryService.preloadCache();log.debug("缓存预热完成!");}}

管理员相关数据表

管理员及权限的管理,涉及的数据表有:

-- 数据库:mall_ams-- 权限表:创建数据表
drop table if exists ams_permission;
create table ams_permission (id bigint unsigned auto_increment,name varchar(50) default null comment '名称',value varchar(255) default null comment '值',description varchar(255) default null comment '描述',sort tinyint unsigned default 0 comment '自定义排序序号',gmt_create datetime default null comment '数据创建时间',gmt_modified datetime default null comment '数据最后修改时间',primary key (id)
) comment '权限表' charset utf8mb4;-- 权限表:插入测试数据
insert into ams_permission (name, value, description) values
('商品-商品管理-读取', '/pms/product/read', '读取商品数据,含列表、详情、查询等'),
('商品-商品管理-编辑', '/pms/product/update', '修改商品数据'),
('商品-商品管理-删除', '/pms/product/delete', '删除商品数据'),
('后台管理-管理员-读取', '/ams/admin/read', '读取管理员数据,含列表、详情、查询等'),
('后台管理-管理员-编辑', '/ams/admin/update', '编辑管理员数据'),
('后台管理-管理员-删除', '/ams/admin/delete', '删除管理员数据');-- 角色表:创建数据表
drop table if exists ams_role;
create table ams_role (id bigint unsigned auto_increment,name varchar(50) default null comment '名称',description varchar(255) default null comment '描述',sort tinyint unsigned default 0 comment '自定义排序序号',gmt_create datetime default null comment '数据创建时间',gmt_modified datetime default null comment '数据最后修改时间',primary key (id)
) comment '角色表' charset utf8mb4;-- 角色表:插入测试数据
insert into ams_role (name) values('超级管理员'), ('系统管理员'), ('商品管理员'), ('订单管理员');-- 角色权限关联表:创建数据表
drop table if exists ams_role_permission;
create table ams_role_permission (id bigint unsigned auto_increment,role_id bigint unsigned default null comment '角色id',permission_id bigint unsigned default null comment '权限id',gmt_create datetime default null comment '数据创建时间',gmt_modified datetime default null comment '数据最后修改时间',primary key (id)
) comment '角色权限关联表' charset utf8mb4;-- 角色权限关联表:插入测试数据
insert into ams_role_permission (role_id, permission_id) values(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6),(2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6),(3, 1), (3, 2), (3, 3);-- 管理员表:创建数据表
drop table if exists ams_admin;
create table ams_admin (id bigint unsigned auto_increment,username varchar(50) default null unique comment '用户名',password char(64) default null comment '密码(密文)',nickname varchar(50) default null comment '昵称',avatar varchar(255) default null comment '头像URL',phone varchar(50) default null unique comment '手机号码',email varchar(50) default null unique comment '电子邮箱',description varchar(255) default null comment '描述',is_enable tinyint unsigned default 0 comment '是否启用,1=启用,0=未启用',last_login_ip varchar(50) default null comment '最后登录IP地址(冗余)',login_count int unsigned default 0 comment '累计登录次数(冗余)',gmt_last_login datetime default null comment '最后登录时间(冗余)',gmt_create datetime default null comment '数据创建时间',gmt_modified datetime default null comment '数据最后修改时间',primary key (id)
) comment '管理员表' charset utf8mb4;-- 管理员表:插入测试数据
insert into ams_admin (username, password, nickname, email, description, is_enable) values('root', '1234', 'root', 'root@tedu.cn', '最高管理员', 1),('super_admin', '1234', 'administrator', 'admin@tedu.cn', '超级管理员', 1),('nobody', '1234', '无名', 'liucs@tedu.cn', null, 0);-- 管理员角色关联表:创建数据表
drop table if exists ams_admin_role;
create table ams_admin_role (id bigint unsigned auto_increment,admin_id bigint unsigned default null comment '管理员id',role_id bigint unsigned default null comment '角色id',gmt_create datetime default null comment '数据创建时间',gmt_modified datetime default null comment '数据最后修改时间',primary key (id)
) comment '管理员角色关联表' charset utf8mb4;-- 管理员角色关联表:插入测试数据
insert into ams_admin_role (admin_id, role_id) values(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (2, 4), (3, 3);-- 查询示例:查询id=1的管理员的权限
select distinct ams_permission.value from ams_permission
left join ams_role_permission on ams_role_permission.permission_id=ams_permission.id
left join ams_role on ams_role_permission.role_id=ams_role.id
left join ams_admin_role on ams_admin_role.role_id=ams_role.id
left join ams_admin on ams_admin_role.admin_id=ams_admin.id
where ams_admin.id=1
order by ams_permission.value;-- 管理员登录日志表:创建数据表
drop table if exists ams_login_log;
create table ams_login_log (id bigint unsigned auto_increment,admin_id bigint unsigned default null comment '管理员id',username varchar(50) default null comment '管理员用户名(冗余)',nickname varchar(50) default null comment '管理员昵称(冗余)',ip varchar(50) default null comment '登录IP地址',user_agent varchar(255) default null comment '浏览器内核',gmt_login datetime default null comment '登录时间',gmt_create datetime default null comment '数据创建时间',gmt_modified datetime default null comment '数据最后修改时间',primary key (id)
) comment '管理员登录日志表' charset utf8mb4;-- 管理员登录日志表:插入测试数据
insert into ams_login_log (admin_id, username, nickname, ip, user_agent, gmt_login) values(1, 'root', 'root', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15', DATE_SUB(NOW(), interval 1 day)),(2, 'root', 'root', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15', DATE_SUB(NOW(), interval 12 hour)),(3, 'root', 'root', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15', NOW());-- 查看数据表结构
desc ams_permission; desc ams_role; desc ams_role_permission; desc ams_admin; desc ams_admin_role; desc ams_login_log;

当某个管理员尝试登录时,必须实现”根据用户名查询此管理员的信息,至少包括id、密码、权限“,需要执行的SQL语句大致是:

-- 管理员表 admin
-- 角色表 role
-- 管理员与角色关联表 admin_role (admin_id, role_id)
-- 权限表 permission
-- 角色与权限关联表 role_permission (role_id, permission_id)
-- 【根据用户名查询管理员,且必须查出对应的权限】
selectams_admin.id,ams_admin.username,ams_admin.password,ams_admin.is_enable,ams_permission.value
from ams_admin
left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
left join ams_role_permission on ams_admin_role.role_id = ams_role_permission.role_id
left join ams_permission on ams_role_permission.permission_id = ams_permission.id
where username='root';

接下来,在根项目中创建csmall-admin模块(与csmall-product类似),并在其下创建csmall-admin-servicecsmall-admin-webapi这2个子模块(与csmall-product的2个子模块类似),然后,尽量在csmall-admin-webapi中实现以上查询功能:

public interface AdminMapper {AdminLoginVO findByUsername(String username);
}

05-使用Redis缓存数据,管理员相关数据表相关推荐

  1. 在php中存redis数据,redis缓存都存哪些数据

    字符串string: 字符串类型是Redis中最为基础的数据存储类型,是一个由字节组成的序列,他在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据货Json对象描 ...

  2. java清空redis缓存数据库_java相关:Spring Cache手动清理Redis缓存

    java相关:Spring Cache手动清理Redis缓存 发布于 2020-4-24| 复制链接 摘记: 这篇文章主要介绍了Spring Cache手动清理Redis缓存,文中通过示例代码介绍的非 ...

  3. SAP 常用MRP元素数据来源相关数据库表和视图总结

    MRP 是SAP的核心要素之一,MRP元素信息会集中显示在MD04中,但是SAP内部没有专门的数据表集中存取此数据,在我们程序开发中业务单位会直接告诉我们,我要MD04 界面上的某些字段数据,本文根据 ...

  4. 常用的激光雷达数据及相关数据

    1.ICESAT/GLAS 2003年发射,2009年失效. 同轨激光足印间距170 m,激光光斑直径60-70 m. 532 1064 nm两个波段 2.ICESAT2/ATLAS 2018年发射( ...

  5. 【Redis学习03】redis缓存及其更新策略

    文章目录 1. 什么是缓存 2. 添加redis缓存 2.1 缓存商铺信息 3. 缓存更新策略 3.1 缓存更新策略方法 3.2 主动更新策略 3.3 缓存更新策略总结 4. 对商铺查询的缓存添加超时 ...

  6. 大数据就是非相关数据的相关性

    大数据的意义在于从海量的数据里寻找出一定的相关性,然后推演出行为方式的可能性.从这个角度来看,很多人所谈的大数据是相关数据的优化和整理概念,压根不是一回事 对大数据我不是专家,但由于大数据也是开放性的 ...

  7. java雪崩_【并发编程】java 如何解决redis缓存穿透、缓存雪崩(高性能示例代码)...

    [并发编程]java 如何解决redis缓存穿透.缓存雪崩(高性能示例代码) 发布时间:2018-11-22 16:48, 浏览次数:872 , 标签: java redis <>缓存穿透 ...

  8. java中注解操作redis_spring boot —— redis 缓存注解使用教程

    spring boot -- redis 缓存注解使用教程 依赖 在pom文件添加如下依赖 org.springframework.boot spring-boot-starter-data-redi ...

  9. python文本框与数据库的关联_Python 基于python+mysql浅谈redis缓存设计与数据库关联数据处理...

    基于python+mysql浅谈redis缓存设计与数据库关联数据处理 by:授客 QQ:1033553122 测试环境 redis-3.0.7 CentOS 6.5-x86_64 python 3. ...

最新文章

  1. poj2965-poj2965-The Pilots Brothers' refrigerator
  2. Dev-C++ v5.11
  3. 【maven插件】flatten-maven-plugin : 处理版本占位符
  4. AE学习笔记——第二章:AE图层中的图层用法及基本操作
  5. 华为谷歌安装器 Android6.0,GO谷歌安装器华为
  6. 如何获取变量token的值
  7. cocos2dx怎样设置ios和Android横屏竖屏的几种方法
  8. java正则表达式 后顾,正则表达式:从Copy到手写
  9. 【基础知识】进程通信之共享内存+信号量
  10. 使用Node.JS,如何将JSON文件读入(服务器)内存?
  11. xmind思维导图(下载、使用——超级详细)
  12. 网络层 IPV4报文格式
  13. 电影海报页面设计Html5,重设100部经典电影海报字体,好设计是这样炼成的!
  14. bmob php支付,GitHub - bmob/bmob-php-sdk: PHP SDK相关源码
  15. 线性回归的多重共线性问题及其解决
  16. ubuntu 18.04 安装 搜狗拼音输入法只有中文标点,没有文字
  17. Debian修改时区
  18. 对象及变量的并发访问(案例加总结)--学习笔记
  19. Distilling Knowledge via Knowledge Review论文和源码阅读笔记
  20. Candence PCB 原理图绘制

热门文章

  1. 打了三年ACM,拿了几个金牌
  2. 计算机科学增刊能检索,SCI增刊是否能检索和被认可
  3. eclipse快捷键、设置及常用插件
  4. html百度静态页面代码,静态百度页面_html/css_WEB-ITnose
  5. 学习绘制专题地图的收获与心得
  6. jquery操作手册
  7. Django之爱鲜蜂项目开发 day03 (一)
  8. 【微信小程序】基于SpringBoot开发后端
  9. nginx--如何基于rpm包进行离线安装及配置(Linux)
  10. 视频剪辑工作者的福音,视频格式转换工具4Videosoft Video Converter Ultimate的介绍使用,可以转换所有的视频格式