使用Redis和Java進行資料庫快取 - DZone資料庫

java閘瓦發表於2019-04-18

資料庫快取是處理這些效能問題的最常見策略之一。快取涉及將資料庫查詢的結果儲存在更快,更容易訪問的位置。正確完成後,快取將減少查詢響應時間,減少資料庫負載並降低成本。

但是,快取也需要小心處理,因為它們實際上是在另一個位置建立另一個資訊副本。保持資料庫和快取同步並保持最新可能比您預期的更棘手。在下一節中,我們將討論一些最常見的資料庫快取策略。

什麼是不同的快取策略?

手動快取(也稱為快取擱置策略)涉及直接管理資料庫和快取。您的應用程式在啟動資料庫查詢之前檢查快取,並在對資料庫進行任何更改後更新快取。

雖然如果正確實現有效,但手動快取可能非常繁瑣,尤其是在您需要查詢多個資料庫時。出於這些原因,開發人員發明了許多替代性的快取策略。

直讀快取策略

在讀取快取中,應用程式首先查詢快取以檢視其所需的資訊是否在內部。如果沒有,它將從資料庫中檢索資訊並使用它來更新快取。快取提供程式或快取庫負責查詢和更新快取的詳細邏輯。

當應用程式重複請求相同的資料時,讀取策略最適合讀取繁重的工作負載:例如,一遍又一遍地載入相同文章的新聞網站。

讀取策略的一個缺點是對快取的第一次查詢將始終導致未命中,因為保證所請求的資訊不在內部。為了解決這個問題,開發人員通常會使用使用者可能要求的資訊提前“加熱”快取。

直寫快取策略

在直寫式快取記憶體中,首先對快取記憶體進行更新,然後對資料庫進行更新。從應用程式到快取以及從快取到資料庫都有一條直接線。與直讀式快取結合使用時,直寫式策略可確保您的資料保持一致,從而無需手動快取失效。

後寫快取策略

在後寫式快取(也稱為回寫式快取記憶體)中,應用程式首先將資料寫入快取記憶體。經過一段設定的延遲後,快取也會將此資訊寫入資料庫。後寫快取最適合寫入繁重的工作負載,即使出現一些故障和停機也可以很好地執行。

基於Java的Redis快取與Redisson

Redis是NoSQL資料庫最受歡迎的選項之一,它使用鍵值系統來儲存資料。Redisson是Java程式語言中的Redis客戶端庫,可以使用所有熟悉的Java集合輕鬆訪問Redis功能。

Redisson允許您將資料放在外部儲存中的地圖中。您可以使用此功能實現資料庫,Web服務或任何其他資料來源的快取。

Redis中的直讀快取

下面是一個Java示例,說明如何在Redis和Redisson中使用直讀快取。 如果請求的條目在快取中不存在,則它將由MapLoader物件載入:

MapLoader<String, String> mapLoader = new MapLoader<String, String>()
{
	@Override
	public Iterable<String> loadAllKeys()
	{
		List<String>	list		= new ArrayList<String>();
		Statement	statement	= conn.createStatement();
		try {
			ResultSet result = statement.executeQuery( "SELECT id FROM student" );
			while ( result.next() )
			{
				list.add( result.getString( 1 ) );
			}
		} finally {
			statement.close();
		}
		return(list);
	}
 
 
	@Override
	public String load( String key )
	{
		PreparedStatement preparedStatement = conn.prepareStatement( "SELECT name FROM student where id = ?" );
		try {
			preparedStatement.setString( 1, key );
			ResultSet result = preparedStatement.executeQuery();
			if ( result.next() )
			{
				return(result.getString( 1 ) );
			}
			return(null);
		} finally {
			preparedStatement.close();
		}
	}
}
複製程式碼

配置使用案例:

