mysql springboot 緩存_Spring Boot 整合 Redis 實現緩存操作

摘要: 原創出處 www.bysocket.com 「泥瓦匠BYSocket 」歡迎轉載,保留摘要,謝謝!

『?產品沒有價值,開發團隊再優秀也無濟于事 – 《啟示錄》 』

本文提綱

一、緩存的應用場景

二、更新緩存的策略

三、運行?springboot-mybatis-redis?工程案例

四、springboot-mybatis-redis?工程代碼配置詳解

運行環境:

Mac OS 10.12.x

JDK 8 +

Redis 3.2.8

Spring Boot 1.5.1.RELEASE

一、緩存的應用場景

什么是緩存?

在互聯網場景下,尤其 2C 端大流量場景下,需要將一些經常展現和不會頻繁變更的數據,存放在存取速率更快的地方。緩存就是一個存儲器,在技術選型中,常用 Redis 作為緩存數據庫。緩存主要是在獲取資源方便性能優化的關鍵方面。

Redis 是一個高性能的 key-value 數據庫。GitHub 地址:https://github.com/antirez/redis?。Github 是這么描述的:

Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, HyperLogLogs, Bitmaps.

緩存的應用場景有哪些呢?

比如常見的電商場景,根據商品 ID 獲取商品信息時,店鋪信息和商品詳情信息就可以緩存在 Redis,直接從 Redis 獲取。減少了去數據庫查詢的次數。但會出現新的問題,就是如何對緩存進行更新?這就是下面要講的。

二、更新緩存的策略

參考《緩存更新的套路》http://coolshell.cn/articles/17416.html,緩存更新的模式有四種:Cache aside, Read through, Write through, Write behind caching。

這里我們使用的是 Cache Aside 策略,從三個維度:(摘自 耗子叔叔博客)

失效:應用程序先從cache取數據,沒有得到,則從數據庫中取數據,成功后,放到緩存中。

命中:應用程序從cache中取數據,取到后返回。

更新:先把數據存到數據庫中,成功后,再讓緩存失效。

大致流程如下:

獲取商品詳情舉例

a. 從商品 Cache 中獲取商品詳情,如果存在,則返回獲取 Cache 數據返回。

b. 如果不存在,則從商品 DB 中獲取。獲取成功后,將數據存到 Cache 中。則下次獲取商品詳情,就可以從 Cache 就可以得到商品詳情數據。

c. 從商品 DB 中更新或者刪除商品詳情成功后,則從緩存中刪除對應商品的詳情緩存

8e35d4be7ea563026606042c5e46144e.png

三、運行?springboot-mybatis-redis?工程案例

git clone 下載工程?springboot-learning-example ,項目地址見 GitHub –?https://github.com/JeffLi1993/springboot-learning-example。

下面開始運行工程步驟(Quick Start):

1.數據庫和 Redis 準備

a.創建數據庫 springbootdb:

CREATE DATABASE springbootdb;

b.創建表 city :(因為我喜歡徒步)

DROP TABLE IF EXISTS? `city`;

CREATE TABLE `city` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '城市編號',

`province_id` int(10) unsigned? NOT NULL COMMENT '省份編號',

`city_name` varchar(25) DEFAULT NULL COMMENT '城市名稱',

`description` varchar(25) DEFAULT NULL COMMENT '描述',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

c.插入數據

INSERT city VALUES (1 ,1,'溫嶺市','BYSocket 的家在溫嶺。');

d.本地安裝 Redis

2.?springboot-mybatis-redis 工程項目結構介紹

springboot-mybatis-redis?工程項目結構如下圖所示:

org.spring.springboot.controller - Controller 層

org.spring.springboot.dao - 數據操作層 DAO

org.spring.springboot.domain - 實體類

org.spring.springboot.service - 業務邏輯層

Application - 應用啟動類

application.properties - 應用配置文件,應用啟動會自動讀取配置

3.改數據庫配置

打開 application.properties 文件, 修改相應的數據源配置,比如數據源地址、賬號、密碼等。

(如果不是用 MySQL,自行添加連接驅動 pom,然后修改驅動名配置。)

