我在执行某些负载平衡算法的存储过程时遇到问题。该服务远程调用此过程,并且该过程必须使用正确的端口来回复该消息。
问题是我想要一个出色的表现(500个大写或者消息/秒)。因此,该服务每秒要求500次数据库,这是将消息转发给它的好端口。
N.B。 :我确信运行数据库服务器的VM有足够的资源来达到500个请求/秒。事实上,我为了更简单的查询而达到了这个数字(选择,更新......)。
这是存储过程:
-- Function: loadbalancing(integer)
-- DROP FUNCTION loadbalancing(integer);
CREATE OR REPLACE FUNCTION loadbalancing(num integer) RETURNS
integer AS $BODY$DECLARE
-- Declarations
port_selected integer;
total_call_attempts integer;
BEGIN
--
-- *** *** *** *** *** ***
-- *** Load Balancing Algorithm ***
-- *** *** *** *** *** ***
--
-- ***************
-- *** locking ***
-- ***************
perform number_of_call_attempt, destination_rate FROM load_distribution WHERE loadb_id = num FOR UPDATE;
-- *****************************
-- *** select the right port ***
-- *****************************
select port_number into port_selected from ( select *, row_number() over(order by destination_ratio asc,
destination_target_rate desc, port_number asc) as rn from
load_distribution where active = true and loadb_id = num ) t where
t.rn = 1;
-- ******************************************************
-- *** Update the number of call attempts at one port ***
-- ******************************************************
UPDATE load_distribution SET number_of_call_attempt = number_of_call_attempt
+ 1 WHERE port_number = port_selected AND loadb_id = num;
-- **************************************
-- *** Get the total of call attempts ***
-- **************************************
SELECT SUM(number_of_call_attempt) into total_call_attempts FROM
load_distribution WHERE loadb_id = num;
-- *******************************
-- *** Update destination rate ***
-- *******************************
UPDATE load_distribution SET destination_rate = (number_of_call_attempt / total_call_attempts)
WHERE loadb_id = num;
-- ********************************
-- *** Update destination ratio ***
-- ********************************
UPDATE load_distribution SET destination_ratio = (destination_rate / destination_target_rate) WHERE
loadb_id = num;
-- ***********************************************
-- *** Return a port to the requesting service ***
-- ***********************************************
RETURN port_selected;
END;$BODY$ LANGUAGE plpgsql VOLATILE COST 100;
ALTER FUNCTION loadbalancing(integer) OWNER TO postgres;
我遇到的麻烦是这最多可以达到70或100个请求/秒。当我开始达到200或更多请求/秒时,Postgres数据库会发送超时异常。
您认为这个问题出于什么原因?至于我,我认为这是一个并发访问的问题。实际上,该过程是根据其他列值选择端口号。每次呼叫/请求到达时,都会更改这些列值。之后的请求必须使用更改的值(干净状态),而不是处于脏状态或未更改值的值(如果下一个请求在上一个请求更改值之前或之前出现)。
因此,我想到的一个解决方案是使用SELECT FOR UPDATE进行显式锁定。不幸的是,我在我的算法中放置的那个似乎不起作用,或者它不是好的解决方案。我是锁定和存储过程的新手。
那么,您认为解决此问题的解决方案是什么? (即提高性能以满足更多请求/ s)。
答案 0 :(得分:0)
这让我觉得在RDBMS中尝试做的事情非常糟糕。我使用易失性存储,可能是非事务性的。在这里做的最好的事情几乎肯定是使用现有的,完善的负载平衡工具,或者如果你必须DIY,那么使用专为小原子数据的低延迟快速查询而设计的技术
在多个查询中,在pl / pgsql存储过程中执行多个窗口和聚合传递是最糟糕的方法。特别是因为你基本上是对行进行序列化访问。
您可能不需要这种分配准确,无差错并且完全尊重分配的比率。相反,你可能需要它非常快,而且大致足够好。 PL / PgSQL不是这项工作的合适工具,只是几乎没有任何一点建议你改进它。它不会起作用。
除了锁定问题之外,您还会通过此快速流失更新过程生成大量 MVCC膨胀,这反过来会产生更多负载进行真空清理。所有ACID都保证您既不需要也不想要。
如果你真的必须使用这种方法,一些事情会有所帮助:
SET synchronous_commit = off
用于调用proc UNLOGGED
表作为端口映射表destination_ratio
声明中设置destination_rate
,number_of_call_attempt
和UPDATE
ORDER BY ... LIMIT 1
获取排序中所需的端口,而不是row_number()
......但实际上,整个方法注定要失败。它正在锤击存储,为UPDATE
进行行锁定和写入写入,并且每次都进行聚合传递。即使它可能全部都在shared_buffers
,但也存在大量的流失。在{30}左右使用UNLOGGED
表可能会有所帮助,并结合异步提交。一点点。它仍然只是很慢。
不要重新发明这个轮子。使用现有的,完善的负载平衡系统。你需要能够将统计数据保存在内存中,懒惰地更新,并使用诸如读取 - 复制 - 更新,CPU原子等技巧,以允许对共享数据结构进行非常高的并发访问。一些不会试图安全或持久的东西,并且不会尝试完全公平地分配。