Summary of common distributed scenarios

Distributed combat

Cache

	缓存概念:
		1、命中率,根据数据使用上的规律,二八规律:有20%的数据最常用,加载入缓存/有80%的数据不常用,最好不占用缓存
		2、缓存将满时回收策略---LRU:  统计数据的使用频率,优先回收频率低的数据 --- 这种缓存失效是被动的
		3、缓存淘汰: 为了应对小缓存映射大数据,需要淘汰策略来取消不常用数据
		绝对过期: 比如设置10分钟有效, 则从数据加入缓存开始算,10分钟后清除掉
		滑动过期: 比如web里面的session机制, 如果整个30分钟没被访问过,就回收掉了

	使用缓存的时候,存在数据一致性问题,有四种处理方法,其中使用比较多的缓存失效机制,容易引起缓存雪崩,缓存穿透,缓存击穿。
	缓存穿透:
       		缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
     	解决:
		1,接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
		2,从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
		3,布隆过滤器,BloomFilter
		布隆过滤器(redis中也使用了):
			本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构,布隆过滤器可以理解为一个不怎么精确的set结构,当你使用它的contains方法判断某个对象是否存在时,它可能会误判。但是布隆过滤器也不是特别不精确,只要参数设置的合理,它的精确度可以控制的相对足够精确,只会有小小的误判概率。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。
			布隆过滤器有二个基本指令,bf.add 添加元素,bf.exists 查询元素是否存在。

			注意:布隆过滤器的initial_size估计的过大,会浪费存储空间,估计的过小,就会影响准确率,布隆过滤器的error_rate越小,需要的存储空间就越大,对于不需要过于精确的场合,error_rate设置稍大一点。
			原理:
				布隆过滤器是一个bit向量或者说bit数组,在映射一个值到布隆过滤器中时,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向的bit位置置为1,
				然后有其他的值再被映射进来的时候会可能在某个已经为1的bit位置上覆盖再次置为1,这样当查询的时候,根据hash函数计算后返回的bit位置,判断是否为1,如果返回的全是1,也因为可能有其他值进来填充的1所以不能保证一定存在,但是如果有一个不为1,就能保证一定不存在。
			优点:
				1,相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入、查询时间都是常数。另外,Hash函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。
				2,布隆过滤器可以表示全集,其它任何数据结构都不能;
				3,k 和 m 相同,使用同一组 Hash 函数的两个布隆过滤器的交并差运算可以使用位操作进行。
			缺点:
				1,随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。
				2,一般情况下不能从布隆过滤器中删除元素. 我们很容易想到把位列阵变成整数数组,每插入一个元素相应的计数器加1,这样删除元素时将计数器减掉就可以了。然而要保证安全的删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面,这一点单凭这个过滤器是无法保证的。
				3,计数器回绕也会造成问题。

	缓存击穿:
		缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
	解决:
		1,加锁
		2,失效时间

	缓存雪崩:
		数据大批量到过期时间,而查询数据量巨大,在缓存失效的一瞬间,有多高并发请求直接透过缓存请求数据库,使得数据库挂掉。
	解决:
		1,给缓存失效时间加“盐”,不让所有的缓存的失效时间一样。
		2,使用jvm锁,推荐使用细粒度锁,mybatis的阻塞装饰器缓存机制中就是使用的对key进行的加锁的细粒度锁,使用粗粒度的锁,即是所有请求争取一把锁,这样效率低。
		3,分布式部署,使用分布式锁。
		4,限流。
		5,缓存预热。

	注意:和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

Distributed lock

Implementation methods:
1, mysql
2, zk
3, redis (SETNX), the disadvantage is that blocking locks cannot be implemented elegantly

Distributed transaction

The lock is for multiple threads to operate one resource, and the transaction is for one thread to operate multiple resources. There is no additional transaction if there is only one resource.

		解决方案:
		1,两阶段提交(2PC)
			两阶段提交使用XA协议的原理,两阶段提交这种解决方案属于牺牲了一部分可用性来换取的一致性。
			优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)
			缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景,如果分布式系统跨接口调用,目前 .NET 界还没有实现方案。

		2,补偿事务(TCC)
			Try:阶段主要是对业务系统做检测及资源预留
			Confirm:阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
			Cancel:阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
			即是所有的try都成功了,再Confirm确认,查询一次数据看是否try真正成功,成功了就完成整个事务,不成功就Cancel
			优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
			缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。
			比较好的使用方式:加一个锁定字段,在try的时候修改这个字段为锁定状态,Cancel恢复锁定状态,这样因为锁定了其他线程,Confirm的成功率大大增加了。
			tcc框架有:tcc trascation,bytetcc等。tcc trascation中使用@Conmpseable在try的方法上,然后注解参数中指定confirm和cancel的方法名。


		3,本地消息表(异步确保)
			本地消息表这种实现方式应该是业界使用最多的,其核心思想是将分布式事务拆分成本地事务进行处理,这种思路是来源于ebay。
			基本思路就是:
			消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。
			消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。
			生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。

			这种方案遵循BASE理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景的,即不会出现像2PC那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。
			优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。在 .NET中 有现成的解决方案。
			缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

		4,MQ事务消息
			有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
			原理:
			第一阶段Prepared消息,会拿到消息的地址。
			第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
			也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

			优点: 实现了最终一致性,不需要依赖本地数据库事务。
			缺点: 实现难度大,主流MQ不支持,没有.NET客户端,RocketMQ事务消息部分代码也未开源。

		5,Sagas 事务模型
			Saga事务模型又叫做长时间运行的事务(Long-running-transaction),它是由普林斯顿大学的H.Garcia-Molina等人提出,它描述的是另外一种在没有两阶段提交的的情况下解决分布式系统中复杂的业务事务问题。该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。
			比如我们一次关于购买旅游套餐业务操作涉及到三个操作,他们分别是预定车辆,预定宾馆,预定机票,他们分别属于三个不同的远程接口。可能从我们程序的角度来说他们不属于一个事务,但是从业务角度来说是属于同一个事务的。

		目前阿里开源了一个分布式事务框架GTS,底层是基于TCC。

sign in

1. Use cookies
to implement the login logic through a CAS system, so login requests from other pages will jump to the CAS system to perform login. After the login is judged, a ticket and sessionid are generated and stored in redis, and the ticket is returned to the requested page , The system of the requested page then obtains the sessionid from redis through the ticket, generates a session, creates a cookie and puts it in reqesones, and then redirects to the current page to judge that the cookie is logged in again.

2,tooken

Distributed task

1. Elastic-job
relies on zk and can process tasks in fragments, that is, some tasks can be evenly distributed to multiple systems for common processing to improve efficiency.

2,xxl-job

Guess you like

Origin blog.csdn.net/qq_28822933/article/details/88362331