4.編譯工程

在項目根目錄 springboot-learning-example,運行 maven 指令:

mvn clean install

5.運行工程

右鍵運行?springboot-mybatis-redis 工程 Application 應用啟動類的 main 函數。

項目運行成功后,這是個?HTTP OVER JSON 服務項目。所以用 postman 工具可以如下操作

根據 ID,獲取城市信息

GET?http://127.0.0.1:8080/api/city/1

再請求一次,獲取城市信息會發現數據獲取的耗時快了很多。服務端 Console 輸出的日志:

2017-04-13 18:29:00.273? INFO 13038 --- [nio-8080-exec-1] o.s.s.service.impl.CityServiceImpl? ? ? ?: CityServiceImpl.findCityById() : 城市插入緩存 >> City{id=12, provinceId=3, cityName='三亞', description='水好,天藍'}

2017-04-13 18:29:03.145? INFO 13038 --- [nio-8080-exec-2] o.s.s.service.impl.CityServiceImpl? ? ? ?: CityServiceImpl.findCityById() : 從緩存中獲取了城市 >> City{id=12, provinceId=3, cityName='三亞', description='水好,天藍'}

可見,第一次是從數據庫 DB 獲取數據,并插入緩存,第二次直接從緩存中取。

更新城市信息

PUT?http://127.0.0.1:8080/api/city

刪除城市信息

DELETE http://127.0.0.1:8080/api/city/2

這兩種操作中,如果緩存有對應的數據,則刪除緩存。服務端 Console 輸出的日志:

2017-04-13 18:29:52.248? INFO 13038 --- [nio-8080-exec-9] o.s.s.service.impl.CityServiceImpl? ? ? ?: CityServiceImpl.deleteCity() : 從緩存中刪除城市 ID >> 12

四、springboot-mybatis-redis?工程代碼配置詳解

這里,我強烈推薦?注解 的方式實現對象的緩存。但是這里為了更好說明緩存更新策略。下面講講工程代碼的實現。

pom.xml 依賴配置:

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

springboot

springboot-mybatis-redis

0.0.1-SNAPSHOT

springboot-mybatis-redis :: 整合 Mybatis 并使用 Redis 作為緩存

org.springframework.boot

spring-boot-starter-parent

1.5.1.RELEASE

1.2.0

5.1.39

1.3.2.RELEASE

org.springframework.boot

spring-boot-starter-redis

${spring-boot-starter-redis-version}

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

org.mybatis.spring.boot

mybatis-spring-boot-starter

${mybatis-spring-boot}

mysql

mysql-connector-java

${mysql-connector}

junit

junit

4.12

包括了?Spring Boot Reids 依賴、 MySQL 依賴和 Mybatis 依賴。

在 application.properties 應用配置文件,增加 Redis 相關配置

## 數據源配置

spring.datasource.url=jdbc:mysql://localhost:3306/springbootdb?useUnicode=true&characterEncoding=utf8

spring.datasource.username=root

spring.datasource.password=123456

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

## Mybatis 配置

mybatis.typeAliasesPackage=org.spring.springboot.domain

mybatis.mapperLocations=classpath:mapper/*.xml

## Redis 配置

## Redis數據庫索引(默認為0)

spring.redis.database=0

## Redis服務器地址

spring.redis.host=127.0.0.1

## Redis服務器連接端口

spring.redis.port=6379

## Redis服務器連接密碼(默認為空)

spring.redis.password=

## 連接池最大連接數(使用負值表示沒有限制)

spring.redis.pool.max-active=8

## 連接池最大阻塞等待時間(使用負值表示沒有限制)

spring.redis.pool.max-wait=-1

## 連接池中的最大空閑連接

spring.redis.pool.max-idle=8

## 連接池中的最小空閑連接

spring.redis.pool.min-idle=0

## 連接超時時間(毫秒)

spring.redis.timeout=0

詳細解釋可以參考注釋。對應的配置類:org.springframework.boot.autoconfigure.data.redis.RedisProperties

CityRestController 控制層依舊是 Restful 風格的,詳情可以參考《Springboot 實現 Restful 服務,基于 HTTP / JSON 傳輸》。?http://www.bysocket.com/?p=1627?domain 對象 City 必須實現序列化,因為需要將對象序列化后存儲到 Redis。如果沒實現?Serializable ,控制臺會爆出以下異常:

Serializable

java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type

City.java 城市對象:

package org.spring.springboot.domain;

import java.io.Serializable;

/**

* 城市實體類

*

* Created by bysocket on 07/02/2017.

*/

