Форум программистов, компьютерный форум, киберфорум
PostgreSQL
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/35: Рейтинг темы: голосов - 35, средняя оценка - 4.86
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314

Как можно увеличить скорость подсчета строк в таблице

19.06.2013, 00:52. Показов 6460. Ответов 15
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Доброй ночи.
Есть таблица на 9500000 строк как можно увеличить скорость подсчета строк в таблице если по одному параметру может выбрать 8999999 строк.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
19.06.2013, 00:52
Ответы с готовыми решениями:

Как увеличить скорость вычислений 500 000 строк?
Здравствуйте. Вопрос к людям с опытом. Как ускорить вычисления 500 000 строк. Задача посчитать, какую прибыль приносил магазин в среднем в...

Как увеличить скорость добавления строк на листе Excel
Всем доброго времени суток ! Программно добавить строки на листе Excel можно таким кодом: With Cells(36, 2) ...

Можно ли увеличить скорость ответа БД
Привет всем у меня возник такой вопрос как можно увеличить скорость ответа бд, иногда когда долго не запускаю старые программы(которые я...

15
1258 / 973 / 383
Регистрация: 02.09.2012
Сообщений: 3,001
19.06.2013, 16:57
построить план запроса (EXPLAIN)... от него уже плясать
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
19.06.2013, 21:33  [ТС]
Цитата Сообщение от grgdvo Посмотреть сообщение
построить план запроса (EXPLAIN)... от него уже плясать
не совсем понятно что он показывает, есть где то расшифровка?

Добавлено через 39 минут
Вот план, можно перевести

"Hash Join (cost=2.27..676918.63 rows=9020883 width=1166) (actual time=0.051..31650.458 rows=8999999 loops=1)"
" Hash Cond: (f.screen_version_id = sv.id)"
" -> Nested Loop (cost=1.11..552880.33 rows=9020883 width=654) (actual time=0.030..28141.769 rows=8999999 loops=1)"
" -> Seq Scan on tbl_release r (cost=0.00..1.25 rows=1 width=8) (actual time=0.007..0.009 rows=1 loops=1)"
" Filter: (id = 15)"
" Rows Removed by Filter: 19"
" -> Hash Join (cost=1.11..462670.25 rows=9020883 width=654) (actual time=0.018..26458.778 rows=8999999 loops=1)"
" Hash Cond: (f.type_film_id = tf.id)"
" -> Seq Scan on tbl_films f (cost=0.00..338632.00 rows=9020883 width=142) (actual time=0.005..22186.697 rows=8999999 loops=1)"
" Filter: (release_id = 15)"
" Rows Removed by Filter: 500002"
" -> Hash (cost=1.05..1.05 rows=5 width=520) (actual time=0.006..0.006 rows=5 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 1kB"
" -> Seq Scan on tbl_type_films tf (cost=0.00..1.05 rows=5 width=520) (actual time=0.002..0.003 rows=5 loops=1)"
" -> Hash (cost=1.07..1.07 rows=7 width=520) (actual time=0.014..0.014 rows=7 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 1kB"
" -> Seq Scan on tbl_screen_version sv (cost=0.00..1.07 rows=7 width=520) (actual time=0.007..0.009 rows=7 loops=1)"
"Total runtime: 32230.258 ms"
0
1258 / 973 / 383
Регистрация: 02.09.2012
Сообщений: 3,001
22.06.2013, 16:04
Не мог я сразу ответить, если еще актуально!

Отступы потеряли! Это важно для понимания структуры плана.

Простые рекомендации, если лень читать доки.
1. Обращайте внимание на "actual time" и стрелочками помечены операции, которые исполнитель запроса делает для получения результатов вашего запроса.
2. Seq Scan - последовательный поиск по таблице! ПЛОХО! Проверьте индексы по полю, которое ниже строчкой стоит под Filter. Индекс выгоднее использовать.
3. Не понятно, откуда взялся Nested Loop (вложенный цикл, то есть перебор записей). У вас что таблицы в запросе не связаны? Это ПЛОХО!

Остальное уже мало времени занимает, пока пропустим
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
23.06.2013, 14:35  [ТС]
Цитата Сообщение от grgdvo Посмотреть сообщение
. Не понятно, откуда взялся Nested Loop (вложенный цикл, то есть перебор записей). У вас что таблицы в запросе не связаны? Это ПЛОХО!
Почему это плохо?
0
1258 / 973 / 383
Регистрация: 02.09.2012
Сообщений: 3,001
23.06.2013, 17:49
Nested Loop: Базе приходится перелапачивать n*m итераций для объединения двух таблиц с n и m записями. Nested Loop хорошо для маленьких таблиц - быстрее выполняется, чем Hash Join, так как не нужно строить хэши для объединения, но для больших таблиц - это фактически гарантировано тормоза.
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
24.06.2013, 18:20  [ТС]
Вот новый, если можно про комментировать
"Aggregate (cost=342640.87..342640.88 rows=1 width=4) (actual time=407536.830..407536.830 rows=1 loops=1)"
" -> Bitmap Heap Scan on tbl_films f (cost=67247.68..335103.51 rows=3014946 width=4) (actual time=404609.514..407143.771 rows=2999999 loops=1)"

" Recheck Cond: (release_id = 7)"

" Rows Removed by Index Recheck: 119"

" -> Bitmap Index Scan on index_tbl_films_release_id (cost=0.00..66493.95 rows=3014946 width=0) (actual time=404607.541..404607.541 rows=3000000 loops=1)"

" Index Cond: (release_id = 7)"
"Total runtime: 407537.071 ms"
0
1258 / 973 / 383
Регистрация: 02.09.2012
Сообщений: 3,001
26.06.2013, 07:17
Bitmap Index имеет ограниченные условия применения (в основном для данных, которые очень редко меняются, ибо существенные накладные расходы на создание и поддержание целостности индекса)
См. например здесь и здесь

Попробуйти заменить на обычный b-tree индекс у соответствующих таблиц.
И покажите сам запрос. Не понимаю почему делается heap scan и index scan. Не могу представить, как это в запросе может выглядеть.
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
26.06.2013, 23:43  [ТС]
Oracle 11 SQL Скопировано
1
2
3
SELECT COUNT(f.id) AS _count  
FROM tbl_films f  
WHERE release_id = 7;
Oracle 11 SQL Скопировано
1
2
3
SELECT COUNT(f.id) AS _count  
FROM tbl_films f  
WHERE release_id = 7 AND screen_version_id=7;
Индексы такие создавал
Oracle 11 SQL Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE INDEX index_tbl_relations_film_id ON tbl_relations USING btree(film_id);
CREATE INDEX index_tbl_relations_genre_id ON tbl_relations USING btree(genre_id);
CREATE INDEX index_tbl_genres_id ON tbl_genres USING btree(id);
CREATE INDEX index_tbl_films_id ON tbl_films USING btree(id);
CREATE INDEX index_tbl_films_disk ON tbl_films USING btree(disk);
CREATE INDEX index_tbl_films_all_genres ON tbl_films USING btree(all_genres text_pattern_ops);
CREATE INDEX index_tbl_films_film_name ON tbl_films USING btree(film_name text_pattern_ops);
CREATE INDEX index_tbl_films_release_id ON tbl_films USING btree(release_id);
CREATE INDEX index_tbl_films_screen_version_id ON tbl_films USING btree(screen_version_id);
CREATE INDEX index_tbl_films_type_film_id ON tbl_films USING btree(type_film_id);
CREATE INDEX index_tbl_video_id ON tbl_video USING btree(id);
CREATE INDEX index_tbl_video_film_id ON tbl_video USING btree(film_id);
CREATE INDEX index_tbl_gallery_id ON tbl_gallery USING btree(id);
CREATE INDEX index_tbl_gallery_film_id ON tbl_gallery USING btree(film_id);
0
1258 / 973 / 383
Регистрация: 02.09.2012
Сообщений: 3,001
01.07.2013, 23:32
Был в отъезде... неудобно отвечать было... опять же, если еще актуально

1. Не знаю какая версия у вас postgres, но можете прочитать этот мануал
2. Сделать VACUUM tbl_film, как там написано + включить indexscanonly, если соотв. версия postgres.
3. Я провел эксперименты над таблицей в 9 млн. записей. Указанными выше способами довел выполнение запроса с 17000 ms (seq scan без индексов), до 0,170 ms (индексы, плюс кеширование уже помогало быстро выбирать результаты на запрос).

SQL Скопировано
1
SELECT COUNT(*) FROM tbl_film WHERE film_releaseid = 7;
Его план без индексов (здесь и далее уже прошла VACUUM'изация таблицы, поэтому так быстро, вообще изначально я получал результат больше 10 с, на "чистой" базе, когда только сгенерированы были данные в 9000000 строк запрос выполнялся 17 с)
Code Скопировано
1
2
3
4
5
"Aggregate  (cost=235677.39..235677.40 rows=1 width=0) (actual time=3387.151..3387.152 rows=1 loops=1)"
"  ->  Seq Scan on tbl_film  (cost=0.00..233215.00 rows=984956 width=0) (actual time=0.039..2439.000 rows=1001693 loops=1)"
"        Filter: (film_releaseid = 7)"
"        Rows Removed by Filter: 8004307"
"Total runtime: 3387.195 ms"
Создал индекс (b-tree)
Code Скопировано
1
2
3
4
5
6
7
"Aggregate  (cost=153852.99..153853.00 rows=1 width=0) (actual time=4076.851..4076.852 rows=1 loops=1)"
"  ->  Bitmap Heap Scan on tbl_film  (cost=18438.65..151390.60 rows=984956 width=0) (actual time=144.397..3146.587 rows=1001693 loops=1)"
"        Recheck Cond: (film_releaseid = 7)"
"        Rows Removed by Index Recheck: 7424552"
"        ->  Bitmap Index Scan on index_film_releaseid  (cost=0.00..18192.41 rows=984956 width=0) (actual time=142.885..142.885 rows=1001693 loops=1)"
"              Index Cond: (film_releaseid = 7)"
"Total runtime: 4077.050 ms"
Как видно будет еще хуже, потому что операция по индексу за два действия идет, вместо последовательного скана за одно.

Включаем, все что написано в 1 и 2
Code Скопировано
1
2
3
4
5
"Aggregate  (cost=31396.39..31396.40 rows=1 width=0) (actual time=2000.319..2000.320 rows=1 loops=1)"
"  ->  Index Only Scan using index_film_releaseid on tbl_film  (cost=0.00..28934.00 rows=984956 width=0) (actual time=0.056..1220.871 rows=1001693 loops=1)"
"        Index Cond: (film_releaseid = 7)"
"        Heap Fetches: 277"
"Total runtime: 2000.362 ms"
Проверяем сам запрос в конце:


Вообщем мораль, если нет частых (и массивных) изменений данных, то включить сканирование по индексу, вакуумизировать, также не создавать излишних (которые не участвуют в оптимизации запросов) индексов - это тоже отнимает время

Все результаты приведены для домашнего ноута (Core2Duo, 2,5Ghz, postgresql 9.2.4, поставлен в Gentoo из portage без каких-либо извратов)
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
09.07.2013, 00:35  [ТС]
Цитата Сообщение от grgdvo Посмотреть сообщение
1. Не знаю какая версия у вас postgres, но можете прочитать этот мануал
2. Сделать VACUUM tbl_film, как там написано + включить indexscanonly, если соотв. версия postgres.
3. Я провел эксперименты над таблицей в 9 млн. записей. Указанными выше способами довел выполнение запроса с 17000 ms (seq scan без индексов), до 0,170 ms (индексы, плюс кеширование уже помогало быстро выбирать результаты на запрос).
1. С английским проблема.
2. vacuum сделал, а где включить indexscanonly (версия 9.2.4.1).
3. Как лучше сделать индексы по каждому поисковому полю или один составной индекс? Как можно настроить кеширование запросов?
0
1258 / 973 / 383
Регистрация: 02.09.2012
Сообщений: 3,001
09.07.2013, 16:50
Цитата Сообщение от rave82 Посмотреть сообщение
1. С английским проблема.
Это проблема. Боюсь на русском такой информации еще не найти. Ну автопереводчики вам в помощь, хотя от них бывает мало толку. В разделе index-only scans (я бы перевел как "Поиск только с использование индексов") кратко написано
1. Сделать обычный индекс (без указания алгоритма) для таблицы по тем полям, которые участвуют в where
2. Сделать вакуумизацию
3. Включить "поиск только с использованием индексов"

Цитата Сообщение от rave82 Посмотреть сообщение
2. vacuum сделал, а где включить indexscanonly (версия 9.2.4.1).
Да вообще-то indexonlyscan уже должна быть включена по умолчанию (как пишут в документации). Но попробуйте через pgadmin или через psql дать команду SET enable_indexscanonly to ON. Это должно настроить планировщик выполнения запросов на использование соответствующего индекса.
Цитата Сообщение от rave82 Посмотреть сообщение
3. Как лучше сделать индексы по каждому поисковому полю или один составной индекс? Как можно настроить кеширование запросов?
Лучше создавать такие индексы, которые соответствуют выражению WHERE. То есть, если отбираем записи только по releaseid, то и индекс нужен только по этому полю, если по releaseid и screenid, то и индекс по этим двум полям. Опять же правило, чем больше индексов, тем больше времени тратиться на их перестроение при изменении данных (INSERT, DELETE, UPDATE). Обязательно сверяйте использование индексов с планом. Если видите, что планировщик выполнения запросов построил какой-то неверный план выполнения, значит где-то ошибка.

Кэширование как-таковое уже включено. То есть если дважды выполнить один и тот же запрос, то результаты для второго запроса результаты появятся гораздо быстрее. Для многопользовательского режима это может быть актуально. Если пользователи часто запрашивают одни и те же данные, не нужно по многу раз перевыполнять один и тотже запрос. Резульаты возмуться из кеша, если данные не изменялись конечно. Упралвять кеширование можно через параметры управления сервером. Об этом написана целая глава 18 в документации. На русском даже не знаю, есть или нет. Спрашивайте, если потребуется, попробуем разобраться.
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
09.07.2013, 18:32  [ТС]
Цитата Сообщение от grgdvo Посмотреть сообщение
Лучше создавать такие индексы, которые соответствуют выражению WHERE. То есть, если отбираем записи только по releaseid, то и индекс нужен только по этому полю, если по releaseid и screenid, то и индекс по этим двум полям.
то есть если у меня например пять поисковых полей то мне нужно создать на каждое поле индекс, а затем перебирать возможные комбинации фильтра? А если требуется возможность динамического добавления и удаления фильтра?
0
1258 / 973 / 383
Регистрация: 02.09.2012
Сообщений: 3,001
09.07.2013, 23:29
С динамическим фильтром может быть проблема: или гибкость или скорость. Обычно одно другое взаимоисключает. Но лучше всего протестировать! Если производительность устроит, значит индексы оставить как есть. Если нет, то тогда придется как-то менять модель данных, чтобы справиться с большим объемом данных и одновременно обеспечивать соотв. уровень гибкости фильтров при выборке.
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
10.07.2013, 14:33  [ТС]
Для хранения списка жанров к фильму есть таблица связи в ней есть поле film_id и genre_id. При выборке например по "году выпуска" и жанру "ужасы" можно как то склеить поле из таблицы tbl_films.id с таблицей tbl_relations.film_id чтоб быстро получить количество строк соответствующему условию. Сейчас выполняется за 4,5с., а если отдельно выполнить запрос для получения количества строк только по жанру то запрос выполняется за 0,4с.
0
1258 / 973 / 383
Регистрация: 02.09.2012
Сообщений: 3,001
01.08.2013, 18:33
Гм-гм... времени правда прошло три недели, но не мог я раньше ответить.

При оптимизации запросов пока (IMHO) лучше не придумали подхода, чем смотреть, что показывает план запроса - в нем мы видим, как СУБД выполняет запрос (как она "думает" и совпадает ли ее "думанье" с нашим).

Мне сложно точно воспроизвести ситуацию, потому что я не знаю ни вашей модели данных, ни ваших данных, но попробую показать на простом примере. У меня остались предыдущие данные (9 млн. записей), я к ним приделал таблицу genre. Вот так:

SQL Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE TABLE tbl_film
(
  film_id INTEGER NOT NULL,
  film_genre INTEGER NOT NULL,
  film_releaseid INTEGER,
  film_screenid INTEGER NOT NULL,
  film_name CHARACTER VARYING(128) NOT NULL,
  CONSTRAINT constrain_film_pk PRIMARY KEY (film_id),
  CONSTRAINT constrain_genre_fk FOREIGN KEY (film_genre)
      REFERENCES tbl_genre (genre_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);
 
CREATE TABLE tbl_genre
(
  genre_id INTEGER NOT NULL,
  genre_name CHARACTER VARYING(128),
  CONSTRAINT constraint_genre_pk PRIMARY KEY (genre_id)
);
Хочу выполнить запрос:

SQL Скопировано
1
2
3
SELECT COUNT(*)
FROM tbl_genre tg, tbl_film tf
WHERE tf.film_genre = tg.genre_id AND genre_id=8;
Посчитать количество фильмов ужасов (у меня это id=8 в tbl_genre).

Запускаю анализ плана:

Bash Скопировано
1
2
3
4
5
6
"Aggregate  (cost=238905.04..238905.05 rows=1 width=0)"
"  ->  Nested Loop  (cost=0.00..237767.28 rows=455103 width=0)"
"        ->  Seq Scan on tbl_genre tg  (cost=0.00..1.25 rows=1 width=4)"
"              Filter: (genre_id = 8)"
"        ->  Seq Scan on tbl_film tf  (cost=0.00..233215.00 rows=455103 width=4)"
"              Filter: (film_genre = 8)"
Получаю проблему. Два последовательных скана, цикл соединения (от него не избавиться - это соединение таблиц), ну и агрегат в виде подсчета количества. Смотрим на cost. Видим у tbl_film оценка времени просто сумасшедшая: 233 тыс. единиц. Вложенный цикл и агрегат добавляют какие-то слезы к общей оценке, поэтому на них мы уже не обращаем внимание. Также не обращаем внимание на таблицы жанров. Ибо там всего у меня 20 записей и дешевле обойтись без индекса вообще. Итак видно, что запрос будет тормозить.
Проверяем, что это так и есть:



Видим 1,4 с. Много!

Создаем индекс для условия genre_id=8

SQL Скопировано
1
2
3
4
CREATE INDEX index_film_genre
  ON tbl_film
  USING btree
  (film_genre);
Проверяем, что от создания индекса есть какой-то толк. Анализируем план запроса. Получаем.

Bash Скопировано
1
2
3
4
5
6
"Aggregate  (cost=19062.59..19062.60 rows=1 width=0)"
"  ->  Nested Loop  (cost=0.00..17924.83 rows=455103 width=0)"
"        ->  Seq Scan on tbl_genre tg  (cost=0.00..1.25 rows=1 width=4)"
"              Filter: (genre_id = 8)"
"        ->  Index Only Scan using index_film_genre on tbl_film tf  (cost=0.00..13372.55 rows=455103 width=4)"
"              Index Cond: (film_genre = 8)"
Видим, что сработал наш индекс! Также помним, что включали до этого Index Scan Only (для postgresql 9.2). Получили оценочное ускорение раз в 10. Проверяем во сколько же ускорились на самом деле.



Получилось где-то раз в 10 быстрее.

Надеюсь алгоритм действий понятен. Много зависит от организации модели данных и запросов, которые строятся по этой модели. Будут вопросы, присылайте.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
01.08.2013, 18:33
Помогаю со студенческими работами здесь

Можно ли увеличить скорость парсинга в HtmlAgilityPack?
Привет всем, я написал код для парсинга одного сайт, код рабочий, но срабатывает довольно долго(от 1-5 секунд). Hap.HtmlWeb webDoc1 =...

Максимальная скорость беспроводного соединения: 54 Мбит/с - можно ли увеличить ее?
Здравствуйте.У меня старый wi-fi роутер. У него Макс. скорость беспроводного соединения: 54 Мбит/с. При подключении по кабелю провайдер...

Можно ли увеличить скорость на мамке Shuttle AB52 USB1.1 до USB2.0?
Добрый вечер!Скажите пожалуйста можно ли как нибудь увеличить скорость на мамке Shuttle AB52 USB1.1 (как пишут в мануале) до USB2.0?С...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Новые блоги и статьи
Логирование в C# ASP.NET Core с помощью Serilog, ElasticSearch, Kibana
stackOverflow 25.04.2025
Помните те времена, когда для анализа проблемы приходилось подключаться к серверу, искать нужный лог-файл среди десятков других и вручную фильтровать тысячи строк в поисках ошибки? К счастью, эти дни. . .
Полностью асинхронный счётчик на логике (сумматорах) трёх состояний и асинхронных регистрах трёх состояний. Структура "электронный Buttom Up"
Hrethgir 25.04.2025
Программа для симуляции схемы - Logisim Evolution В общем какое-то время отвлёкся, так было надо, теперь когда запилю это на verilog и FPGA , досоставлю заявку в ФИПС на полезную модель - не готов. . .
Автоматизация Amazon Web Services (AWS) с Boto3 в Python
py-thonny 25.04.2025
Облачные вычисления стали неотъемлемой частью современной ИТ-инфраструктуры, а Amazon Web Services (AWS) занимает лидирующие позиции среди провайдеров облачных услуг. Управление многочисленными. . .
Apache Kafka vs RabbitMQ в микросервисной архитектуре
ArchitectMsa 25.04.2025
Современная разработка ПО всё чаще склоняется к микросервисной архитектуре — подходу, при котором приложение разбивается на множество небольших, автономных сервисов. В этой распределённой среде. . .
Параллельное программирование с OpenMP в C++
NullReferenced 24.04.2025
Параллельное программирование — подход к созданию программ, когда одна задача разбивается на несколько подзадач, которые могут выполняться одновременно. Оно стало необходимым навыком для. . .
Цепочки методов в C# с Fluent API
UnmanagedCoder 24.04.2025
Современное программирование — это не только решение функциональных задач, но и создание кода, который удобно поддерживать, расширять и читать. Цепочки методов и Fluent-синтаксис в C# стали мощным. . .
Мульти-тенантные БД с PostgreSQL Row Security
Codd 23.04.2025
Современные облачные сервисы и бизнес-приложения всё чаще обслуживают множество клиентов в рамках единой программной инфраструктуры. Эта архитектурная модель, известная как мульти-тенантность, стала. . .
Реализация конвейеров машинного обучения с Python и Scikit-learn
AI_Generated 23.04.2025
Мир данных вокруг нас растёт с каждым днём, и умение эффективно обрабатывать информацию стало необходимым навыком. Специалисты по машинному обучению ежедневно сталкиваются с задачами предобработки. . .
Контроллеры Kubernetes Ingress: Сравнительный анализ
Mr. Docker 23.04.2025
В Kubernetes управление входящим трафиком представляет собой одну из ключевых задач при построении масштабируемых и отказоустойчивых приложений. Ingress — это API-объект, который служит вратами. . .
Оптимизация кода Python с Cython и Numba
py-thonny 23.04.2025
Python прочно обосновался в топе языков программирования благодаря своей простоте и гибкости. Разработчики любят его за читабельность кода и богатую экосистему библиотек. Но у этой медали есть и. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru
Выделить код Копировать код Сохранить код Нормальный размер Увеличенный размер