рдХреИрд╕реЗ рдЕрдЬрдЧрд░ рдореЗрдВ PostgreSQL рдФрд░ ClickHouse рд╕реЗ, рдмрд╣реБрдд рдЬрд▓реНрджреА рдФрд░ рддреБрд░рдВрдд рдЦрд╕реНрддрд╛ рдореЗрдВ

рдкрд╛рдпрдерди рдореЗрдВ рдмрдбрд╝реА рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рдкрд░рд┐рд╕рдВрдкрддреНрддрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рд▓рдВрдмреА рдХреАрдордд рдХреЗ рдЗрддрд┐рд╣рд╛рд╕ рдХреЛ рдЬрд▓реНрджреА рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдорд╛рдзрд╛рди рдХреА рддрд▓рд╛рд╢ рдореЗрдВ рдмрд╣реБрдд рд╕рд╛рд░реЗ рдордВрдбрд▓рд┐рдпреЛрдВ рдХреЛ рддреЛрдбрд╝ рджрд┐рдпрд╛ред рдореЗрд░реА рдпрд╣ рдЗрдЪреНрдЫрд╛ рднреА рдереА рдХрд┐ рдореИрдВ рдЦрд╕реНрддрд╛рд╣рд╛рд▓ рд╕рд░рдгрд┐рдпреЛрдВ рдореЗрдВ рдХреАрдорддреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ, рд▓реЗрдХрд┐рди рдкрд╛рдВрдбрд╛рдУрдВ рдореЗрдВ рддреБрд░рдВрдд рдмреЗрд╣рддрд░ рд╣реИред

рдорд╛рдереЗ рдХреЗ рд▓рд┐рдП рдорд╛рдирдХ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдиреЗ рдирд┐рд░рд╛рд╢рд╛рдЬрдирдХ рд░реВрдк рд╕реЗ рдХрд╛рдо рдХрд┐рдпрд╛, рдЬрд┐рд╕рд╕реЗ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЛ рдХреНрд╡реЗрд░реА рдХреЛ 30 рд╕реЗрдХрдВрдб рдпрд╛ рдЙрд╕рд╕реЗ рдЕрдзрд┐рдХ рдХреЗ рд▓рд┐рдП рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ред рдирд╣реАрдВ рдбрд╛рд▓рдирд╛ рдЪрд╛рд╣рддреЗ, рдореБрдЭреЗ рдХрдИ рд╕рдорд╛рдзрд╛рди рдорд┐рд▓реЗ рдЬрд┐рдиреНрд╣реЛрдВрдиреЗ рдореБрдЭреЗ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕рдВрддреБрд╖реНрдЯ рдХрд┐рдпрд╛ред

рдкреИрд░ рдкрд╛рдпрдерди рдХреА рд╡рд╕реНрддреБ рдкреНрд░рдХреГрддрд┐ рд╕реЗ рдмрд╛рд╣рд░ рдирд┐рдХрд▓рддреЗ рд╣реИрдВред рдЖрдЦрд┐рд░рдХрд╛рд░, рдкреВрд░реНрдгрд╛рдВрдХ рд╕рдВрдЦреНрдпрд╛рдПрдВ рднреА рд╡рд╕реНрддреБрдПрдВ рд╣реИрдВ, рдЬреЛ рдХрд╛рдо рдХреА рдЧрддрд┐ рдХреЛ рдмреЗрд╣рдж рдирдХрд╛рд░рд╛рддреНрдордХ рд░реВрдк рд╕реЗ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддреА рд╣реИрдВред рдореИрдВ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рднрд╛рд╖рд╛ рдХреЛ рдмрджрд▓рдирд╛ рдирд╣реАрдВ рдЪрд╛рд╣рддрд╛ рдерд╛ред