public class City implements Serializable {

private static final long serialVersionUID = -1L;

/**

* 城市編號

*/

private Long id;

/**

* 省份編號

*/

private Long provinceId;

/**

* 城市名稱

*/

private String cityName;

/**

* 描述

*/

private String description;

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

public Long getProvinceId() {

return provinceId;

}

public void setProvinceId(Long provinceId) {

this.provinceId = provinceId;

}

public String getCityName() {

return cityName;

}

public void setCityName(String cityName) {

this.cityName = cityName;

}

public String getDescription() {

return description;

}

public void setDescription(String description) {

this.description = description;

}

@Override

public String toString() {

return "City{" +

"id=" + id +

", provinceId=" + provinceId +

", cityName='" + cityName + '\'' +

", description='" + description + '\'' +

'}';

}

}

如果需要自定義序列化實現,只要實現?RedisSerializer 接口去實現即可,然后在使用 RedisTemplate.setValueSerializer 方法去設置你實現的序列化實現。

主要還是城市業務邏輯實現類 CityServiceImpl.java:

package org.spring.springboot.service.impl;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.spring.springboot.dao.CityDao;

import org.spring.springboot.domain.City;

import org.spring.springboot.service.CityService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.core.StringRedisTemplate;

import org.springframework.data.redis.core.ValueOperations;

import org.springframework.stereotype.Service;

import java.util.List;

import java.util.concurrent.TimeUnit;

/**

* 城市業務邏輯實現類

*

* Created by bysocket on 07/02/2017.

*/

@Service

public class CityServiceImpl implements CityService {

private static final Logger LOGGER = LoggerFactory.getLogger(CityServiceImpl.class);

@Autowired

private CityDao cityDao;

@Autowired

private RedisTemplate redisTemplate;

/**

* 獲取城市邏輯:

* 如果緩存存在,從緩存中獲取城市信息

* 如果緩存不存在,從 DB 中獲取城市信息,然后插入緩存

*/

public City findCityById(Long id) {

// 從緩存中獲取城市信息

String key = "city_" + id;

ValueOperations operations = redisTemplate.opsForValue();

// 緩存存在

boolean hasKey = redisTemplate.hasKey(key);

if (hasKey) {

City city = operations.get(key);

LOGGER.info("CityServiceImpl.findCityById() : 從緩存中獲取了城市 >> " + city.toString());

return city;

}

// 從 DB 中獲取城市信息

City city = cityDao.findById(id);

// 插入緩存

operations.set(key, city, 10, TimeUnit.SECONDS);

LOGGER.info("CityServiceImpl.findCityById() : 城市插入緩存 >> " + city.toString());

return city;

}

@Override

public Long saveCity(City city) {

return cityDao.saveCity(city);

}

/**

* 更新城市邏輯:

* 如果緩存存在,刪除

* 如果緩存不存在,不操作

*/

@Override

public Long updateCity(City city) {

Long ret = cityDao.updateCity(city);

// 緩存存在,刪除緩存

String key = "city_" + city.getId();

boolean hasKey = redisTemplate.hasKey(key);

if (hasKey) {

redisTemplate.delete(key);

LOGGER.info("CityServiceImpl.updateCity() : 從緩存中刪除城市 >> " + city.toString());

}

return ret;

}

@Override

public Long deleteCity(Long id) {

Long ret = cityDao.deleteCity(id);

// 緩存存在,刪除緩存

String key = "city_" + id;

boolean hasKey = redisTemplate.hasKey(key);

if (hasKey) {

redisTemplate.delete(key);

LOGGER.info("CityServiceImpl.deleteCity() : 從緩存中刪除城市 ID >> " + id);

}

return ret;

}

}

