關於Redis緩存
-
為什么使用緩存?
提升重復訪問數據的訪問效率。
-
Redis的三個用途
數據庫,緩存,消息中間件
Redis的應用場景(針對被重復訪問的數據)
- 頁面緩存(圖片,CSS,html等靜態數據)——熱點數據
- 最新列表
- 排行榜
- 計數器
- session存儲
使用建議
- Redis 速度快是建立在內存數據庫基礎上的,但是一台服務器的內存要比磁盤金貴許多,所以在項目初期不要想什么都往 Redis 里放,這樣當數據量上來后很快內存就會不夠用,反而得不償失。合理的利用有限的內存,將讀(寫)頻繁的熱數據放在 Redis 中才能更好感受到它帶來的性能提升。
- Redis 雖然提供了
RDB
和AOF
兩種持久化方式,但是普遍還是認為 Redis 的持久化並不是很靠譜。非常重要的數據不要依賴 Redis 來開發,或者最起碼不要只在 Redis 中持久化 - MySQL 經過不斷優化性能已經非常好,所以 MySQL 提供的數據結構和訪問效率能滿足的需求的情況下不要引入 Redis,多引入一個組件就多一個可能的故障節點,尤其在保持數據一致性的場景中數據(比如用戶余額)應該只放在數據庫中,除非你知道怎么解決考系統的分布式事務。
Redis數據類型
文檔參考:http://doc.redisfans.com/
-
string(一個鍵最大能存儲 512MB)
-
hash(適合存儲對象)每個 hash 可以存儲 2的32次方 -1個 鍵值對
-
list
list是一個從左至右的隊列
lpush從左往右插入元素,最后插入的3在最左邊
rpush從右往左插入元素,最后插入的元素c在最右邊
lpop和rpop分別是從左邊和右邊取出元素並移除
lrange返回指定范圍內的元素
-
set(無序集合,不允許重復)
返回集合中元素的個數
-
zset
Redis zset 和 set 一樣也是string類型元素的集合,且不允許重復的成員。
不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。
zset的成員是唯一的,但分數(score)卻可以重復。
zrange按照score從小到大排列
服務器安裝Redis
測試環境:阿里雲 CentOS 7.6
安裝Redis
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
tar xzf redis-5.0.5.tar.gz
cd redis-5.0.5
make
啟動關閉Redis
打開src文件夾
./redis-server
./redis-cli shutdown
配置Redis遠程連接
在redis-5.0.5目錄下的redis.conf
-
遠程連接(注釋該行)開啟阿里雲安全組6379端口
-
設置密碼
-
開啟允許公網訪問
-
重新啟動redis,並加載配置文件
./redis-server ../redis.conf
-
查看配置是否生效
打開src目錄
./redis-cli auth "123456" config get *
-
在win10本地用可視化工具連接
集成Redis
在上次集成Druid的基礎上集成Redis
https://www.cnblogs.com/noneplus/p/11532065.html
-
添加Redis緩存依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-start-cache</artifactId></dependency> </dependency>
-
配置yaml
spring: datasource: # 數據源基本配置 username: noneplus password: MEMMpYHaOUFVuaR37bMbUmGW76WVSLAD7pnFLrbup5H4Q6sZvWMDsYAcnZvAL2hY2Man1rc6SCJMYwrse1xPKw== # 1.配置生成的password driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://47.103.6.247:3306/user?serverTimezone=UTC type: com.alibaba.druid.pool.DruidDataSource # Druid數據源配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置監控統計攔截的filters,去掉后監控界面sql無法統計,'wall'用於防火牆 filters: stat,wall,log4j,config # 3.添加config maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true # 2.開啟加密,配置公鑰 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500;config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIIl9Pp9nYiIsVgEgOuNqqyPIU6NsYNSyLX3gxcBhIPRtcL5WqxevYKvsAwaT4WOtww268vHdyP7zWTGhtGxscMCAwEAAQ== thymeleaf: cache: false redis: host: 47.103.6.247 port: 6379 password: 123456 pagehelper: helperDialect: mysql reasonable: true supportMethodsArguments: true pageSizeZero: false #pageSize=0
-
測試是否可以正常連接到redis
@Autowired StringRedisTemplate stringRedisTemplate; @Test public void testRedis() { stringRedisTemplate.opsForValue().append("ms","hello"); }
-
單條數據緩存
-
主程序類添加@EnableCaching注解
-
配置Redis序列化
package zkrun.top.web.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; @Configuration @EnableCaching public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); // 使用Jackson2JsonRedisSerialize 替換默認序列化 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); // 設置value的序列化規則和 key的序列化規則 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //解決查詢緩存轉換異常的問題 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置序列化(解決亂碼的問題),過期時間30秒 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(1800000)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; } }
-
創建RedisController類
@Autowired RedisService redisService; @RequestMapping("/get") @ResponseBody public String get(Integer id) { return redisService.getUserById(id); } @RequestMapping("/update") @ResponseBody public UserInfo update(UserInfo userInfo) { return redisService.updateUser(userInfo); } @RequestMapping("/deleteCache") @ResponseBody public String delete(Integer id) { return redisService.deleteUser(id); }
-
RedisService(其中,緩存注解放在Service層)
@Cacheable產生緩存
@CachePut更新緩存
@CacheEvict刪除緩存
package zkrun.top.web.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import zkrun.top.web.bean.UserInfo; import zkrun.top.web.mapper.UserInfoMapper; @Service public class RedisService { @Autowired UserInfoMapper userInfoMapper; /**將方法運行結果進行緩存,當方法被再次調用時,直接返回緩存中的結果。 * 參數: * value:指定緩存組件的名字 * key:緩存的key。可以使用SpEl表達式 * condition:緩存條件。(為true時緩存),使用EL表達式 * unless:否定緩存。(為true時不緩存)unless在方法執行之后判斷,所以unless可以用結 果作為判斷條件。 * @param id * @return */ @Cacheable(value = "test", key = "#id") public String getUserById(Integer id) { UserInfo userInfo=userInfoMapper.getUserById(id); return userInfo.toString(); } //修改了數據庫的數據,同時更新緩存。先調用目標方法,然后緩存方法結果。 @CachePut(value = "test",key="#result.id") //只能是result.id public UserInfo updateUser(UserInfo userInfo) { userInfoMapper.updateUser(userInfo); return userInfo; } //刪除數據之后,清除緩存 @CacheEvict(value = "test", key = "#id") public String deleteUser(Integer id) { userInfoMapper.deleteUserById(id); return "已刪除"; } }
測試
-
查詢id=60的數據
http://localhost:8080/get?id=60
緩存已生成
緩存已更新
數據庫已更新
-
刪除id=60的數據
http://localhost:8080/deleteCache?id=60
緩存已清空
數據庫已刪除