النحات

بالأمس كنت جالسًا بهدوء ، كالعادة لا أزعج أحداً. هنا ، من جهتي اتصال مختلفتين ، يرسلان في وقت واحد تقريبًا رابطًا للتغريدة المعروفة حول JSON من SQL. بدت إحدى الرسائل كما يلي:





لقد كان بالفعل تحديًا مباشرًا. لا أستطيع تجاهله. لذلك قررت أن أحكي قصة لا تزال تثير مشاعر متناقضة في داخلي. بعد ثلاثة سنوات.



في ذلك الوقت المبارك ، كان الجميع يحلم بالعملات المشفرة ، وشارك في عمليات الطرح الأولي للعملات وعمليات تبادل العملات المشفرة. كان حقا شيء جديد. لدي خبرة في إنشاء أنظمة لإدارة الأصول الكلاسيكية (الأسهم والسندات وما إلى ذلك). كانت المشكلة أنها تشكلت حول أنظمة المحاسبة. أردت أن أدرك نفسي في إنشاء التبادل. ليس من المستغرب أن أغوص بسعادة في هذا المرجل المغلي.



هذه هي الخلفية. كان هناك الكثير من الأشياء المثيرة للاهتمام ، لكني اليوم أريد أن أخبركم عن حالة معينة - كيف أنشأنا المطابق.



المطابق ، هذا هو جوهر التبادل. في ذلك تحدث المعاملات. في الصورة النمطية الكلاسيكية ، هذا نظام فرعي عالي الأداء. لكن هذا صحيح بالنسبة للتبادلات الكبيرة. هناك ، التطبيقات المفتوحة للبيع والشراء بمئات الآلاف.



التبادل الذي تم إنشاؤه على ركبتيه لا يمكن أن يلحق بمثل هذا المرور. إنها مجبرة على البحث عن مكانة. إحدى الطرق هي إنشاء أدوات محددة تجذب بعض مستخدمي التبادلات الكبيرة. أغبى طريقة هي إغراق العمولات لإيداع وسحب العملات الرقمية.



في حالتنا ، كانت المحادثة تدور حول مئات المعاملات يوميًا.



كان لدينا أداة مطابقة في 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;


, .. . :



  1. depth_buy — ;
  2. depth_sell — ;
  3. transaction — = .


make_order_v2 :



  1. order_id — .
  2. order_type — . 21 — , 22 — .
  3. order_account — ( ).
  4. order_market — . .
  5. order_limit — . . , 100. .. 1.15btc 115.
  6. order_price — . .


, . — . , . .



, .



, . . 100 . 95 . 1.46 . 570 . .



, . . , . . .. .



, ? :



  • . .
  • , , . .


:



  • . , . .
  • . . .


بالطبع، أنا لا نشارك في التفاؤل من مؤلف الأصلي آخر أن كل شيء يمكن أن يتم من خلال نظم إدارة قواعد البيانات، ولكن أثبتت التجربة أن كل شيء يمكن أن يتم من خلال نظم إدارة قواعد البيانات. أذا أردت. لذلك ، عليك توخي الحذر بشأن رغباتك. خلاف ذلك ، سوف تضطر إلى تجربة الألم العقلي لسنوات.



الامور جيدة!



التغريدة الأصلية




All Articles