Ayer estuve sentado tranquilamente, como siempre no molesto a nadie. AquĂ, desde dos contactos diferentes, envĂan casi simultáneamente un enlace al conocido tweet sobre JSON de SQL. Uno de los mensajes se veĂa asĂ:
Ya era un desafĂo directo. No pude ignorarlo. AsĂ que decidĂ contar una historia que aĂşn me evoca sentimientos ambivalentes. Tres años despues.
En ese momento bendecido, todos soñaban con criptomonedas, participaban en ICO e intercambios criptográficos esculpidos. Realmente fue algo nuevo. TenĂa experiencia en la creaciĂłn de sistemas para la gestiĂłn de activos clásicos (acciones, bonos, etc.). El problema fue que se formĂł en torno a los sistemas contables. QuerĂa realizarme a mĂ mismo creando un intercambio. Como era de esperar, me sumergĂ felizmente en este caldero hirviendo.
Este es el trasfondo. Hubo muchas cosas interesantes, pero hoy quiero contarles sobre un caso especĂfico: cĂłmo creamos nuestro comparador.
Matcher, este es el nĂşcleo del intercambio. Es en Ă©l donde tienen lugar las transacciones. En el estereotipo clásico, este es un subsistema de alto rendimiento. Pero esto es cierto para los grandes intercambios. AllĂ, las aplicaciones abiertas para la compra y venta se cuentan por cientos de miles.
“ ” . . — , . — .
.
Java . , , . , . “” . .
. , , “” . . , 270 . RabbitMQ. ….
. . 2 . , , . , , .
. . .. , … , .. . - :
! ()
, . . … . .
, . , . , . — .
, . , 100, 5. 100 7 . , .
SQL . , . MySQL . , MySQL . .. . :
SET depth_sell.`limit` = depth_sell.`limit` - IF(
((@tofill := IF(
depth_sell.`limit` <= @limit,
depth_sell.`limit`,
@limit
))
+ (@limit := @limit - @tofill)),
@tofill,
@tofill
),
depth_sell.`executed` = @tofill
WHERE @limit > 0;
@limit := @limit - @tofill
() .
IN MEMORY . .. . .
. — . . , , .
CREATE TABLE transactions
(
id INT AUTO_INCREMENT PRIMARY KEY,
moment TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
side1 INT NOT NULL,
side2 INT NOT NULL,
price BIGINT NOT NULL,
volume BIGINT NOT NULL
);
CREATE TABLE depth_buy
(
id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
order_id BIGINT NOT NULL,
type INT DEFAULT '0' NOT NULL,
market INT DEFAULT '0' NOT NULL,
account INT NOT NULL,
price BIGINT DEFAULT '0' NOT NULL,
`limit` BIGINT DEFAULT '0' NOT NULL,
taker INT,
rev_price BIGINT NOT NULL,
executed BIGINT
) ENGINE = MEMORY;
CREATE TABLE depth_sell
(
id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
order_id INT NOT NULL,
type INT DEFAULT '0' NOT NULL,
market INT DEFAULT '0' NOT NULL,
account INT NOT NULL,
price BIGINT DEFAULT '0' NOT NULL,
`limit` BIGINT DEFAULT '0' NOT NULL,
taker INT,
rev_price BIGINT NOT NULL,
executed BIGINT
) ENGINE = MEMORY;
CREATE PROCEDURE `make_order_v2`(IN order_id INT,
IN order_type INT,
IN order_account INT,
IN order_market INT,
IN order_limit BIGINT,
IN order_price BIGINT)
BEGIN
START TRANSACTION;
SET @limit := order_limit;
IF order_type = 21 THEN
UPDATE depth_sell
INNER JOIN (
SELECT id
FROM depth_sell
WHERE market = order_market
AND depth_sell.price <= order_price
ORDER BY depth_sell.price + id ASC
) source ON depth_sell.id = source.id
SET depth_sell.taker = order_id,
depth_sell.`limit` = depth_sell.`limit` - IF(
((@tofill := IF(
depth_sell.`limit` <= @limit,
depth_sell.`limit`,
@limit
))
+ (@limit := @limit - @tofill)),
@tofill,
@tofill
),
depth_sell.`executed` = @tofill
WHERE @limit > 0;
INSERT INTO transactions (moment, side1, side2, price, volume)
SELECT now(), depth_sell.id, order_id, depth_sell.price, depth_sell.executed
FROM depth_sell
WHERE depth_sell.`taker` = order_id;
DELETE
FROM depth_sell
WHERE market = order_market
AND depth_sell.`limit` = 0;
IF @limit > 0 THEN
INSERT INTO depth_buy (order_id, type, market, account, price, rev_price, `limit`)
VALUES (order_id, order_type, order_market, order_account, order_price, -order_price, @limit);
END IF;
ELSE
UPDATE depth_buy
INNER JOIN (
SELECT id
FROM depth_buy
WHERE market = order_market
AND depth_buy.price >= order_price
ORDER BY depth_buy.rev_price - id ASC
) source ON depth_buy.id = source.id
SET depth_buy.taker = order_id,
depth_buy.`limit` = depth_buy.`limit` - IF(
((@tofill := IF(
depth_buy.`limit` <= @limit,
depth_buy.`limit`,
@limit
))
+ (@limit := @limit - @tofill)),
@tofill,
@tofill
),
depth_buy.`executed` = @tofill
WHERE @limit > 0;
INSERT INTO transactions (moment, side1, side2, price, volume)
SELECT now(), depth_buy.id, order_id, depth_buy.price, depth_buy.executed
FROM depth_buy
WHERE depth_buy.`taker` = order_id;
DELETE
FROM depth_buy
WHERE market = order_market
AND depth_buy.`limit` = 0;
IF @limit > 0 THEN
INSERT INTO depth_sell (order_id, type, market, account, price, rev_price, `limit`)
VALUES (order_id, order_type, order_market, order_account, order_price, -order_price, @limit);
END IF;
END IF;
COMMIT;
END;
CREATE PROCEDURE do_load_matcher_v2(IN market INT)
BEGIN
DECLARE count INT DEFAULT 100000;
WHILE count > 0 DO
call make_order_v2(
count,
IF(count % 2 = 0, 21, 22),
1,
market,
FLOOR(1 + (RAND() * 1000)),
FLOOR(1 + (RAND() * 1000))
);
SET count = count - 1;
END WHILE;
END;
, .. . :
- depth_buy — ;
- depth_sell — ;
- transaction — = .
make_order_v2 :
- order_id — .
- order_type — . 21 — , 22 — .
- order_account — ( ).
- order_market — . .
- order_limit — . . , 100. .. 1.15btc 115.
- order_price — . .
, . — . , . .
, .
, . . 100 . 95 . 1.46 . 570 . .
, . . , . . .. .
, ? :
- . .
- , , . .
:
- . , . .
- . . .
Por supuesto, no comparto el optimismo del autor del post original de que todo se puede hacer a través de un DBMS, pero la experiencia demuestra que todo se puede hacer a través de un DBMS. Si quieres. Por lo tanto, debes tener cuidado con tus deseos. De lo contrario, tendrá que experimentar dolor mental durante años.
¡Todo bien!
Tweet original