合理選擇Redis部署模式
查看推薦配置
冷熱數(shù)據(jù)分離,不要將所有數(shù)據(jù)全部都放到Redis中
建議根據(jù)業(yè)務(wù)只將高頻熱數(shù)據(jù)存儲到Redis中【QPS大于5000】,對于低頻冷數(shù)據(jù)可以使用Mysql/ElasticSearch等基于磁盤的存儲方式,不僅節(jié)省內(nèi)存成本,而且數(shù)據(jù)量小在操作時速度更快、效率更高!
不同的業(yè)務(wù)數(shù)據(jù)要分開存儲
Redis默認(rèn)是提供了32個DataBase來使用,以自然數(shù)來做區(qū)分,如果不選擇DataBase,都會默認(rèn)使用0這個庫
推薦一個部門一個DataBase,然后再做NameSpace的隔離,該操作需要有SDK的支持,一個DataBase可以設(shè)置多個NameSpace,這樣可以便于管理且方便清理
<bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="database" value="${redis.default.db}" />
<property name="password" value="${redis.pass}" />
<property name="timeout" value="${redis.timeout}" />
<property name="poolConfig" ref="jedisPoolConfig" />
</bean>
規(guī)范Key的格式
合適的key,便于查看,統(tǒng)計,排錯。
比如:
| 平臺名 | 平臺縮寫 |
|---|---|
| 網(wǎng)管 | GW |
“平臺縮寫“+“-”+“項目名”+“-”+“業(yè)務(wù)含義”
例如:GW-TRADE-USERID
GW是新網(wǎng)關(guān),TRADE是交易項目,USERID為業(yè)務(wù)ID。
存儲的Key一定要設(shè)置超時時間
如果應(yīng)用將Redis定位為緩存Cache使用,對于存放的Key一定要設(shè)置超時時間!因為若不設(shè)置,這些Key會一直占用內(nèi)存不釋放,造成極大的浪費,而且隨著時間的推移會導(dǎo)致內(nèi)存占用越來越大,直到達(dá)到服務(wù)器內(nèi)存上限!另外Key的超時長短要根據(jù)業(yè)務(wù)綜合評估,而不是越長越好!(某些業(yè)務(wù)要求key長期有效??梢栽诿看螌懭霑r,都設(shè)置超時時間,讓超時時間順延。)
public Boolean set(final byte[] key, final byte[] value, final long liveTime) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
connection.set(key, value);
if (liveTime > 0) {
connection.expire(key, liveTime);
}
return Boolean.TRUE;
}
});
}
對于必須要存儲的大文本數(shù)據(jù)一定要壓縮后存儲
對于大文本【超過500字節(jié)】寫入到Redis時,一定要壓縮后存儲!大文本數(shù)據(jù)存入Redis,除了帶來極大的內(nèi)存占用外,在訪問量高時,很容易就會將網(wǎng)卡流量占滿,進(jìn)而造成整個服務(wù)器上的所有服務(wù)不可用,并引發(fā)雪崩效應(yīng),造成各個系統(tǒng)癱瘓!
public Boolean setBigValue(final byte[] key, final byte[] value, final long liveTime){
return redisTemplate.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
byte[] compressedBytes = CompressUtil.compress(value);
connection.set(key, compressedBytes);
if (liveTime > 0) {
connection.expire(key, liveTime);
}
return Boolean.TRUE;
}
});
}
壓縮方式可參考
public class CompressUtil {
private static final Inflater infl = new Inflater();
private static final Deflater defl = new Deflater();
private CompressUtil(){
}
public static byte[] uncompress(byte[] inputByte) throws IOException {
int len = 0;
infl.setInput(inputByte);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] outByte = new byte[1024];
try {
while (!infl.finished()) {
len = infl.inflate(outByte);
if (len == 0) {
break;
}
bos.write(outByte, 0, len);
}
infl.end();
} catch (Exception e) {
} finally {
bos.close();
}
return bos.toByteArray();
}
public static byte[] compress(byte[] inputByte) throws IOException {
int len = 0;
defl.setInput(inputByte);
defl.finish();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] outputByte = new byte[1024];
try {
while (!defl.finished()) {
len = defl.deflate(outputByte);
bos.write(outputByte, 0, len);
}
defl.end();
} finally {
bos.close();
}
return bos.toByteArray();
}
}
線上Redis禁止使用Keys正則匹配操作
Redis是單線程處理,在線上KEY數(shù)量較多時,操作效率極低【時間復(fù)雜度為O(N)】,該命令一旦執(zhí)行會嚴(yán)重阻塞線上其它命令的正常請求,而且在高QPS情況下會直接造成Redis服務(wù)崩潰!如果有類似需求,請使用scan命令代替!
//此操作禁止
public Set<byte[]> get(final byte[] pattern){
return redisTemplate.execute(new RedisCallback<Set<byte[]>>() {
@Override
public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
return connection.keys(pattern);
}
});
}
謹(jǐn)慎全量操作Hash、Set等集合結(jié)構(gòu)
在使用HASH結(jié)構(gòu)存儲對象屬性時,開始只有有限的十幾個field,往往使用HGETALL獲取所有成員,效率也很高,但是隨著業(yè)務(wù)發(fā)展,會將field擴(kuò)張到上百個甚至幾百個,此時還使用HGETALL會出現(xiàn)效率急劇下降、網(wǎng)卡頻繁打滿等問題【時間復(fù)雜度O(N)】,此時建議根據(jù)業(yè)務(wù)拆分為多個Hash結(jié)構(gòu);或者如果大部分都是獲取所有屬性的操作,可以將所有屬性序列化為一個STRING類型存儲!同樣在使用SMEMBERS操作SET結(jié)構(gòu)類型時也是相同的情況!
根據(jù)業(yè)務(wù)場景合理使用不同的數(shù)據(jù)結(jié)構(gòu)類型
目前Redis支持的數(shù)據(jù)庫結(jié)構(gòu)類型較多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog和地理空間索引(geospatial)等,需要根據(jù)業(yè)務(wù)場景選擇合適的類型,常見的如:String可以用作普通的K-V、計數(shù)類;Hash可以用作對象等,包含較多屬性的信息;List可以用作消息隊列、粉絲/關(guān)注列表等;Set可以用于推薦;Sorted Set可以用于排行等!
考慮Redis的sharding機制
目前spring-data-redis的JedisConnectionFactory沒有實現(xiàn)sharding的功能,但是其依賴Jedis實現(xiàn)了,所以如果要sharding,需要自己實現(xiàn)邏輯,需要自己去實現(xiàn)封裝
對重要的數(shù)據(jù)使用try/catch
如果必須確保關(guān)鍵性的數(shù)據(jù)可以被放入到 Redis 的實例中,我強烈建議將其放入 try/except 塊中。幾乎所有的Redis客戶端采用的都是“發(fā)送即忘”策略,因此經(jīng)常需要考慮一個 key 是否真正被放到 Redis 數(shù)據(jù)庫中了。至于將 try/expect 放到 Redis 命令中的復(fù)雜性并不是本文要講的,你只需要知道這樣做可以確保重要的數(shù)據(jù)放到該放的地方就可以了。
合理使用Hash
1. 127.0.0.1:6379> HSET foo first_name "Joe"
3. 127.0.0.1:6379> HSET foo last_name "Engel"
5. 127.0.0.1:6379> HSET foo address "1 Fanatical Pl"
7. 127.0.0.1:6379> HGETALL foo
"first_name"
"Joe"
"last_name"
"Engel"
"address"
"1 Fanatical Pl"
14. 127.0.0.1:6379> HGET foo first_name
"Joe"
推薦配置
properties配置
####單機模式#######
##redis的服務(wù)器地址
redis.host=192.168.1.20
##redis的服務(wù)端口
redis.port=6400
####哨兵模式
##redis的服務(wù)器地址
redis.sentinel.master.host=192.168.1.20
##redis的服務(wù)端口
redis.sentinel.master.port=26379
redis.sentinel.slave1.host=192.168.1.21
redis.sentinel.slave1.port=26380
redis.sentinel.slave2=192.168.1.22
redis.sentinel.slave2.port=26381
####集群模式
##redis的服務(wù)器地址
redis.cluster.host1=192.168.1.20
##redis的服務(wù)端口
redis.cluster.port1=6400
redis.cluster.host2=192.168.1.20
redis.cluster.port2=6400
redis.cluster.host3=192.168.1.20
redis.cluster.port3=6400
##redis密碼
redis.pass=1234xxxxx
##redis連接數(shù)據(jù)庫
redis.default.db=0
##客戶端超時時間單位是毫秒
redis.timeout=100000
##最大連接數(shù)
redis.maxActive=300
##最大空閑數(shù)
redis.maxIdle=100
##最大建立連接等待時間
redis.maxWait=1000
##指明是否在從池中取出連接前進(jìn)行檢驗,如果檢驗失敗,則從池中去除連接并嘗試取出另一個
redis.testOnBorrow=true
單機模式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--引入redis配置文件 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:redis.properties</value>
</list>
</property>
</bean>
<!--連接池 屬性定義 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="${redis.maxActive}" />
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWait" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!--定義連接工廠 -->
<bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="database" value="${redis.default.db}" />
<property name="password" value="${redis.pass}" />
<property name="timeout" value="${redis.timeout}" />
<property name="poolConfig" ref="jedisPoolConfig" />
</bean>
<!-- redis模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connectionFactory-ref="jedisConnFactory" p:enableTransactionSupport="true">
<property name="keySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
</bean>
</beans>
哨兵模式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--引入redis配置文件 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:redis.properties</value>
</list>
</property>
</bean>
<!--連接池 屬性定義 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="${redis.maxActive}" />
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWait" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- 哨兵模式 -->
<bean id="redisSentinelConfiguration"
class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<property name="name" value="mymaster">
</property>
</bean>
</property>
<property name="sentinels">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.sentinel.master.host}" />
<constructor-arg name="port" value="${redis.sentinel.master.port}" />
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.sentinel.slave1.host}" />
<constructor-arg name="port" value="${redis.sentinel.slave1.port}" />
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode ">
<constructor-arg name="host" value="${redis.sentinel.slave2.host}" />
<constructor-arg name="port" value="${redis.sentinel.slave2.port}" />
</bean>
</set>
</property>
</bean>
<!--定義連接工廠 -->
<bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration" />
<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
<property name="database" value="${redis.database}" />
<property name="password" value="${redis.pass}" />
<property name="timeout" value="${redis.timeout}" />
</bean>
<!-- redis 模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connectionFactory-ref="jedisConnFactory" p:enableTransactionSupport="true">
<property name="keySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
</bean>
</beans>
集群模式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--引入redis配置文件 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:redis.properties</value>
</list>
</property>
</bean>
<!--連接池 屬性定義 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="${redis.maxActive}" />
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWait" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- 集群模式 -->
<bean id="redisClusterConfiguration"
class="org.springframework.data.redis.connection.RedisClusterConfiguration">
<property name="maxRedirects" value="${redis.maxRedirects}"></property>
<property name="clusterNodes">
<set>
<bean class="org.springframework.data.redis.connection.RedisClusterNode">
<constructor-arg name="host" value="${redis.cluster.host1}"/>
<constructor-arg name="port" value="${redis.cluster.port1}"/>
</bean>
<bean class="org.springframework.data.redis.connection.RedisClusterNode">
<constructor-arg name="host" value="${redis.cluster.host2}"/>
<constructor-arg name="port" value="${redis.cluster.port2}"/>
</bean>
<bean class="org.springframework.data.redis.connection.RedisClusterNode">
<constructor-arg name="host" value="${redis.cluster.host3}"/>
<constructor-arg name="port" value="${redis.cluster.port3}"/>
</bean>
</set>
</property>
</bean>
<!--定義連接工廠 -->
<bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg name="clusterConfig" ref="redisClusterConfiguration" />
<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
<property name="database" value="${redis.database}" />
<property name="password" value="${redis.pass}" />
<property name="timeout" value="${redis.timeout}" />
</bean>
<!-- redis 模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connectionFactory-ref="jedisConnFactory" p:enableTransactionSupport="true">
<property name="keySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
</bean>
</beans>