已经开始预备
已经开始以后他们须要有redis加装,他们选用本机docker运转Redis, 主要就指示如下表所示
docker pull redisdocker run –name my_redis -d -p 6379:6379 redisdocker exec -it my_redis bashredis-cli拷贝标识符
后面四个指示是开启redis docker, 后四个是相连到docker, 在选用redis-cli 去查阅redis里头的文本,主要就查阅他们存有redis里头的统计数据。
RedisTemplate
他们先从RedisTemplate已经开始,那个是最合适认知的一类形式,我以后沃苏什卡也选用过此种形式,先看标识符实例 他们先表述四个POJO类
@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class Book implements Serializable {private Long id;private String name;private String author;拷贝标识符
四个很单纯的BOOK类,四个表头: id,name和author. 燕座RedisTemplate的Bean
@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template;拷贝标识符
再表述四个选用那个RedisTemplate的Service类
public Optional findOneBook(string name) {HashOperations 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 = 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中, 这是缓存的通用做法。 选用起来感觉很方便。 他们这里为了单纯没有选用持久化存储,就硬编码了几条统计数据, 标识符如下表所示
@Repositorypublic class BookRepository {Map 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 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”hash127.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里头的统计数据。单纯看下标识符
@Beanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;public Optional getBookString(String name){HashOperations 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 = 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_book1) “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) 104127.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@AllArgsConstructorpublic class CacheBook implements Serializable {@Idprivate Long userId;@Indexedprivate String name;private String author;拷贝标识符
那个类与他们上面template上面的类的区别就是他们加了四个注解, 在类开头加了 @RedisHash(value = “cache-book”, timeToLive = 600) 在表头上面加了@Id和@Indexed 表述四个Repository的接口
public interface CacheBookRepository extends CrudRepository {Optional findOneByName(String name);拷贝标识符
再表述四个service和上面那个例子template一样,缓存中有就到缓存中拿,没有就到持久化存储中找,并写入缓存
@Slf4j@Servicepublic class BookService {private static final String CACHE = “repository-book”;@Autowiredprivate CacheBookRepository cacheRepository;@Autowiredprivate BookRepository bookRepository;public Optional findOneBook(String name) {Optional optionalCacheBook = cacheRepository.findOneByName(name);if(!optionalCacheBook.isPresent())Optional 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-book1) “1”2) “2”拷贝标识符
它里头存的是他们的id所有的value, 可以用来判断id是否存有 再来看
127.0.0.1:6379> hgetall repository-book:21) “_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:idx1) “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@AllArgsConstructorpublic class CacheBook implements Serializable {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String author;@Column(updatable = false)@CreationTimestampprivate Date createTime;@UpdateTimestampprivate Date updateTime;拷贝标识符
完全是和统计数据库绑定的标识符,和缓存没有任何关系 四个Repository来出访统计数据库
public interface BookRepository extends JpaRepository {拷贝标识符
表述四个service来调用它
@Slf4j@Service@CacheConfig(cacheNames = “cache-book”)public class BookService {@Autowiredprivate BookRepository bookRepository;@Cacheablepublic List findAllCoffee() {return bookRepository.findAll();@CacheEvictpublic void reloadCoffee() {拷贝标识符
这里就比较关键了,在类上加上了注解 @CacheConfig(cacheNames = “cache-book”) 在方法上面加上了Cacheable和CacheEvict, Cacheable那个方法就是用来实现逻辑,有就从缓存中拿,没有就从统计数据库拿的,CacheEvict是调用这个方法的时候清除缓存。 然后再开启入口程序的地方加上注解 @EnableJpaRepositories @EnableCaching(proxyTargetClass = true) 在配置文件application.properties中加上
spring.jpa.hibernate.ddl-auto=nonespring.jpa.properties.hibernate.show_sql=truespring.jpa.properties.hibernate.format_sql=truemanagement.endpoints.web.exposure.include=*spring.cache.type=redisspring.cache.cache-names=cache-bookspring.cache.redis.time-to-live=600000spring.cache.redis.cache-null-values=falsespring.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来做一些工作。
