来源 http://www.javashuo.com/article/p-ruynfosf-cu.htmlhtml
Redis分布式锁实现的三个核心要素nginx
1. 加锁web
setnx(key,1)当一个线程执行setnx返回1,说明key本来不存在,该线程成功获得了锁,当其余线程执行setnx返回0,说明key已经存在,该线程抢锁失败。redis
2.解锁算法
当获得锁的线程执行完任务,须要释放锁,以便其余线程能够进入。释放锁的最简单方式是执行del指令,del(key)释放锁以后,其余线程就能够继续执行setnx命令来得到锁。数据库
3.锁超时后端
若是一个获得锁的线程在执行任务的过程当中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别的线程再也别想进来。设计模式
因此,setnx的key必须设置一个超时时间,以保证即便没有被显式释放,这把锁也要在必定时间后自动释放。setnx不支持超时参数,因此须要额外的指令,expire(key, 30)浏览器
if(setnx(key,1) == 1){ expire(key,30) try { do something ...... }catch() { } finally { del(key) } }
问题 1 setnx和expire的非原子性
缓存
设想一个极端场景,当某线程执行setnx,成功获得了锁:setnx刚执行成功,还将来得及执行expire指令,该节点挂了。这样一来,这把锁就没有设置过时时间,变得“长生不老”,别的线程再也没法得到锁了。
解决:
setnx指令自己是不支持传入超时时间的,Redis 2.6.12以上版本为set指令增长了可选参数,伪代码以下:set(key,1,30,NX),这样就能够取代setnx指令
问题 2 超时后使用del 致使误删其余线程的锁:
假如某线程成功获得了锁,而且设置的超时时间是30秒。
若是某些缘由致使线程B执行的很慢很慢,过了30秒都没执行完,这时候锁过时自动释放,线程B获得了锁。随后,线程A执行完了任务,线程A接着执行del指令来释放锁。但这时候线程B还没执行完,线程A实际上删除的是线程B加的锁。
解决:
能够在del释放锁以前作一个判断,验证当前的锁是否是本身加的锁。
至于具体的实现,能够在加锁的时候把当前的线程ID当作value,并在删除以前验证key对应的value是否是本身线程的ID。
1 加锁: 2 String threadId = Thread.currentThread().getId() 3 set(key,threadId ,30,NX) 4 5 doSomething..... 6 7 解锁: 8 if(threadId .equals(redisClient.get(key))){ 9 del(key) 10 }
可是,这样作又隐含了一个新的问题,if判断和释放锁是两个独立操做,不是原子性的
能够经过使用Lua脚本 来实现两个语句的原子性。
1. 在执行业务时,多个并发的请求,致使同个资源被访问被屡次执行(加锁)
2. 加锁后,业务并发访问这个线程大部分都返回了失败,只有少部分获取到锁的线程才能处理,这种状况下是很是不通情理的(使用reddison已经内部实现的自旋锁来进行锁的等待,获取不到锁就while循环一直尝试加锁,知道锁的获取成功才返回结果,可是这是悲观锁)
3. 加完锁后,每次获取完锁时就对一个特定值+1,执行完后对特定值进行释放,可是线程拿到锁后,抛出异常时,没法执行最后释放锁操做(加finally块执行释放锁操做)
4. 加了finally块后,这个块中的业务失败了,或者程序挂了,redis链接失败了,没法释放锁(对锁加超时时间)
5. 加了超时时间后,在持有锁这个业务中的执行时间比超时时间长,在业务执行的时候,锁超时释放了,这时,其余的请求的线程就能获取到这个锁了,出现了线程的重复进入
(对redis锁的key值用一个UUID来设计,同个线程内获取这个锁都须要生成一个惟一的id,释放时只能让同个线程内存放的id匹配释放)
,第二个线程执行了业务,并且这个业务执行后,比第一个线程快,而且锁超时解锁解了第一个锁
(获取到这个锁的时候,另外开辟一个线程,好比超时时间是10秒,这个线程就每5秒就查询一下这个锁是否失效,若是没有失效就增长5秒中,保持这个锁的有效性)
参考 http://www.javashuo.com/article/p-ngkmxygo-gt.html
http链接池+NIO+线程池(多生产者多消费者)(反向代理服务器,一致性哈希算法)+阻塞队列+数据库链接池+缓存(主从、集群)+数据库(集群、分库主从)。
细节:
一、设置http链接池,能够下降延迟,提升客户端响应时间。还能够链接池复用,支持更大的并发量。
二、把一些静态资源先加载到浏览器缓存里面,减小服务器端的压力
三、能够对服务器端的数据进行压缩
四、反向代理服务器能够保护服务器的安全,来自互联网的请求必需通过代理服务器。因此也能够在代理服务器放一些静态数据,当用户第一次访问静态内容时,静态内容就被缓存在反向代理服务器上,其余用户请求进来时,就能够直接返回,减轻web服务器负载压力。
五、NIO模型(是在Linux仍是Windows系统下,Windows建议用AIO,Linux系统下AIO的底层也是基于epoll多路复用,差异不大,LF的区别)
六、线程池(根据线程池处理不一样性质的任务,要有不一样性质的线程池,IO密集型,CPU*2。CPU密集型,CPU+1.多生产者多消费这模型)线程池还须要考虑:a.先设置一个最大线程数量和最小线程数量,进行性能评估,压测。b.线程池阻塞队列的大小要有界,不然服务器压力过大。c.须考虑线程池的失败策略,失败后的补偿。d.后台批处理服务须与线上面向用户的服务进行分离。
七、阻塞队列,由于NIO第二个阶段会引发用户线程的阻塞,好比可能等待JDBC链接数据库,所以在这里用一个阻塞队列,线程把请求放到阻塞队列里面,这个线程就能够回归线程池,处理别的事情了。是一个生产者消费者模型
八、建一个数据库链接池,主要是为了减小资源的消耗、减小延迟。
九、数据存储部分,1)根据实际状况设置索引和优化SQL语句。2)幂等、乐观、悲观。3)防止SQL注入攻击。4)一个事务当中操做不要过多,可能会阻塞,进而累积形成数据库的故障。5)数据量太大,查询的时间利用limit关键字进行分页处理,防止结果集太大,让应用OOM。
十、使用缓存,减小数据库的访问次数,提升并发量。1)缓存的结构,LRU,链表(集合类存放超时对象),大小,时间。2)核心业务和非核心业务进行分离,减小相互影响的可能性,不要使用共享缓存。3)不经常使用的数据不要使用缓存。4)夜间查询一天之类搜索频率比较高的词汇,结合AI进行预测,预测的结果预先放到缓存里面。5)考虑分布式缓存数据库:Redis、memcached,防止本地缓存内存溢出。Redis的主从同步,读写分离、负载均衡。主从+一级二级缓存+哨兵。哨兵是Redis 的高可用性解决方案:由一个或多个哨兵实例 组成的哨兵 系统能够监视任意多个主服务器,以及这些主服务器属下的全部从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。
十一、主从有瓶颈,会有延迟、主服务器压力过大。考虑集群、分库分表。考虑用一致性哈希算法实现分布式缓存数据库。数据迁移量小,引入虚拟节点、防止数据倾斜。
十二、 如有重复数据,布隆过滤器去重。
1三、数据库存储文件名之类的,服务器保存实际的数据。
保持数据一致性的方法:1. 消息队列 2. 用同一个数据库,A.B,C用同一个数据库。 3。 用Redis缓存,把一些数据放到缓存。
来源 http://www.javashuo.com/article/p-rxyoqhdb-n.html
1、系统架构扩展
系统的扩展性能够提供系统的性能。表明系统可以容纳更高的负载、更大的数据集,而且系统是可维护的。扩展能够分为两种:
垂直扩展(stade up),提升单一的机器性能配置,如添加内存、更换更强的处理器等等。
2.水平扩展(out),横向添加新机器。
水平扩展比垂直扩展有更强大的扩展性,但水平扩展也来了更高的维护成本。实践中须要根据具体状况来寻求一个平衡点。
2、静态化技术
采用预处理方式将页面静态化,存储在磁盘,不须要链接数据库读取数据,能够提升服务端性能
3、使用缓存服务器减小IO
使用Redis,Memache内存服务器,在内存中存储数据,减小磁盘IO读取量
4、引入微服务器框架Dubbo,SpringCloud
将业务模块切分为多个微服务,托管在微服务框架中,提升负载均衡与容错处理
5、使用数据库集群技术
数据库层面采用主从复制模式、集群模式、采用分区表将数据平均分配到多个磁盘控制器。采用读写分离设计模式
6、图片存储在分布文件系统或CDN服务中
静态资源(图片、视频、网页)采用分布式存储,或者使用CDN服务分发,系统须要设计为先后端分离。
7、业务处理采用NIO技术
采用非阻塞IO技术,借鉴Dubbo使用Netty框架。
8、应用服务调优(Tomcat,Weblogic,Websphere)
通用的配置是设置JVM参数,内存各分区大小,垃圾回收线程多少。再根据不一样应用服务器的配置参数,优化应用服务器。
9、负载均衡
使用了水平扩展之如何将大量的请求“均衡”到咱们的扩展机器上
两种负载均衡模式:有状态(若有携带session)和无状态
两种负载均衡方式:硬件均衡和软件均衡
硬件均衡比较简单,一般接入一个设备便可,以后的均衡和故障检测等等都由硬件自动完成。成本较高。
软件均衡则是经过软件来转发各类请求,更加容易的定义转发规则,有较多的开源产品选择。
第四层和第七层
常常在负载均衡中看到第四层和第七层这两个名词。它们其实是指它们各自工做时所处理的网络协议的层数(使用ISO模型)。
第四层是数据传输层,包括TCP和UDP,第七层则是应用层,一般web中为HTTP应用。如Apache、nginx等支持第七层的均衡,并且可配置性都至关强大,可以适应较复杂的应用。例如能够简单的将流量分担到各个负载机上,也能够定义一套业务规则,将应用划分为不一样的池,每一个池处理某些固定规则的URL。
对比软件均衡与硬件均衡,能够发现它们各自的优缺点:
硬件均衡成本比较高,软件均衡多数可使用免费的开源软件来实现。
硬件均衡对于故障检测比软件均衡更增强大、快速。在采用硬件均衡时,一旦某台机器出现故障,立刻就能够检测出来并当即屏蔽。
硬件均衡能够快速的添加机器(接入硬件接口便可),而软件均衡除了添加机器外还要添加一些配置信息,以将某些流量导入到新的机器。
软件均衡能够定义很是复杂的业务规则,而硬件均衡在这方面相对较弱。
多数的硬件均衡方案都有捆绑附加的一些服务如HTTPS加速、DOS防火墙等等。
10、操做系统优化虚拟内存调优、可用文件句柄多少配置,