рдкрд╣рд▓рд╛ рд╕рдорд╛рдзрд╛рди PostgreSQL рджреНрд╡рд╛рд░рд╛ рдореВрд▓реНрдп рдЗрддрд┐рд╣рд╛рд╕ рдХрд╛ рдПрдХ рд╕рдореВрд╣рди рдерд╛, рдЬрд┐рд╕рдХреЗ рдХрд╛рд░рдг рдбреЗрдЯрд╛рдмреЗрд╕ рдХреА рдУрд░ рд╕реЗ рдкреНрд░рджрд░реНрд╢рди рдореЗрдВ рдирдЧрдгреНрдп рдЧрд┐рд░рд╛рд╡рдЯ рдЖрдИ, рд▓реЗрдХрд┐рди рдЗрд╕рдиреЗ рдХрд╛рд░реНрдп рдХреЛ рд▓рдЧрднрдЧ ~ 3 рдЧреБрдирд╛ рддреЗрдЬ рдХрд░ рджрд┐рдпрд╛ред рд╡рд┐рдзрд┐ рдПрдХ рдФрд░ рд▓реЗрдЦ рдореЗрдВ рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рд╡рд░реНрдгрд┐рдд рд╣реИред

рдЗрд╕рдХрд╛ рдкрд░рд┐рдгрд╛рдо рдпрд╣ рд╕рдордЭ рдореЗрдВ рдЖрдпрд╛ рдХрд┐ рдкрд╛рдпрдерди рдореЗрдВ рдЖрдкрдХреЛ рдХрд┐рд╕реА рднреА рддрд░рд╣ рдХрдо рд╕реЗ рдХрдо рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдореЗрдВ рдкреВрд░рд╛ рдбреЗрдЯрд╛ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдФрд░ рд╕реБрдиреНрди-рд╕рд░рдгрд┐рдпреЛрдВ рд╕реЗ рдпрд╛ рддреБрд░рдВрдд рдкрдВрдбреЛрдВ рджреНрд╡рд╛рд░рд╛ рдкрд╛рд░реНрд╕ рдХрд░реЗрдВред

рдЕрдВрддрд┐рдо рдкрд░рд┐рдгрд╛рдо:

рдЫрд╡рд┐

PostgreSQL рдХреЗ рд▓рд┐рдП рдорд╛рдереЗ рд╕рдорд╛рдзрд╛рди


рд╣рдо рдПрдХ sql рдХреНрд╡реЗрд░реА рдореЗрдВ рдбреЗрдЯрд╛ рдЧреНрд░реБрдкрд┐рдВрдЧ рдХрд░рддреЗ рд╣реИрдВред рдПрдХ рдЙрджрд╛рд╣рд░рдг:

SELECT string_agg(symbol::text, ',') AS symbol_list , string_agg(dt::text, ',') AS dt_list , string_agg(open::text, ',') AS open_list , string_agg(high::text, ',') AS high_list , string_agg(low::text, ',') AS low_list , string_agg("close"::text, ',') AS close_list , string_agg(volume::text, ',') AS volume_list , string_agg(adj::text, ',') AS adj_list FROM v_prices_fast WHERE symbol IN ('{symbols}') 

