Warning: count(): Parameter must be an array or an object that implements Countable in /home/pasokime/domains/mysql.lt/public_html/forumas/include/parser.php on line 820

1 (edited by neworld 2010-11-25 11:48:58)

Topic: "freeing items" stabdo INSERT ir UPDATE [innoDB]

Sveiki,

kuris laikas naudoju innoDB, tačiau pastebėjau kad joje yra lėtos update ir insert užklausos. Paėmiau profilį:

mysql> show profile for query 4;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000083 |
| checking permissions | 0.000009 |
| Opening tables       | 0.000018 |
| System lock          | 0.000004 |
| Table lock           | 0.000005 |
| init                 | 0.000061 |
| Updating             | 0.000094 |
| end                  | 0.000008 |
| query end            | 0.000003 |
| freeing items        | 0.065511 | <<-------
| logging slow query   | 0.000011 |
| cleaning up          | 0.000004 |
+----------------------+----------+
12 rows in set (0.00 sec)

Pati lentelė:

CREATE TABLE `buildings` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tid` int(10) unsigned NOT NULL,
  `damage` tinyint(3) unsigned NOT NULL DEFAULT '100',
  `guest` int(11) NOT NULL DEFAULT '0',
  `cost` smallint(5) unsigned NOT NULL,
  `builded` int(10) unsigned NOT NULL,
  `on` tinyint(1) NOT NULL DEFAULT '1',
  `lastTime` int(10) unsigned NOT NULL,
  `x` int(11) NOT NULL,
  `y` int(11) NOT NULL,
  `size` tinyint(1) unsigned NOT NULL,
  `static` tinyint(1) NOT NULL DEFAULT '0',
  `use` tinyint(1) NOT NULL DEFAULT '1',
  `type` enum('P','K','A','L') NOT NULL,
  `pasitenkinimas` float unsigned NOT NULL DEFAULT '0',
  `connected` tinyint(1) NOT NULL DEFAULT '0',
  `hasWorkers` tinyint(1) NOT NULL DEFAULT '0',
  `queue` smallint(5) unsigned NOT NULL DEFAULT '0',
  `revenue` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=497 DEFAULT CHARSET=latin1

Ji susieta per foregin keys su:

CREATE TABLE `zones` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `x` int(11) NOT NULL,
  `y` int(11) NOT NULL,
  `block` int(10) unsigned NOT NULL,
  `pid` int(10) unsigned DEFAULT NULL,
  `building` int(10) unsigned DEFAULT NULL,
  `private` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `coo` (`block`,`x`,`y`) USING BTREE,
  KEY `pid` (`pid`),
  KEY `building` (`building`),
  KEY `block` (`block`),
  CONSTRAINT `buildingas` FOREIGN KEY (`building`) REFERENCES `buildings` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT `parkas` FOREIGN KEY (`pid`) REFERENCES `parks` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=10435 DEFAULT CHARSET=latin1

Bet kadangi tik per building.id, kuris niekada nesikeičia, tačiau pats įrašas gali būti ištrintas.

Visą reikalą stabdo tik tas free items. šitoje lentelėje yra ~1500 įrašų. Kai vykdomos update užklausos, matau kaip HDD lemputė mirkčioja. šita reiškmė nesikeičia išjungus arba įjungus kešą. Produkcinis serveris bus žymiai spartesnis (nes turės 4 SSD RAID masyvą) negu darbinis laptopas, tačiau turės atlaikyti bent 4 milijonus įrašų, ir ~20.000 update per sekundę vien tik šioje lentelėje.

Taigi gal galima kažką pamodifikuoti, kad optimizuoti šitą vietą. Turiu visišką laisvę keisti my.cnf nustatymus. Būtų gerai kad update užklausos būtų įvykdomos per trumpiau nei 1 ms (dabar yra ~65ms kurias užima vien tik tas freeing items);

Re: "freeing items" stabdo INSERT ir UPDATE [innoDB]

Pirmiausia kalbant apie MySQL ir InnoDB visokias problemas reikia minimum žinoti apie kokias versijas ir operacines sistemas kalbam.

Toliau, tai kad tau rodo, kad "freeing items" užima daug laiko, tai dar nereiškia, kad kažkoks resursų atpalaidavimas tiek ir užima (-; Visai gali būt, kad yra kažkas užrakinta (lock) ar vyksta kažkas kitko.

Taip pat iš tavo parašymo nelabai aišku kuri lentelė ir ta, kurią reikės updeitinti 20k/sek.? Taip pat idomu pamatyti ir pačia UPDATE užklausą, kuri stabdo.

Beje, 20k/sek, UPDATE'ų nėra mažai (-;

UPDATE'us dažniausiai stabdo tokie dalykai:
a) elemento suradimas ne pagal indeksą, kurį reikia updeitinti;
b) keičiamų indeksų kiekiai/dydžiai - kuo daugiau ir didesnių indeksų keičiama - tuo lėčiau;
c) jeigu yra ryšiai ir taišku jie yra tikrinami ir esant reikalui atliekami veiksmai ir susijusiose lentose, kas papildomai prisideda prie stabdymo (-;

UPDATE pagreitina - pavienių UPDATE apjungimas į krūvas - sutaupoma ant lock'inimo ir indeksų paieškos/atnaujinmo;

3 (edited by neworld 2010-11-25 12:42:49)

Re: "freeing items" stabdo INSERT ir UPDATE [innoDB]

šiuo metu sukasi ant ubuntu 10.10 ir mysql 5.1.49.

UPDATE vienintelės užklausos kuriose HDD lemputė sumirkčioja.

Užklausos pavyzdys:  UPDATE buildings SET `lastTime` = '1290676129' WHERE id = 363

Dar pastebėjau, kad užklausos laikas nepriklauso nuo to, ar eilutę yra užrakinama su SELECT .... FOR UPDATE ar ne

Bus updeitinama būtent buildings eilutė. Kadangi ji yra pagrindinė, o aplikacija yra žaidimas, tai užklausų kiekio mažinti nebegaliu.

Beje, googlėje šia tema klausimų pilna, bet niekas nepaaiškino ką tai reiškia, ir kaip tai būtų galima spręsti.

Daugiau dėl pačio update, tai:

a) visada updeitinama tik pagal id, kuris yra primary
b) vienitelis indeksas yra id, kuris nesikeičia niekada (nebent yra įterpiami arba ištrinami įrašai)
c) iš esmės ryšiai neturėtų stabdyti, nes vienintelis ryšis yra pagal tą patį stulpelį id, kuris niekuomet nesikeičia.

Pats galvojau apjungti, bet nesugalvoju kaip reikėtų neskausmingai tai realizuoti programiniame kode.

Dar vienas pastebėjimas. Jeigu freeing items metu procesorius miega tas 65ms, tada nieko blogo, nes tuo metu CPU resursai tenka kitiems procesams, bet kaip tai patikrinti?

Beje, gal įžvelgsi kokį nors itin blogą nustatymą:

mysql> show variables like "%inno%";
+-----------------------------------------+------------------------+
| Variable_name                           | Value                  |
+-----------------------------------------+------------------------+
| have_innodb                             | YES                    |
| ignore_builtin_innodb                   | OFF                    |
| innodb_adaptive_hash_index              | ON                     |
| innodb_additional_mem_pool_size         | 33554432               |
| innodb_autoextend_increment             | 8                      |
| innodb_autoinc_lock_mode                | 1                      |
| innodb_buffer_pool_size                 | 268435456              |
| innodb_checksums                        | ON                     |
| innodb_commit_concurrency               | 0                      |
| innodb_concurrency_tickets              | 500                    |
| innodb_data_file_path                   | ibdata1:10M:autoextend |
| innodb_data_home_dir                    |                        |
| innodb_doublewrite                      | ON                     |
| innodb_fast_shutdown                    | 1                      |
| innodb_file_io_threads                  | 4                      |
| innodb_file_per_table                   | OFF                    |
| innodb_flush_log_at_trx_commit          | 1                      |
| innodb_flush_method                     |                        |
| innodb_force_recovery                   | 0                      |
| innodb_lock_wait_timeout                | 50                     |
| innodb_locks_unsafe_for_binlog          | OFF                    |
| innodb_log_buffer_size                  | 1048576                |
| innodb_log_file_size                    | 5242880                |
| innodb_log_files_in_group               | 2                      |
| innodb_log_group_home_dir               | ./                     |
| innodb_max_dirty_pages_pct              | 90                     |
| innodb_max_purge_lag                    | 0                      |
| innodb_mirrored_log_groups              | 1                      |
| innodb_open_files                       | 300                    |
| innodb_rollback_on_timeout              | OFF                    |
| innodb_stats_on_metadata                | ON                     |
| innodb_support_xa                       | ON                     |
| innodb_sync_spin_loops                  | 20                     |
| innodb_table_locks                      | ON                     |
| innodb_thread_concurrency               | 8                      |
| innodb_thread_sleep_delay               | 10000                  |
| innodb_use_legacy_cardinality_algorithm | ON                     |
+-----------------------------------------+------------------------+
37 rows in set (0.00 sec)

Re: "freeing items" stabdo INSERT ir UPDATE [innoDB]

neworld wrote:

UPDATE vienintelės užklausos kuriose HDD lemputė sumirkčioja.

Sukelk lentą į RAM - nemirkčios (-;

Supranti, kad jeigu reikia kažką skaityti iš disko ir rašyti, tai ir mirkčioja tos lemputės. Jeigu per kažkokia užklausą tu nematai, kad mirkčioja lemputė, tai dar nieko nereiškia, gal ji užkešuota. Ir aplamai tie mirkčiojimai yra labai subjektyvus dalykus, apie kuriuos net nereikėtų kalbėt.

neworld wrote:

Užklausos pavyzdys:  UPDATE buildings SET `lastTime` = '1290676129' WHERE id = 363

Bus updeitinama būtent buildings eilutė. Kadangi ji yra pagrindinė, o aplikacija yra žaidimas, tai užklausų kiekio mažinti nebegaliu.

Jeigu yra šita tipinė užklausa, tai aš jos tą lastTime ir id tikriausiai bandyčiau iškelti į atskirą lentą, žiūrėčiau ar galima apsieiti be InnoDB ir pan. O gal atminty galimą ją laikyti. Ir tik kas kažkiek laiko surašyti į fizinę lentą.

Kaip jau rašiau 20k UPDATE/sek. yra daug. O dar InnoDB su visu tranzakcinių apvalkalu, tai turiu dideliu abejonių ar ant vieno kompo iš vis tai padarysi, jeigu kalba eina apie dideles lentas su multimilijoniais įrašų kiekiais ir susijusiom lentom - turi eksperimentuoti.

Aš pats su InnoDB dirbu labai mažai. Bet tavo vietoje pirmiausia žiūrėčiau ar man tikrai reikia tiek tikrų/fizinių updeitų - ar negaliu jų laikyti kažkur RAM'e (memcached ir pan.).

InnoDB turi pranašumą UPDATE atveju, kad yra rakinama tik tą eilutė, kuri yra UPDATE'inama, MyISAM atveju rakinama visa lenta. Bet tau pataričiau pasitikrint kokiame režime dirba MySQL'as ir ar ten tikrai rakinama tik ta eilutė. Gal yra galimybės atsisakyti to rakinimo - t.y. - ar tikrai programos/biznio logikoje reikalingas rakinimas.

Re: "freeing items" stabdo INSERT ir UPDATE [innoDB]

Užklausoje yra updeitinami įvairūs laukeliai, taigi nėra ko iškelti. Kur rakinimo nereikia, naudoju myisam lentutes. Tai tada teks galvoti kaip įgyvendinti APP <-> MEMORY -> innoDB struktūrą

Re: "freeing items" stabdo INSERT ir UPDATE [innoDB]

Reikia ieškotis medžiagos ir eksperimentuti. Gali būti nemažai skirtumu tarp to ką matysi ant lokalaus kompo ir ant serverio su skirtingu HW ir OS ir MySQL'u. (-;

http://www.mysqlperformanceblog.com/200 … on-basics/
http://www.mysqlperformanceblog.com/fil … zation.pdf
http://www.mysqlperformanceblog.com/200 … k-through/

Re: "freeing items" stabdo INSERT ir UPDATE [innoDB]

neworld wrote:

Užklausoje yra updeitinami įvairūs laukeliai, taigi nėra ko iškelti. Kur rakinimo nereikia, naudoju myisam lentutes. Tai tada teks galvoti kaip įgyvendinti APP <-> MEMORY -> innoDB struktūrą

Rakinimą aš turėjau omenyje INSERT/UPDATE metu (table/row lock). šiuo atveju turint daug UPDATE'u InnoDB yra netgi geriau negu MyISAM jeigu tuo metu reikia dar ir skaityti iš tos lentos. Bet reikia pasižiūrėt, kokių operacijų kokie kiekiai, ar galima kažkaip atskirti tas operacijas ir pan. Ar tikrai reikalinga tiek ir tokių atnaujinimų.