首先這里注入了?RedisTemplate 對象。聯想到 Spring 的 JdbcTemplate ,RedisTemplate 封裝了 RedisConnection,具有連接管理,序列化和 Redis 操作等功能。還有針對 String 的支持對象?StringRedisTemplate。

Redis?操作視圖接口類用的是?ValueOperations,對應的是 Redis String/Value 操作。還有其他的操作視圖,ListOperations、SetOperations、ZSetOperations 和 HashOperations 。ValueOperations 插入緩存是可以設置失效時間,這里設置的失效時間是 10 s。

回到更新緩存的邏輯

a. findCityById 獲取城市邏輯:

如果緩存存在,從緩存中獲取城市信息

如果緩存不存在,從 DB 中獲取城市信息,然后插入緩存

b. deleteCity 刪除 / updateCity 更新城市邏輯:

如果緩存存在,刪除

如果緩存不存在,不操作

其他不明白的,可以 git clone 下載工程 springboot-learning-example ,工程代碼注解很詳細。 https://github.com/JeffLi1993/springboot-learning-example。

五、小結

本文涉及到 Spring Boot 在使用 Redis 緩存時,一個是緩存對象需要序列化,二個是緩存更新策略是如何的。

歡迎掃一掃我的公眾號關注 — 及時得到博客訂閱哦!

d0c1501a6d8bb921cf36400dc89de69f.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/388898.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/388898.shtml
英文地址,請注明出處:http://en.pswp.cn/news/388898.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

http壓力測試工具及使用說明

