Sprintboot redisはgzipとSnappy compressを採用してデータを圧縮する
18276 ワード
1はじめに
Sprintboot+redisの使い方と組み合わせは、私の前の文章を参照してください.https://blog.csdn.net/zzhongcy/article/details/102584028
ここでは主に,生産環境において単一のredisデータが大きい場合に,圧縮データを考慮してredisに格納する可能性について述べる.
圧縮データのメリットとデメリット:の利点1:圧縮はredisストレージデータ量を減少させ、redisのスループット を増加させる.の利点2:圧縮が少ないネットワーク帯域幅 の欠点は、CPU消費量 が増加することである.
2 Sprintboot redis構成
次の2つの構成方法があります.
2.1方式1:RedisTemplate構成
格納方式コードは次のとおりです.
//valueシーケンス化方式jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);
2.2方式2:RedisCacheConfiguration構成
velue圧縮設定は次のとおりです.
.serializeValuesWith( SerializationPair.fromSerializer( jackson2JsonRedisSerializer)
ここではカスタムJackson 2 JsonRedisSerializerクラスを使用し、
jsonデータの内部にはjavaが含まれている.util.ArrayList、com.server.model.classなどのタイプの文字列.
もちろんデフォルトの汎用フォーマットGenericJackson 2 JsonRedisSerializerも使用できます.以下のようになります.
.serializeValuesWith( SerializationPair.fromSerializer( new GenericJackson2JsonRedisSerializer())
jsonデータの内部にはjavaが含まれている.util.ArrayList、com.server.model.class、@classなどのタイプの文字列.
3データ圧縮
3.1 gzip圧縮
注意:
//gzipOutputStream.flush();//flushを呼び出すだけではリフレッシュされません.圧縮タイプのストリームはcloseまたはfinishを実行する必要があります.gzipOutputStreamが完了します.close();//内部コールfinish
圧縮データが完了しないと、解凍がエラーになります.
java.io.EOFException : Unexpected end of ZLIB input stream
at java.util.zip.InflaterInputStream.fill( InflaterInputStream.java:240)
at java.util.zip.InflaterInputStream.read( InflaterInputStream.java:158)
at java.util.zip.GZIPInputStream.read( GZIPInputStream.java:117)
3.1.1自定バッファ解凍方式:
3.1.2 IOUtils.copyソース解読
実はcopy関数の内部もwhile((n=ungzip.read)>=0)を呼び出して実現される.
ただIOUtilscopy内部バッファサイズは4 kで、大きい点を設定すると読み取り速度が向上する可能性があります.
3.2 Snappy圧縮
3.3圧縮設定
ここではRedisTemplateの構成について簡単に説明しますが、RedisCacheConfigurationの構成は上記の説明を参照してください.
これにより、データ圧縮が実現されます.
4エンド
4.1圧縮率効果
ここでは圧縮効率を簡単にテストしました(注意:圧縮効率は圧縮内容と密接な関係があり、ここでは文字列の例を簡単に採用し、テストを行うだけです):1、Jackson 2 JsonRedisSerializer、テストデータ長:13.70 Kb 2、RedisSerializerGzip圧縮、テストデータ長:9.19 Kb 3、RedisSerializerSnappy圧縮、テストデータ長:13.37 Kb RedisSerializerGzipは圧縮率ではなく圧縮速度を追求するため、RedisSerializerSnappyは圧縮率が高く、RedisSerializerSnappyは圧縮率が低いことがわかる.どのような圧縮を採用するかは、皆さんのプロジェクトの状況に応じて自分で決めましょう.
4.2性能比較:
以前の記事を参考にしてください.
圧縮フォーマットgzip/snappy/lzo/bzip 2の比較とまとめ:https://blog.csdn.net/zzhongcy/article/details/89375346
圧縮フォーマット
あっしゅくひ
あっしゅくそくど
解凍速度
gzip
13.4%
21 MB/s
118 MB/s
lzo
20.5%
135 MB/s
410 MB/s
snappy
22.2%
172 MB/s
409 MB/s
bzip2
13.2%
2.4MB/s
9.5MB/s
データを圧縮するかどうか、およびどのような圧縮フォーマットを使用するかは、パフォーマンスに重要な影響を及ぼします.
5その他のシーケンス
5.1 Fstシーケンス化
5.2 Kryoシーケンス化
6参照
https://stackoverflow.com/questions/58829724/adding-compression-to-spring-data-redis-with-lettuceconnectionfactory
https://github.com/cboursinos/java-spring-redis-compression-snappy-kryo
https://ld246.com/article/1532328272348
https://gitee.com/SoftMeng/spring-boot-skill/tree/master/redis-serializer-line
Sprintboot+redisの使い方と組み合わせは、私の前の文章を参照してください.https://blog.csdn.net/zzhongcy/article/details/102584028
ここでは主に,生産環境において単一のredisデータが大きい場合に,圧縮データを考慮してredisに格納する可能性について述べる.
圧縮データのメリットとデメリット:
2 Sprintboot redis構成
次の2つの構成方法があります.
2.1方式1:RedisTemplate構成
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(factory);
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);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key String
template.setKeySerializer(stringRedisSerializer);
// hash key String
template.setHashKeySerializer(stringRedisSerializer);
// value jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash value jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
格納方式コードは次のとおりです.
//valueシーケンス化方式jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);
2.2方式2:RedisCacheConfiguration構成
@Bean
public RedisCacheConfiguration redisCacheConfiguration (long defaultExpiration ) {
Jackson2JsonRedisSerializer
velue圧縮設定は次のとおりです.
.serializeValuesWith( SerializationPair.fromSerializer( jackson2JsonRedisSerializer)
ここではカスタムJackson 2 JsonRedisSerializerクラスを使用し、
jsonデータの内部にはjavaが含まれている.util.ArrayList、com.server.model.classなどのタイプの文字列.
もちろんデフォルトの汎用フォーマットGenericJackson 2 JsonRedisSerializerも使用できます.以下のようになります.
.serializeValuesWith( SerializationPair.fromSerializer( new GenericJackson2JsonRedisSerializer())
jsonデータの内部にはjavaが含まれている.util.ArrayList、com.server.model.class、@classなどのタイプの文字列.
3データ圧縮
3.1 gzip圧縮
public class RedisSerializerGzip extends JdkSerializationRedisSerializer {
@Override
public Object deserialize(byte[] bytes) {
return super.deserialize(decompress(bytes));
}
@Override
public byte[] serialize(Object object) {
return compress(super.serialize(object));
}
private byte[] compress(byte[] content) {
byte[] ret = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream= new GZIPOutputStream(byteArrayOutputStream);
gzipOutputStream.write(content);
//gzipOutputStream.flush(); // flush , close finish
stream.close(); // finish
ret = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.flush();
byteArrayOutputStream.close();
} catch (IOException e) {
throw new SerializationException("Unable to compress data", e);
}
return ret;
}
private byte[] decompress(byte[] contentBytes) {
byte[] ret = null;
ByteArrayOutputStream out = null;
try {
out = new ByteArrayOutputStream();
GZIPInputStream stream = new GZIPInputStream(new ByteArrayInputStream(contentBytes));
IOUtils.copy(stream, out);
stream.close();
ret = out.toByteArray();
out.flush();
out.close();
} catch (IOException e) {
throw new SerializationException("Unable to decompress data", e);
}
return ret;
}
}
注意:
//gzipOutputStream.flush();//flushを呼び出すだけではリフレッシュされません.圧縮タイプのストリームはcloseまたはfinishを実行する必要があります.gzipOutputStreamが完了します.close();//内部コールfinish
圧縮データが完了しないと、解凍がエラーになります.
java.io.EOFException : Unexpected end of ZLIB input stream
at java.util.zip.InflaterInputStream.fill( InflaterInputStream.java:240)
at java.util.zip.InflaterInputStream.read( InflaterInputStream.java:158)
at java.util.zip.GZIPInputStream.read( GZIPInputStream.java:117)
3.1.1自定バッファ解凍方式:
/**
* GZIP
*
* @param bytes
* @return
*/
public static byte[] uncompress(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
try {
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
} catch (IOException e) {
ApiLogger.error("gzip uncompress error.", e);
}
return out.toByteArray();
}
/**
*
* @param bytes
* @param encoding
* @return
*/
public static String uncompressToString(byte[] bytes, String encoding) {
if (bytes == null || bytes.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
try {
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toString(encoding);
} catch (IOException e) {
ApiLogger.error("gzip uncompress to string error.", e);
}
return null;
}
3.1.2 IOUtils.copyソース解読
実はcopy関数の内部もwhile((n=ungzip.read)>=0)を呼び出して実現される.
public static int copy(InputStream input, OutputStream output) throws IOException {
long count = copyLarge(input, output);
return count > 2147483647L ? -1 : (int)count;
}
public static long copyLarge(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[4096];
long count = 0L;
int n;
for(boolean var5 = false; -1 != (n = input.read(buffer)); count += (long)n) {
output.write(buffer, 0, n);
}
return count;
}
ただIOUtilscopy内部バッファサイズは4 kで、大きい点を設定すると読み取り速度が向上する可能性があります.
3.2 Snappy圧縮
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.SerializationUtils;
import org.xerial.snappy.Snappy;
import java.io.Serializable;
public class RedisSerializerSnappy extends JdkSerializationRedisSerializer {
private RedisSerializer innerSerializer;
public RedisSerializerSnappy(RedisSerializer innerSerializer) {
this.innerSerializer = innerSerializer;
}
@Override
public Object deserialize(byte[] bytes) {
return super.deserialize(decompress(bytes));
}
@Override
public byte[] serialize(Object object) {
return compress(super.serialize(object));
}
private byte[] compress(byte[] content) {
try {
byte[] bytes = innerSerializer != null ? innerSerializer.serialize(content)
: SerializationUtils.serialize((Serializable) content);
return Snappy.compress(bytes);
} catch (Exception e) {
throw new SerializationException(e.getMessage(), e);
}
}
private byte[] decompress(byte[] contentBytes) {
try {
byte[] bos = Snappy.uncompress(contentBytes);
return (byte[]) (innerSerializer != null ? innerSerializer.deserialize(bos) : SerializationUtils.deserialize(bos));
} catch (Exception e) {
throw new SerializationException(e.getMessage(), e);
}
}
}
3.3圧縮設定
ここではRedisTemplateの構成について簡単に説明しますが、RedisCacheConfigurationの構成は上記の説明を参照してください.
@Bean
public RedisTemplate redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// Set a custom serializer that will compress/decompress data to/from redis
RedisSerializerGzip serializerGzip = new RedisSerializerGzip();
template.setValueSerializer(serializerGzip);
template.setHashValueSerializer(serializerGzip);
//RedisSerializerSnappy serializerSnappy = new RedisSerializerSnappy(null);
//redisTemplate.setValueSerializer(serializerSnappy);
//redisTemplate.setHashValueSerializer(serializerSnappy);
return template;
}
これにより、データ圧縮が実現されます.
4エンド
4.1圧縮率効果
ここでは圧縮効率を簡単にテストしました(注意:圧縮効率は圧縮内容と密接な関係があり、ここでは文字列の例を簡単に採用し、テストを行うだけです):
4.2性能比較:
以前の記事を参考にしてください.
圧縮フォーマットgzip/snappy/lzo/bzip 2の比較とまとめ:https://blog.csdn.net/zzhongcy/article/details/89375346
圧縮フォーマット
あっしゅくひ
あっしゅくそくど
解凍速度
gzip
13.4%
21 MB/s
118 MB/s
lzo
20.5%
135 MB/s
410 MB/s
snappy
22.2%
172 MB/s
409 MB/s
bzip2
13.2%
2.4MB/s
9.5MB/s
データを圧縮するかどうか、およびどのような圧縮フォーマットを使用するかは、パフォーマンスに重要な影響を及ぼします.
5その他のシーケンス
5.1 Fstシーケンス化
import org.nustaq.serialization.FSTConfiguration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import javax.xml.crypto.Data;
import java.time.LocalDateTime;
import java.util.Date;
/**
* Description: Fst
*/
public class FstSerializer implements RedisSerializer {
private static FSTConfiguration configuration = FSTConfiguration.createStructConfiguration();
private Class clazz;
public FstSerializer(Class clazz) {
super();
this.clazz = clazz;
configuration.registerClass(clazz);
}
@Override
public byte[] serialize(T t) throws SerializationException {
return configuration.asByteArray(t);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
return (T) configuration.asObject(bytes);
}
}
5.2 Kryoシーケンス化
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.ByteBufferInput;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.DefaultSerializers;
import com.esotericsoftware.kryo.serializers.JavaSerializer;
import com.esotericsoftware.kryo.util.Pool;
import de.javakaffee.kryoserializers.ArraysAsListSerializer;
import de.javakaffee.kryoserializers.BitSetSerializer;
import de.javakaffee.kryoserializers.GregorianCalendarSerializer;
import de.javakaffee.kryoserializers.JdkProxySerializer;
import de.javakaffee.kryoserializers.RegexSerializer;
import de.javakaffee.kryoserializers.SynchronizedCollectionsSerializer;
import de.javakaffee.kryoserializers.URISerializer;
import de.javakaffee.kryoserializers.UUIDSerializer;
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.awt.print.Book;
import java.io.ByteArrayInputStream;
import java.lang.reflect.InvocationHandler;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
/**
* Description: Kryo .
*/
public class KryoSerializer implements RedisSerializer {
private static final int BUFFER_SIZE = 2048;
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private static Pool kryoPool = new Pool(true, false, 8) {
@Override
protected Kryo create() {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.register(Book.class);
kryo.addDefaultSerializer(Throwable.class, new JavaSerializer());
kryo.register(Arrays.asList("").getClass(), new ArraysAsListSerializer());
kryo.register(GregorianCalendar.class, new GregorianCalendarSerializer());
kryo.register(InvocationHandler.class, new JdkProxySerializer());
kryo.register(BigDecimal.class, new DefaultSerializers.BigDecimalSerializer());
kryo.register(BigInteger.class, new DefaultSerializers.BigIntegerSerializer());
kryo.register(Pattern.class, new RegexSerializer());
kryo.register(BitSet.class, new BitSetSerializer());
kryo.register(URI.class, new URISerializer());
kryo.register(UUID.class, new UUIDSerializer());
UnmodifiableCollectionsSerializer.registerSerializers(kryo);
SynchronizedCollectionsSerializer.registerSerializers(kryo);
kryo.register(HashMap.class);
kryo.register(ArrayList.class);
kryo.register(LinkedList.class);
kryo.register(HashSet.class);
kryo.register(TreeSet.class);
kryo.register(Hashtable.class);
kryo.register(Date.class);
kryo.register(Calendar.class);
kryo.register(ConcurrentHashMap.class);
kryo.register(SimpleDateFormat.class);
kryo.register(GregorianCalendar.class);
kryo.register(Vector.class);
kryo.register(BitSet.class);
kryo.register(StringBuffer.class);
kryo.register(StringBuilder.class);
kryo.register(Object.class);
kryo.register(Object[].class);
kryo.register(String[].class);
kryo.register(byte[].class);
kryo.register(char[].class);
kryo.register(int[].class);
kryo.register(float[].class);
kryo.register(double[].class);
return kryo;
}
};
private static Pool
6参照
https://stackoverflow.com/questions/58829724/adding-compression-to-spring-data-redis-with-lettuceconnectionfactory
https://github.com/cboursinos/java-spring-redis-compression-snappy-kryo
https://ld246.com/article/1532328272348
https://gitee.com/SoftMeng/spring-boot-skill/tree/master/redis-serializer-line