五种数据结构
分布式锁
队列
延迟队列
位图
bitfield
HyperLogLog
BloomFilter
限流
简单限流
漏桶限流
地理位置 Geo 模块
scan
rehash 操作变化
- 如果 slot 的二进制值为 xxxx
- 在 rehash 后,该 slot 的一半值还在 0xxxx, 而另一半就在 1xxxx (xxxx+扩容数)
扩容、缩容后 scan 遍历顺序变化
- TODO
扩展
过期策略
LRU
异步操作
jedis 优化使用
- 硬编码要求使用自带回收连接的 jedis
例子是 springboot + jedis 依赖(木有 redisTemplate)
初始化
读取配置文件值类
/**
* 单机版 jedis 实例
*/
@Component
public class JedisSinglePropertiesValue {
/**
* 单机服务器host
*/
@Value("${spring.redis.host}")
private String host;
/**
* 单机服务器端口
*/
@Value("${spring.redis.port}")
private int port;
/**
* 选用哪个数据库
*/
@Value("${spring.redis.database}")
private int database;
/**
* 连接超时时间,单位 ms
*/
@Value("${spring.redis.timeout}")
private int timeout;
/**
* 连接池最大连接数
*/
@Value("${spring.redis.jedis.pool.max-active}")
private int maxActive;
/**
* 连接池最大空闲连接数
*/
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
/**
* 连接池最小空闲连接数
*/
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
/**
* 连接池最大堵塞等待时间
*/
@Value("${spring.redis.jedis.pool.max-wait}")
private int maxWait;
// getter setter
}
工具类赋值类
/**
* 为工具类注入属性的代理工具
*/
@Component
public class ReadValueUtil {
public ReadValueUtil(JedisPool jedisPool) {
MyJedisPool.setJedisPoolOnlyOnce(jedisPool);
}
}
配置类
@Configuration
public class JedisConfig {
private static final Logger logger = LoggerFactory.getLogger(JedisConfig.class);
/**
* 属性相关
*/
@Autowired
private JedisSinglePropertiesValue singlePropertiesValue;
@Bean
public JedisPool jedisPoolFactory() {
logger.info("单机版 jedis 注入开始");
JedisPoolConfig config = new JedisPoolConfig();
// 高版本 jedis 将 maxActive -> maxTotal
config.setMaxTotal(singlePropertiesValue.getMaxActive());
config.setMaxIdle(singlePropertiesValue.getMaxIdle());
config.setMinIdle(singlePropertiesValue.getMinIdle());
// 高版本 jedis 将 maxWait -> maxWaitMillis
config.setMaxWaitMillis(singlePropertiesValue.getMaxWait());
// jmx 监控,默认 true
config.setJmxEnabled(true);
return new JedisPool(config,
singlePropertiesValue.getHost(),
singlePropertiesValue.getPort(),
singlePropertiesValue.getTimeout());
}
}
优化
封装指令
public interface CallWithJedis {
/**
* <h2> 对参数 jedis 实例的具体业务逻辑 </h2>
*
* @param jedis
*/
public abstract void call(Jedis jedis);
}
封装 jedis 回收
/**
* 硬性封装 jedis 的连接归还操作
*
* 可以这样使用
*
* HolderResult<String> ret = new HolderResult<>();
* MyJedisPool.execute((jedis)-> {
* String r = jedis.set("heheda", "555");
* ret.setResult(r);
* });
* System.out.println(ret);
*/
public class MyJedisPool {
private static final Logger logger = LoggerFactory.getLogger(MyJedisPool.class);
private static JedisPool jedisPool;
/**
* 只可以初始化一次 JedisPool
*/
public static void setJedisPoolOnlyOnce(JedisPool jedisPool) {
if (Objects.isNull(MyJedisPool.jedisPool)) {
MyJedisPool.jedisPool = jedisPool;
} else {
throw new RuntimeException("MyJedis is already set JedisPool, do not repeat");
}
}
/**
* <h2> 强制使用 try with resources 归还 jedis 连接 </h2>
*
* @param call 具体的代码逻辑
*/
public static void execute(CallWithJedis call) {
try (Jedis jedis = jedisPool.getResource()) {
call.call(jedis);
} catch (JedisConnectionException e) {
logger.error("连接出错: {}", e.getLocalizedMessage(), e);
// TODO 重试或者记录日志
}
}
}
封装返回结果
/**
* 包装 jedis 的返回结果
*
* 绕过闭包要求 final 和 不可修改的限制
* @param <T>
*/
public class HolderResult<T> {
private T result;
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
@Override
public String toString() {
return "HolderResult{" +
"result=" + result +
'}';
}
}
.etc lua
- 如何发送 lua 脚本
- 直接发送 Lua到Redis 服务器去执行。
先把Lua 发送给 Redis, Redis 会对 Lua 脚本进行缓存,然后返回 SHA 32 位编码回来,之后只需要发送 SHAl 和相关参数给 Redis 便可以执行了
-
运行
@Test
void contextLoads() {
HolderResult<Object> ret = new HolderResult<>();
MyJedisPool.execute((jedis) -> {
ret.setResult(jedis.evalsha(jedis.scriptLoad(readToString("src/main/resources/test.lua"))));
});
System.out.println(ret);
}
public String readToString(String fileName) {
String encoding = "UTF-8";
File file = new File(fileName);
long fileLength = file.length();
byte[] fileContent = new byte[(int) fileLength];
FileInputStream in = null;
try {
in = new FileInputStream(file);
in.read(fileContent);
return new String(fileContent, encoding);
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
-