http壓力測試工具及使用說明 轉 說明:介紹幾款簡單、易使用http壓測工具,便于研發同學,壓測服務,明確服務臨界值,尋找服務瓶頸點。 壓測時候可重點以下指標,關注并發用戶數、TPS(每秒事務數量&a…

itchat 道歉_人類的“道歉”

itchat 道歉When cookies were the progeny of “magic cookies”, they were seemingly innocuous packets of e-commerce data that stored a user’s partial transaction state on their computer. It wasn’t disclosed that you were playing a beneficial part in a muc…

使用Kubespray部署生產可用的Kubernetes集群(1.11.2)

Kubernetes的安裝部署是難中之難,每個版本安裝方式都略有區別。筆者一直想找一種支持多平臺 、相對簡單 、適用于生產環境 的部署方案。經過一段時間的調研,有如下幾種解決方案進入筆者視野: 部署方案優點缺點Kubeadm官方出品部署較麻煩、不夠…

android webView 與 JS交互方式

webView 與JS交互 Android調用JS代碼的方法有: 通過WebView的loadUrl()通過WebView的evaluateJavascript() 對于JS調用Android代碼的方法有3種: 通過WebView的addJavascriptInterface(&…

matlab軟件imag函數_「復變函數與積分變換」基本計算代碼

使用了Matlab代碼,化簡平時遇到的計算問題,也可以用于驗算結果來自211工科專業2學分復變函數與積分變換課程求復角主值sym(angle(待求復數))%公式 sym(angle(1sqrt(3)*i))%舉例代入化簡將 代入關于z的函數f(z)中并化解,用于公式法計算無窮遠點…

數據科學 python_為什么需要以數據科學家的身份學習Python的7大理由

數據科學 pythonAs a new Data Scientist, you know that your path begins with programming languages you need to learn. Among all languages that you can select from Python is the most popular language for all Data Scientists. In this article, I will cover 7 r…

[luoguP4142]洞穴遇險

https://www.zybuluo.com/ysner/note/1240792 題面 戳我 解析 這種用來拼接的奇形怪狀的東西,要不就是輪廓線\(DP\),要不就是網絡流。 為了表示奇數點(即\((xy)\%21\))的危險值,把該點拆為兩個點,連一條邊長…

飛信虛擬機

做完了一個圖片處理軟件,突然想到上次上網看到C#程序脫離.NET FRAMEWORK運行的文章,于是決定自己動手試一下。 之前看到有用別的方法來實現的,但我還是選擇了現在比較流行的軟件飛信中帶的VMDotNet,也就是所謂的.NET FRAMEWORK虛擬機吧。相信有很多人也已…

django的contenttype表

https://blog.csdn.net/aaronthon/article/details/81714496 這篇文章已經非常詳細了,供自己以后忘了...回看...... 總結: 當一張表和多個表FK關聯,并且多個FK中只能選擇其中一個或其中n個時,可以利用contenttype,固定用三個字段…

視頻播放問題和提高性能方案

1.Five symptoms of poor video performance 1.1 視頻加載緩慢 ?Perceived Wait Time Time to first frame (TTFF): ? 播放開始所需的adaptive bitrate(ABR)流媒體段的數量。(我們稍后將對此進行更詳細的討論。) ? 視頻請求發送到視頻加載之間的時間(即接收到足夠的數據…

rabbitmq 不同的消費者消費同一個隊列_RabbitMQ 消費端限流、TTL、死信隊列

消費端限流1. 為什么要對消費端限流假設一個場景,首先,我們 Rabbitmq 服務器積壓了有上萬條未處理的消息,我們隨便打開一個消費者客戶端,會出現這樣情況: 巨量的消息瞬間全部推送過來,但是我們單個客戶端無法同時處理這…

動量策略 python_在Python中使用動量通道進行交易

動量策略 pythonMost traders use Bollinger Bands. However, price is not normally distributed. That’s why only 42% of prices will close within one standard deviation. Please go ahead and read this article. However, I have some good news.大多數交易者使用布林…

css3 變換、過渡效果、動畫

1 CSS3 選擇器 1.1 基本選擇器 1.2 層級 空格 > .itemli ~ .item~p 1.3 屬性選擇器 [attr] [attrvalue] [attr^value] [attr$value] [attr*value] [][][] 1.4 偽類選擇器 :link :visited :hover :active :focus :first-child .list li:first-child :last-chi…

webservice 啟用代理服務器

您會發現你寫完了一個webservice在調用的時候發現怎也沒辦法調用,一個簡單的webservice怎么不能使用,一肚子的怨恨,哈哈您可能沒有為webservice設置代理。 下面就給您寫個調用的用例和大家分享下。其實很簡單,但是你沒有想到的時…

mysql常用的存儲引擎_Mysql存儲引擎

什么是存儲引擎?關系數據庫表是用于存儲和組織信息的數據結構,可以將表理解為由行和列組成的表格,類似于Excel的電子表格的形式。有的表簡單,有的表復雜,有的表根本不用來存儲任何長期的數據,有的表讀取時非…

android studio設計模式和文本模式切換

轉載于:https://www.cnblogs.com/judes/p/9437104.html

高斯模糊為什么叫高斯濾波_為什么高斯是所有發行之王?

高斯模糊為什么叫高斯濾波高斯分布及其主要特征: (Gaussian Distribution and its key characteristics:) Gaussian distribution is a continuous probability distribution with symmetrical sides around its center. 高斯分布是連續概率分布,其中心周…

C# webbrowser 代理

百度,google加自己理解后,將所得方法總結一下: 方法1:修改注冊表Software//Microsoft//Windows//CurrentVersion//Internet Settings下 ProxyEnable和ProxyServer。這種方法適用于局域網用戶,撥號用戶無效。 1p…

C MySQL讀寫分離連接串_Mysql讀寫分離

一 什么是讀寫分離MySQL Proxy最強大的一項功能是實現“讀寫分離(Read/Write Splitting)”。基本的原理是讓主數據庫處理事務性查詢,而從數據庫處理SELECT查詢。數據庫復制被用來把事務性查詢導致的變更同步到集群中的從數據庫。當然,主服務器也可以提供…

golang 編寫的在線redis 內存分析工具 rma4go

redis 內存分析工具 rma4go redis是一個很有名的內存型數據庫,這里不做詳細介紹。而rma4go (redis memory analyzer for golang) 是一個redis的內存分析工具,這個工具的主要作用是針對運行時期的redis進行內存的分析,統計redis中key的分布情…