目录
哈希函数(散列函数)
布隆过滤器
Redis原子性操作
反向代理
哈希函数(散列函数)
哈希函数的输入域可以是非常大的范围,但是输出域是固定的范围设为R,并具有如下性质:
1、典型的哈希函数都有无穷的输入值域
2、当给哈希函数传入相同的输入值时,返回值一样
3、当给hash函数传入不同的输入值时,返回值可能一样,也可能不一样
4、很多不同的输入值所得到的返回值会均匀地分布在R上
布隆过滤器
1)布隆过滤器是用来判断一个元素是否出现在给定集合中的重要工具,具有快速,比哈希表更节省空间等优点,而缺点在于有一定的误识别率(false-positive,假阳性),亦即,它可能会把不是集合内的元素判定为存在于集合内,不过这样的概率相当小,在大部分的生产环境中是可以接受的;
2)其原理比较简单,S集合中有n个元素,利用k个哈希函数,将S中的每个元素映射到一个长度为m的位(bit)数组B中不同的位置上,这些位置上的二进制数均置为1,如果待检测的元素经过这k个哈希函数的映射后,发现其k个位置上的二进制数不全是1,那么这个元素一定不在集合S中,反之,该元素可能是S中的某一个元素(参考1);
3)到底需要多少个哈希函数,以及创建长度为多少的bit数组比较合适,为了估算出k和m的值,在构造一个布隆过滤器时,需要传入两个参数,即可以接受的误判率fpp和元素总个数n(不一定完全精确)。
使用场景:
1、网页爬虫对URL的去重,避免爬取相同的URL地址
2、反垃 圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱(同理,垃圾短信)
3、缓存击穿,将已存在的缓存放到布隆过滤器中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉。
Redis原子性操作
1、Redis所有单个命令的执行都是原子性的
2、多个操作也支持事务,及原子性:通过MULTI和EXEC指令包起来
Redis分布式锁:相关命令:
setnx | SETNX是『SET if Not eXists』(如果不存在,则SET)将key的值设为value,当且仅当key不存在。若给定的key已经存在,则SETNX不做任何动作。 |
getset | 获取旧的值并设置新的值,原子性操作 |
expire | 设置键的有效期,原子性操作 |
del | 删除,原子性操作 |
应用:(1)使用互斥锁,解决缓存击穿(一份热点数据,它的访问量非常大。在其缓存失效(如过期淘汰等)的瞬间,大量请求直达存储层,导致服务崩溃。):该方法是比较普遍的做法,即,在根据key获得的value值为空时,先锁上,再从数据库加载,加载完毕,释放锁。若其他线程发现获取锁失败,则睡眠50ms后重试。至于锁的类型,单机环境用并发包的Lock类型就行,集群环境则使用分布式锁( redis的setnx)。集群环境的redis的代码如下所示:
String get(String key) {
String value = redis.get(key);
if (value == null) {
if (redis.setnx(key_mutex, "1")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(key_mutex, 3 * 60)
value = db.get(key);
redis.set(key, value);
redis.delete(key_mutex);
} else {
//其他线程休息50毫秒后重试
Thread.sleep(50);
get(key);
}
}
}
(2)设置永不过期(异步构建缓存:构建缓存采取异步策略,会从线程池中取线程来异步构建缓存,从而不会让所有的请求直接怼到数据库上。该方案redis自己维护一个timeout,当timeout小于System.currentTimeMillis()时,则进行缓存更新,否则直接返回value值。)
集群环境的redis代码如下所示:
String get(final String key) {
V v = redis.get(key);
String value = v.getValue();
long timeout = v.getTimeout();
if (v.timeout <= System.currentTimeMillis()) {
// 异步更新后台异常执行
threadPool.execute(new Runnable() {
public void run() {
String keyMutex = "mutex:" + key;
if (redis.setnx(keyMutex, "1")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(keyMutex, 3 * 60);
String dbValue = db.get(key);
redis.set(key, dbValue);
redis.delete(keyMutex);
}
}
});
}
return value;
}
反向代理
传统代理服务器位于浏览器一侧,代理浏览器将HTTP请求发送到互联网上,而反向代理服务器位于网站机房一侧,代理网站Web服务器接收HTTP请求。和传统代理服务器可以保护浏览器安全一样,反向代理服务器也具有保护网站安全的作用,来自互联网的访问请求必须经过代理服务器,相当于在web服务器和可能的网站之间建立了一个屏障。
除了安全,代理服务器也可以通过配置缓存功能加速web请求。当用户第一次访问静态内容的时候,静态内容就被缓存在反向代理服务器上,这样当其他用户访问该静态内容的时候,就可以直接从反向代理服务器返回,加速web请求响应速度,减轻web服务器负载压力。有些网站会把动态内容也缓存在代理服务器上,当这些动态内容有变化时,通过内部通知机制通知反向代理缓存失效,反向代理会重新加载最新的动态内容再次缓存起来。
反向代理也可以实现负载均衡的功能,通过负载均衡构建的应用集群可以提高系统总体处理能力,进而改善网站高并发情况下的性能。由于web服务器不直接对外提供访问,因此web服务器不需要使用外部ip地址,而反向代理服务器则需要配置双网卡和内部外部两套IP地址。由于反向代理服务器转发请求在HTTP协议层面,因此也叫应用层负载均衡。