рдбреЗрдЯрд╛ рдкрд╛рд░реНрд╕ рдХрд░рдирд╛ рдЖрд╕рд╛рди рд╣реИ:

 { 'symbol': np.array(r[0].split(',')), # str 'dt': np.array(r[1].split(','), dtype='datetime64'), # str w/type 'open': np.fromstring(r[2], sep=','), # numbers # ... } 

~ 1.7 рдорд┐рд▓рд┐рдпрди рд▓рд╛рдЗрдиреЛрдВ рдкрд░ рдЙрддреНрдкрд╛рджрдХрддрд╛:

 %timeit get_prices_fast(is_adj=False) # 11.9s 

рддреИрдпрд╛рд░ рдкрд╛рдпрдерди рдкреИрдХреЗрдЬ


рдЕрдЬрдЧрд░ рдЕрдкрдиреЗ рд╕рдореБрджрд╛рдп рдХреЗ рд▓рд┐рдП рдЕрдЪреНрдЫрд╛ рд╣реИ, рдЬреЛ рд╕рдорд╛рди рдореБрджреНрджреЛрдВ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рддрд╛ рд╣реИред рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╣рдорд╛рд░реЗ рдЙрджреНрджреЗрд╢реНрдп рдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рд╣реИрдВ:

  • рдУрдбреЛ - рдбреЗрдЯрд╛ рдЯреНрд░рд╛рдВрд╕рдлрд░ рдХреА рдЧрддрд┐ рдХреЛ рдПрдХ рд╕реНрд░реЛрдд рд╕реЗ рджреВрд╕рд░реЗ рдореЗрдВ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдкрд╛рдпрдерди рдореЗрдВред рдпрд╣ SQLAlchemy рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ PostgreSQL рдХреЗ рд╕рд╛рде рдЗрдВрдЯрд░реИрдХреНрдЯ рдХрд░рддрд╛ рд╣реИред
  • warp_prism - PostgreSQL рдХреЗ рдбреЗрдЯрд╛ рдХреЛ рдкреБрдирдГ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреНрд╡рд╛рдВрдЯреЛрдкрд┐рдпрди рдкрд░рд┐рдпреЛрдЬрдирд╛ рджреНрд╡рд╛рд░рд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдиреЗ рд╡рд╛рд▓рд╛ C рдПрдХреНрд╕рдЯреЗрдВрд╢рдиред рдЖрдзрд╛рд░ рдУрдбреЛ рдХреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рд╣реИред

рджреЛрдиреЛрдВ рдкреИрдХреЗрдЬ CSV рдХреЛ рдбреЗрдЯрд╛ рдХреЙрдкреА рдХрд░рдиреЗ рдХреА PostgreSQL рдХреА рдХреНрд╖рдорддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ:

 COPY {query} TO :path WITH ( FORMAT CSV, HEADER :header, DELIMITER :delimiter, QUOTE :quotechar, NULL :na_value, ESCAPE :escapechar, ENCODING :encoding ) 

рдЖрдЙрдЯрдкреБрдЯ рдХреЛ рдкрдВрдбреЛрдВ рдореЗрдВ рд░рдЦрд╛ рдЧрдпрд╛ рд╣реИред DataFrame () рдпрд╛ numpy.ndarray ()ред

рдЪреВрдБрдХрд┐ warp_prism C рдореЗрдВ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдбреЗрдЯрд╛ рдкрд╛рд░реНрд╕ рдХрд░рдиреЗ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ рдЗрд╕рдХрд╛ рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд▓рд╛рдн рд╣реИред рд▓реЗрдХрд┐рди рдПрдХ рд╣реА рд╕рдордп рдореЗрдВ рдЗрд╕рдХрд╛ рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рджреЛрд╖ рд╣реИ - рдбреЗрдЯрд╛ рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЗ рд▓рд┐рдП рд╕реАрдорд┐рдд рд╕рдорд░реНрдердиред рдпрд╣реА рд╣реИ, рдпрд╣ int, float, date, рдФрд░ str рдХреЛ рдкрд╛рд░ рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╕рдВрдЦреНрдпрд╛рддреНрдордХ рдирд╣реАрдВред рдУрдбреЛ рдХреЗ рдкрд╛рд╕ рдЗрд╕ рддрд░рд╣ рдХрд╛ рдХреЛрдИ рдкреНрд░рддрд┐рдмрдВрдз рдирд╣реАрдВ рд╣реИред

рдЙрдкрдпреЛрдЧ рдХреЗ рд▓рд┐рдП, sqlalchemy рдкреИрдХреЗрдЬ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рддрд╛рд▓рд┐рдХрд╛ рд╕рдВрд░рдЪрдирд╛ рдФрд░ рдХреНрд╡реЗрд░реА рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ:

 tbl_prices = sa.Table( 'prices', metadata, sa.Column('symbol', sa.String(16)), sa.Column('dt', sa.Date), sa.Column('open', sa.FLOAT), sa.Column('high', sa.FLOAT), sa.Column('low', sa.FLOAT), sa.Column('close', sa.FLOAT), sa.Column('volume', sa.BIGINT), sa.Column('adj', sa.FLOAT), ) query = sa.select(tbl_prices.c).where( tbl_prices.c.symbol.in_(SYMBOLS) ).order_by('symbol', 'dt') 

рдЧрддрд┐ рдкрд░реАрдХреНрд╖рдг:

 %timeit odo(query, pd.DataFrame, bind=engine) # 13.8s %timeit warp_prism.to_dataframe(query, bind=engine) # 8.4s %timeit warp_prism.to_arrays(query, bind=engine) # 8.0s 

warp_prism.to_arrays () - рд╕рдВрдЦреНрдпрд╛рддреНрдордХ рд╕рд░рдгрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рдЕрдЬрдЧрд░ рд╢рдмреНрджрдХреЛрд╢ рддреИрдпрд╛рд░ рдХрд░рдирд╛ред

ClickHouse рдХреЗ рд╕рд╛рде рдХреНрдпрд╛ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ?


PostgreSQL рднрдВрдбрд╛рд░рдг рдЖрдХрд╛рд░ рдХреЗ рд▓рд┐рдП рднреВрдЦ рдХреЛ рдЫреЛрдбрд╝рдХрд░ рдФрд░ рдмрдбрд╝реА рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреЛ рдЫреЛрдбрд╝рдХрд░ рд╕рднреА рдХреЗ рд▓рд┐рдП рдЕрдЪреНрдЫрд╛ рд╣реИред ClickHouse рдЦреБрдж рдХреЛ рд╢рд╛рд░реНрдк рдХрд░рддрд╛ рд╣реИ, рд╕рдм рдХреБрдЫ рдХреЙрдореНрдкреИрдХреНрдЯ рд░реВрдк рд╕реЗ рд╕реНрдЯреЛрд░ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдмрд┐рдЬрд▓реА рдХреА рдЧрддрд┐ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, ClickHouse рдореЗрдВ ~ 5Gb рдХреЗ рдЖрдХрд╛рд░ рдХреЗ рд╕рд╛рде рдПрдХ PostgreSQL рддрд╛рд▓рд┐рдХрд╛ ~ 1Gb рдореЗрдВ рдлрд┐рдЯ рд╣реЛрддреА рд╣реИред рдХреАрдорддреЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП ClickHouse рдХрд╛ рдЙрдкрдпреЛрдЧ рдПрдХ рдЕрдиреНрдп рд▓реЗрдЦ рдореЗрдВ рд╡рд░реНрдгрд┐рдд рд╣реИред

рдореЗрд░реЗ рдЪрдЧрд░рд┐рди рдХреЛ, рдУрдбреЛ рдиреЗ рдорджрдж рдирд╣реАрдВ рдХреА, рд╣рд╛рд▓рд╛рдВрдХрд┐ sqlalchemy рдХреЗ рд▓рд┐рдП рдПрдХ рдХреНрд▓рд┐рдХрд╣рд╛рдЙрд╕ рдПрдХреНрд╕рдЯреЗрдВрд╢рди рд╣реИред рдХрдВрд╕реЛрд▓ рдореЗрдВ рдХреНрд▓рд┐рдХрд╣рд╛рдЙрд╕ рдХреА рдЧрддрд┐ рдХреА рд╕реНрдореГрддрд┐ рдиреЗ рдореБрдЭреЗ рдПрдХ рдЕрд▓рдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдбреЗрдЯрд╛рдмреЗрд╕ рддрдХ рдкрд╣реБрдВрдЪрдиреЗ рдХреЗ рд╡рд┐рдЪрд╛рд░ рдХреЗ рд▓рд┐рдП рдкреНрд░реЗрд░рд┐рдд рдХрд┐рдпрд╛ред рдореБрдЭреЗ рдкрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд▓рдВрдмреА рдФрд░ рд╕рдВрд╕рд╛рдзрди рдЦрдкрдд рд╣реИ, рд▓реЗрдХрд┐рди рдкрд░рд┐рдгрд╛рдо рдкреНрд░рд╢рдВрд╕рд╛ рд╕реЗ рдкрд░реЗ рдереЗред

 sql = 'SELECT days.symbol, days.date, days.open/10000, days.high/10000, days.low/10000, days.close/10000, days.volume FROM days ' \ 'WHERE days.symbol IN (\'{0}\') ORDER BY days.symbol, days.date;'.format("','".join(SYMBOLS)) cmd = 'clickhouse-client --query="{0}"'.format(sql) def ch_pandas(cmd): p = subprocess.Popen([cmd], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) return pd.io.parsers.read_csv(p.stdout, sep="\t", names=['symbol', 'date', 'open', 'high', 'low', 'close', 'volume']) 

рдкрд░рд┐рдгрд╛рдо:

 %timeit ch_pandas(cmd) # 1.6s 

рдХреНрд▓рд┐рдХрд╣рд╛рдЙрд╕ HTTP рдкреЛрд░реНрдЯ рдЕрдиреБрд░реЛрдз


рдЬрдм рдбреЗрдЯрд╛рдмреЗрд╕ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░рддрд╛ рд╣реИ, рддреЛ 8123 рдХреЛ рдкреЛрд░реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реАрдзреЗ рдПрдХреНрд╕реЗрд╕ рдХрд░рдиреЗ рдкрд░ рдкрд░рд┐рдгрд╛рдо рдереЛрдбрд╝рд╛ рдмрд┐рдЧрдбрд╝ рдЧрдпрд╛:

 import urllib %timeit pd.io.parsers.read_csv('http://localhost:8123/?{0}'.format(urllib.parse.urlencode({'query': sql})), sep="\t", names=['symbol', 'date', 'open', 'high', 'low', 'close', 'volume']) # 1.9s 

рд▓реЗрдХрд┐рди рдорд░рд╣рдо рдореЗрдВ рдПрдХ рдордХреНрдЦреА рдХреЗ рдмрд┐рдирд╛ рдирд╣реАрдВред

ClickHouse рдХреЗ рд╕рд╛рде рдорд░рд╣рдо рдореЗрдВ рдЙрдбрд╝рдирд╛


рдбреЗрдЯрд╛рдмреЗрд╕ рдмрдбрд╝реЗ рдирдореВрдиреЛрдВ рдкрд░ рдкреНрд░рднрд╛рд╡рд╢рд╛рд▓реА рдерд╛, рд▓реЗрдХрд┐рди рдЫреЛрдЯреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдкрд░ рдпрд╣ рдирд┐рд░рд╛рд╢рд╛рдЬрдирдХ рдерд╛ред ~ рдУрдбреЛ рд╕реЗ 20 рдЧреБрдирд╛ рдмрджрддрд░ред рд▓реЗрдХрд┐рди рдпрд╣ рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рдХрд┐рдЯ рдХреА рд▓рд╛рдЧрдд рд╣реИ рдЬреЛ рдПрдЪрдЯреАрдЯреАрдкреА рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдпрд╛ рдПрдХреНрд╕реЗрд╕ рдХреЗ рд▓реЙрдиреНрдЪ рдХреЗ рд╕рд╛рде рд╣реИред

рдкрд░рд┐рдгрд╛рдо:

рдЫрд╡рд┐

рдирд┐рд╖реНрдХрд░реНрд╖


рдЗрд╕ рд▓реЗрдЦ рдХреЗ рд╕рд╛рде, рдкрд╛рдпрдерди рдФрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рдмреАрдЪ рдмрд╛рддрдЪреАрдд рдХреЛ рддреЗрдЬ рдХрд░рдиреЗ рдХрд╛ рдкреАрдЫрд╛ рдЦрддреНрдо рд╣реЛ рдЧрдпрд╛ рд╣реИред рдорд╛рдирдХ рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЗ рд╕рд╛рде PostgreSQL рдФрд░ рдХреАрдорддреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рд╛рд░реНрд╡рднреМрдорд┐рдХ рдкрд╣реБрдВрдЪ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреЗ рд▓рд┐рдП, рдХреНрд╡рд╛рдВрдЯреЛрдкрд┐рдпрди рд╕реЗ warp_prism рдкреИрдХреЗрдЬ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рддрд░реАрдХрд╛ рд╣реИред рдпрджрд┐ рдЖрдкрдХреЛ рдмрдбрд╝реА рдорд╛рддреНрд░рд╛ рдореЗрдВ рдЗрддрд┐рд╣рд╛рд╕ рдФрд░ рдмрдбрд╝реА рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рд▓рд╛рдЗрдиреЛрдВ рдХреЗ рд▓рд┐рдП рдЕрдиреБрд░реЛрдзреЛрдВ рдХреА рдПрдХ рдЙрдЪреНрдЪ рдЖрд╡реГрддреНрддрд┐ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ рдХреНрд▓рд┐рдХрд╣рд╛рдЙрд╕ рдЖрджрд░реНрд╢ рд╣реИред

Source: https://habr.com/ru/post/hi416681/


All Articles