網站首頁 編程語言 正文
開始準備
開始之前我們需要有Redis安裝,我們采用本機Docker運行Redis, 主要命令如下
docker pull redis
docker run --name my_redis -d -p 6379:6379 redis
docker exec -it my_redis bash
redis-cli
前面兩個命令是啟動redis docker, 后兩個是連接到docker, 在使用redis-cli 去查看redis里面的內容,主要查看我們存在redis里面的數據。
RedisTemplate
我們先從RedisTemplate開始,這個是最好理解的一種方式,我之前在工作中也使用過這種方式,先看代碼示例 我們先定義一個POJO類
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Book implements Serializable {
private Long id;
private String name;
private String author;
}
一個很簡單的BOOK類,三個字段: id,name和author. 再來一個RedisTemplate的Bean
@Bean
public RedisTemplate<String, Book> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Book> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
再定義一個使用這個RedisTemplate的Service類
public Optional<Book> findOneBook(String name) {
HashOperations<String, String, Book> hashOperations = redisTemplate.opsForHash();
if (redisTemplate.hasKey(CACHE) && hashOperations.hasKey(CACHE, name)) {
log.info("Get book {} from Redis.", name);
return Optional.of(hashOperations.get(CACHE, name));
}
Optional<Book> book = bookRepository.getBook(name);
log.info("Book Found: {}", book);
if (book.isPresent()) {
log.info("Put book {} to Redis.", name);
hashOperations.put(CACHE, name, book.get());
redisTemplate.expire(CACHE, 10, TimeUnit.MINUTES);
}
return book;
}
我們使用Hash來存儲這個Book信息,在上面的方法中查找書名存不存在Redis中,如果存在就直接返回,如果不存在就去持久化存儲中找,找到就再通過Template寫入到Redis中, 這是緩存的通用做法。 使用起來感覺很方便。 我們這里為了簡單沒有使用持久化存儲,就硬編碼了幾條數據, 代碼如下
@Repository
public class BookRepository {
Map<String, Book> bookMap = new HashMap<>();
public BookRepository(){
bookMap.put("apache kafka", Book.builder()
.name("apache kafka").id(1L).author("zhangsan")
.build());
bookMap.put("python", Book.builder()
.name("python").id(2L).author("lisi")
.build());
}
public Optional<Book> getBook(String name){
if(bookMap.containsKey(name)){
return Optional.of(bookMap.get(name));
}
else{
return Optional.empty();
}
}
}
我們調用 bookService.findOneBook("python")和bookService.findOneBook("apache kafka"); 來把數據寫入到換存中
我們來看下存儲在Redis的數據長什么樣子。
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04book"
127.0.0.1:6379> type "\xac\xed\x00\x05t\x00\x04book"
hash
127.0.0.1:6379> hgetall "\xac\xed\x00\x05t\x00\x04book"
1) "\xac\xed\x00\x05t\x00\x06python"
2) "\xac\xed\x00\x05sr\x00&com.ken.redistemplatesample.model.Book=\x19\x96\xfb\x7f\x7f\xda\xbe\x02\x00\x03L\x00\x06authort\x00\x12Ljava/lang/String;L\x00\x02idt\x00\x10Ljava/lang/Long;L\x00\x04nameq\x00~\x00\x01xpt\x00\x04lisisr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00\x00\x00\x00\x02t\x00\x06python"
3) "\xac\xed\x00\x05t\x00\x0capache kafka"
4) "\xac\xed\x00\x05sr\x00&com.ken.redistemplatesample.model.Book=\x19\x96\xfb\x7f\x7f\xda\xbe\x02\x00\x03L\x00\x06authort\x00\x12Ljava/lang/String;L\x00\x02idt\x00\x10Ljava/lang/Long;L\x00\x04nameq\x00~\x00\x01xpt\x00\bzhangsansr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00\x00\x00\x00\x01t\x00\x0capache kafka"
我們可以看到數據被存在了key是“\xac\xed\x00\x05t\x00\x04book”的一個Hash表中, Hash里面有兩條記錄。 大家發現一個問題沒有? 就是這個key不是我們想象的用“book”做key,而是多了一串16進制的碼, 這是因為RedisTemplate使用了默認的JdkSerializationRedisSerializer 去序列化我們的key和value, 如果大家都用Java語言那沒有問題, 如果有人用Java語言寫,有人用別的語言讀,那就有問題,就像我開始的時候用hgetall "book"始終拿不到數據那樣。 RedisTemplate也提供了StringRedisTemplate來方便大家需要使用String來序列化redis里面的數據。簡單看下代碼
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
{
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
public Optional<String> getBookString(String name){
HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
if (stringRedisTemplate.hasKey(STRINGCACHE) && hashOperations.hasKey(STRINGCACHE, name)) {
log.info("Get book {} from Redis.", name);
return Optional.of(hashOperations.get(STRINGCACHE, name));
}
Optional<Book> book = bookRepository.getBook(name);
log.info("Book Found: {}", book);
if (book.isPresent()) {
log.info("Put book {} to Redis.", name);
hashOperations.put(STRINGCACHE, name, book.get().getAuthor());
stringRedisTemplate.expire(STRINGCACHE, 10, TimeUnit.MINUTES);
return Optional.of(book.get().getAuthor());
}
return Optional.empty();
}
使用上就沒有那么方便,你就得自己寫需要存的是哪個字段,讀出來是哪個字段。
127.0.0.1:6379> keys *
1) "string_book"
127.0.0.1:6379> hgetall string_book
1) "python"
2) "lisi"
3) "apache kafka"
4) "zhangsan"
如上圖所示,使用客戶端讀出來看起來就比較清爽一些。也可以看到占用的Size會小很多,我們這個例子相差7倍,如果是數據量大,這個還是比較大的浪費。
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04book"
2) "string_book"
127.0.0.1:6379> memory usage "string_book"
(integer) 104
127.0.0.1:6379> memory usage "\xac\xed\x00\x05t\x00\x04book"
(integer) 712
JPA Repository
我們知道使用JPA Repository來訪問DataBase的時候,增刪改查那樣的操作能夠很方便的實現,基本就是定義個接口,代碼都不用寫,Spring就幫我們完成了大部分的工作,那么訪問Redis是不是也可以這樣呢? 答案是肯定的,我們來看代碼 首先我們還是定義一個POJO
@RedisHash(value = "cache-book", timeToLive = 600)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CacheBook implements Serializable {
@Id
private Long userId;
@Indexed
private String name;
private String author;
}
這個類與我們上面template上面的類的區別就是我們加了兩個注解, 在類開頭加了 @RedisHash(value = "cache-book", timeToLive = 600) 在字段上面加了@Id和@Indexed 定義一個Repository的接口
public interface CacheBookRepository extends CrudRepository<CacheBook, Long> {
Optional<CacheBook> findOneByName(String name);
}
再定義一個service和上面那個例子template一樣,緩存中有就到緩存中拿,沒有就到持久化存儲中找,并寫入緩存
@Slf4j
@Service
public class BookService {
private static final String CACHE = "repository-book";
@Autowired
private CacheBookRepository cacheRepository;
@Autowired
private BookRepository bookRepository;
public Optional<CacheBook> findOneBook(String name) {
Optional<CacheBook> optionalCacheBook = cacheRepository.findOneByName(name);
if(!optionalCacheBook.isPresent())
{
Optional<CacheBook> book = bookRepository.getBook(name);
log.info("Book Found: {}", book);
if (book.isPresent()) {
log.info("Put book {} to Redis.", name);
cacheRepository.save(book.get());
}
return book;
}
return optionalCacheBook;
}
}
代碼很簡單,簡單到不敢相信是真的。 還是一樣,調用這個方法,我們來看存在Redis里面的數據
127.0.0.1:6379> keys *
1) "repository-book:2"
2) "repository-book:2:idx"
3) "repository-book"
4) "repository-book:name:apache kafka"
5) "repository-book:name:python"
6) "repository-book:1:idx"
7) "repository-book:1"
哇,感覺存的內容有些多, 不用怕我們來看下各自存什么數據 首先看最短的一個
127.0.0.1:6379> smembers repository-book
1) "1"
2) "2"
它里面存的是我們的id所有的value, 可以用來判斷id是否存在 再來看
127.0.0.1:6379> hgetall repository-book:2
1) "_class"
2) "com.ken.redisrepositorysample.model.CacheBook"
3) "author"
4) "lisi"
5) "name"
6) "python"
7) "userId"
8) "2"
這個是我們數據存放的地方
127.0.0.1:6379> smembers repository-book:1:idx
1) "repository-book:name:apache kafka"
127.0.0.1:6379> smembers "repository-book:name:apache kafka"
1) "1"
另外兩個都是set, 存在在它們里面的數據是索引信息。 由此可以看出通過JPA Repository 的方式,代碼很少,而且存儲的數據也很通用,個人覺得是比較理想的訪問方法。
Cache
我們已經看了兩種方式,在訪問的時候遵循這樣的模式:緩存中有就從緩存中返回,沒有就從持久化存儲中找,然后寫入緩存,這部分代碼我也不想自己寫,那么Cache就是你的救星。 我們先看代碼 我們這次使用內存數據庫H2作為持久化存儲, 放一個schema.sql在resouces下面
drop table t_book if exists;
create table t_book (
id bigint auto_increment,
create_time timestamp,
update_time timestamp,
name varchar(255),
author varchar(200),
primary key (id)
);
insert into t_book (name, author, create_time, update_time) values ('python', 'zhangsan', now(), now());
insert into t_book (name, author, create_time, update_time) values ('hadoop', 'lisi', now(), now());
insert into t_book (name, author, create_time, update_time) values ('java', 'wangwu', now(), now());
然后定義POJO
@Entity
@Table(name = "T_BOOK")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CacheBook implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String author;
@Column(updatable = false)
@CreationTimestamp
private Date createTime;
@UpdateTimestamp
private Date updateTime;
}
完全是和數據庫綁定的代碼,和緩存沒有任何關系 一個Repository來訪問數據庫
public interface BookRepository extends JpaRepository<CacheBook, Long> {
}
定義一個service來調用它
@Slf4j
@Service
@CacheConfig(cacheNames = "cache-book")
public class BookService {
@Autowired
private BookRepository bookRepository;
@Cacheable
public List<CacheBook> findAllCoffee() {
return bookRepository.findAll();
}
@CacheEvict
public void reloadCoffee() {
}
}
這里就比較關鍵了,在類上加上了注解 @CacheConfig(cacheNames = "cache-book") 在方法上面加上了Cacheable和CacheEvict, Cacheable這個方法就是用來實現邏輯,有就從緩存中拿,沒有就從數據庫拿的,CacheEvict是調用這個方法的時候清除緩存。 然后再啟動入口程序的地方加上注解 @EnableJpaRepositories @EnableCaching(proxyTargetClass = true) 在配置文件application.properties中加上
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
management.endpoints.web.exposure.include=*
spring.cache.type=redis
spring.cache.cache-names=cache-book
spring.cache.redis.time-to-live=600000
spring.cache.redis.cache-null-values=false
spring.redis.host=localhost
這樣就可以了, 感覺就是通過配置下就把緩存給完成了,非常的簡單 我們來看Redis中是怎么存的
127.0.0.1:6379> keys *
1) "cache-book::SimpleKey []"
127.0.0.1:6379> get "cache-book::SimpleKey []"
"\xac\xed\x00\x05sr\x00\x13java.util.ArrayListx\x81\xd2\x1d\x99\xc7a\x9d\x03\x00\x01I\x00\x04sizexp\x00\x00\x00\x03w\x04\x00\x00\x00\x03sr\x00(com.ken.rediscachesample.model.CacheBook\xec\xcbR=\xe1U\x9b\xf7\x02\x00\x05L\x00\x06authort\x00\x12Ljava/lang/String;L\x00\ncreateTimet\x00\x10Ljava/util/Date;L\x00\x02idt\x00\x10Ljava/lang/Long;L\x00\x04nameq\x00~\x00\x03L\x00\nupdateTimeq\x00~\x00\x04xpt\x00\bzhangsansr\x00\x12java.sql.Timestamp&\x18\xd5\xc8\x01S\xbfe\x02\x00\x01I\x00\x05nanosxr\x00\x0ejava.util.Datehj\x81\x01KYt\x19\x03\x00\x00xpw\b\x00\x00\x01\x84\xff]\x85\xb0x\b-\x81\x80sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00\x00\x00\x00\x01t\x00\x06pythonsq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b-\x81\x80sq\x00~\x00\x02t\x00\x04lisisq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b<\xbf\xd8sq\x00~\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x02t\x00\x06hadoopsq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b<\xbf\xd8sq\x00~\x00\x02t\x00\x06wangwusq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b<\xbf\xd8sq\x00~\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x03t\x00\x04javasq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b<\xbf\xd8x"
看到沒有,就是當成Redis里面的String來存的, 如果數據量比較小,那是非常的方便,如果數據量大,這種方式就有些問題了。
總結
我們看了這三種方式,這里僅僅是做了個入門,每個里面都有很多細節的地方需要去研究和使用,整體的感覺是要想使用的簡單,那么存儲在Redis中的數據就要量少,量大后,就需要自己來定制了,那基本上要用RedisTemplate來做一些工作。
原文鏈接:https://juejin.cn/post/7180290294481420348
相關推薦
- 2022-07-18 Linux如何設置SSH免密登錄
- 2023-04-26 numpy數組切片的使用_python
- 2022-06-15 python?OpenCV?圖像通道數判斷_python
- 2022-04-28 Python可視化學習之seaborn調色盤_python
- 2022-07-13 ELK 日志分析系統的部署
- 2022-06-18 android實現可拖動的浮動view_Android
- 2022-11-06 python?基本結構語句(函數和模塊)_python
- 2022-09-23 C#實現目錄跳轉(TreeView和SplitContainer)的示例代碼_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支