MapOptions<K, V> options = MapOptions.< K, V > defaults()
.loader( mapLoader );
RMap<K, V> map = redisson.getMap( "test", options );
/* or */
RMapCache<K, V> map = redisson.getMapCache( "test", options );
/* or with boost up to 45x times */
RLocalCachedMap<K, V> map = redisson.getLocalCachedMap( "test", options );
/* or with boost up to 45x times */
RLocalCachedMapCache<K, V> map = redisson.getLocalCachedMapCache( "test", options );
複製程式碼

Redis中的直寫快取

下面是一個Java示例,說明如何在Redis中使用Redis中的Redis使用直寫快取。

在MapWriter物件更新快取和資料庫之前,快取更新方法不會返回:

MapWriter<String, String> mapWriter = new MapWriter<String, String>()
{
	@Override
	public void writeAll( Map<String, String> map )
	{
		PreparedStatement preparedStatement = conn.prepareStatement( "INSERT INTO student (id, name) values (?, ?)" );
		try {
			for ( Entry<String, String> entry : map.entrySet() )
			{
				preparedStatement.setString( 1, entry.getKey() );
				preparedStatement.setString( 2, entry.getValue() );
				preparedStatement.addBatch();
			}
			preparedStatement.executeBatch();
		} finally {
			preparedStatement.close();
		}
	}
 
 
	@Override
	public void write( String key, String value )
	{
		PreparedStatement preparedStatement = conn.prepareStatement( "INSERT INTO student (id, name) values (?, ?)" );
		try {
			preparedStatement.setString( 1, key );
			preparedStatement.setString( 2, value );
			preparedStatement.executeUpdate();
		} finally {
			preparedStatement.close();
		}
	}
 
 
	@Override
	public void deleteAll( Collection<String> keys )
	{
		PreparedStatement preparedStatement = conn.prepareStatement( "DELETE FROM student where id = ?" );
		try {
			for ( String key : keys )
			{
				preparedStatement.setString( 1, key );
				preparedStatement.addBatch();
			}
			preparedStatement.executeBatch();
		} finally {
			preparedStatement.close();
		}
	}
 
 
	@Override
	public void delete( String key )
	{
		PreparedStatement preparedStatement = conn.prepareStatement( "DELETE FROM student where id = ?" );
		try {
			preparedStatement.setString( 1, key );
			preparedStatement.executeUpdate();
		} finally {
			preparedStatement.close();
		}
	}
}
複製程式碼

使用配置案例:

MapOptions<K, V> options = MapOptions.< K, V > defaults()
.writer( mapWriter )
.writeMode( WriteMode.WRITE_THROUGH );
RMap<K, V> map = redisson.getMap( "test", options );
/* or */
RMapCache<K, V> map = redisson.getMapCache( "test", options );
/* or with boost up to 45x times */
RLocalCachedMap<K, V> map = redisson.getLocalCachedMap( "test", options );
/* or with boost up to 45x times */
RLocalCachedMapCache<K, V> map = redisson.getLocalCachedMapCache( "test", options );
複製程式碼

Redis中的後寫快取

MapWriter介面還用於非同步提交對Map物件(快取)和外部儲存(資料庫)的更新。後臺寫入操作執行中使用的執行緒數量通過writeBehindThreads設定設定。

下面,我們看到Redisson中基於Redis的後寫快取實現的配置的Java示例:

MapOptions<K, V> options = MapOptions.< K, V > defaults()
.writer( mapWriter )
.writeMode( WriteMode.WRITE_BEHIND )
.writeBehindThreads( 8 );
RMap<K, V> map = redisson.getMap( "test", options );
/* or */
RMapCache<K, V> map = redisson.getMapCache( "test", options );
/* or with boost up to 45x times */
RLocalCachedMap<K, V> map = redisson.getLocalCachedMap( "test", options );
/* or with boost up to 45x times */
RLocalCachedMapCache<K, V> map = redisson.getLocalCachedMapCache( "test", options );
複製程式碼

上述所有討論的策略可用於Redisson的RMap,RMapCache,RLocalCachedMap和RLocalCachedMapCache物件。使用後兩個物件可以使Redis中的讀取操作速度提高45倍。

相關文章