大部分互联网公司都需要处理计数器场景,例如风控系统的请求频控、内容平台的播放量统计、电商系统的库存扣减等。
传统方案一般会直接使用RedisUtil.incr(key)
,这是最简单的方式,但这种方式在生产环境中会暴露严重问题:
// 隐患示例
public long addOne(String key) {
Long result = RedisUtil.incr(key);
// 若未设置TTL,key将永久驻留内存
return result;
}
大部分互联网公司都需要处理计数器场景,例如风控系统的请求频控、内容平台的播放量统计、电商系统的库存扣减等。
传统方案一般会直接使用RedisUtil.incr(key)
,这是最简单的方式,但这种方式在生产环境中会暴露严重问题:
// 隐患示例
public long addOne(String key) {
Long result = RedisUtil.incr(key);
// 若未设置TTL,key将永久驻留内存
return result;
}
Redis是一个高效的内存数据库,它支持包括String、List、Set、SortedSet和Hash等数据类型的存储,在Redis中通常根据数据的key查询其value值,Redis没有模糊条件查询,在面对一些需要分页、排序以及条件查询的场景时(如评论,时间线,检索等),只凭借Redis所提供的功能就不太好不处理了。
本文不对Redis的特性做过多赘述。由于之前基于业务问题需要实现基于Redis的条件查询和分页功能,在百度上查询了不少文章,基本不是只有分页功能就是只有条件查询功能的实现,缺少两者组合的解决方案。因此,本文将基于Redis提供条件查询+分页的技术解决方案。
编写一个MyBatis插件可以让你在执行SQL语句前后进行自定义的操作,比如日志记录、性能监控等。下面我将演示一个简单的MyBatis插件,它会在执行查询SQL语句前打印一条日志。
首先,你需要实现一个MyBatis的拦截器(Interceptor)。一个拦截器需要实现MyBatis的Interceptor接口,其中最重要的是intercept方法,它会在执行SQL语句前后被调用。
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Statement;
import java.util.Properties;
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, org.apache.ibatis.session.ResultHandler.class})})
public class LoggingInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("Before executing query...");
Object result = invocation.proceed(); // 执行原来的方法
System.out.println("After executing query...");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可以在这里设置一些属性
}
}
今天来讲讲 Redis 的请求监听,通俗点说,就是Redis是如何监听客户端发出的set、get等命令的。
众所周知,Redis 是单进程单线程架构,虽然是单进程单线程,但是Redis的性能却毫不逊色,能轻松应对一般的高并发场景,那么Redis究竟是施了什么魔法呢?
其实 Redis 的原理和 Nginx 差不多,都利用了 IO 多路复用来提高处理能力,所谓多路复用,就是一个线程可以同时处理多个IO操作,当 某个 IO 操作 Ready 时,操作系统会主动通知进程。使用 IO 多路复用,我们可以使用 epoll、kqueue、select,API 都差不多。
一般来说,现在的redis连接都会有池化技术, Redis连接池技术有以下好处:
资源复用:连接池允许多个客户端共享一定数量的数据库连接。当一个客户端完成操作并释放连接时,这个连接可以被另一个客户端重用,而不是关闭后重新建立,这大大减少了创建和销毁连接所需的时间和资源。
减少等待时间:预创建的连接可以立即提供给需要它们的客户端。这意味着应用程序不需要等待建立连接就可以执行操作,从而提高了响应速度。
提高性能:连接池可以减少因频繁打开和关闭连接造成的开销,因为建立数据库连接通常是一个高成本的操作,包括网络延迟、认证等过程。
连接数控制:连接池可以限制系统中活跃的总连接数。这防止了系统打开过多的连接,从而避免了对数据库服务器资源的过度消耗和可能的拒绝服务。
自动管理:连接池通常提供自动化的连接管理功能,如检测空闲连接、移除无效连接等,而不需要开发者手动干预。
减轻数据库压力:通过重用连接,连接池使得数据库服务器不必处理大量短生命周期的连接,这有助于维持数据库的稳定性和性能。
配置灵活性:可以根据应用程序的负载特征和数据库服务器的能力对连接池进行配置,如调整池的大小、连接的生命周期等。
故障恢复:当某个连接发生故障时,连接池可以提供一个新的连接替代它,从而增强了应用程序的鲁棒性。
更好的并发处理:在高并发环境下,连接池可以更有效地管理不同线程或进程的连接需求,减少了锁的竞争和上下文切换的成本。
简化编程模型:开发者可以从连接池中获取连接,执行操作,然后返回连接,而无需关心连接的创建和关闭,简化了编程模型。
回忆下之前创建一个Guava cache
对象时的代码逻辑:
public LoadingCache<String, User> createUserCache() {
return CacheBuilder.newBuilder()
.initialCapacity(1000)
.maximumSize(10000L)
.expireAfterWrite(30L, TimeUnit.MINUTES)
.concurrencyLevel(8)
.recordStats()
.build((CacheLoader<String, User>) key -> userDao.getUser(key));
}
来源:稀土掘金社区深入理解缓存原理与实战设计,Seven进行了部分补充完善
Ehcache最初是由Greg Luck于2003年开始开发,截止目前,Ehcache已经演进到了3.10.0
版本,各方面的能力已经构建的非常完善。Ehcache官网上也毫不谦虚的描述自己是“Java's most widely-used cache”,即JAVA中使用最广泛的缓存,足见Ehcache
的强大与自信。
详情点击这篇文章
来源:稀土掘金社区深入理解缓存原理与实战设计,Seven进行了部分补充完善
本篇文章中,我们就来一起深入剖析JAVA本地缓存的优秀“轮子” —— 来自Google家族的Guava Cache
。聊一聊其实现机制、看一看如何使用。