Jump to content
php.lv forumi

random datu izvadīšana bez atkārtošanās


ziedinjsh

Recommended Posts

Sveiki, štukoju ā verētu izvadīt random datus no datubāzes bez atkārtošanās kamēr nav apskatīti visi rezūltāti. Laikam vajadzētu to darīt ar sesijām, katru parādīto rezūltātu ielikt sesijā
 

$answers = array();
	$awr = implode(',', $_SESSION['id']);
		if($result = $db->query("select * from answers where status='1' order by rand() limit 0,1")){
			$answer = array();
			if($result->num_rows > 0){
				while($data = $result->fetch_object()){
					$answer = $data;
					array_push($answers, $answer);
					if ( !isset( $_SESSION['id'] ) ) { 
						$_SESSION['id'] = array(); 
					} 
					$id = $data->aid; 
					$_SESSION['id'][$id] = $id;
				
				}
				echo json_encode($answers, JSON_UNESCAPED_UNICODE).'<br>';
				
			}else{
				echo 'Tukšs';
			}
		}

Kaut kā tā, bet nestrādā :D

Link to comment
Share on other sites

Леший, katru reizi... Emmm, tu taču saproti, ka PHP ir process-per-request? Tālāk, kāds labums tieši ir no tā, ka tu ielasi visus datus aplikācijā no DB un pārcel darbu no DB uz aplikāciju? Ņemot vērā overheed uz datu izlasi un padevi uz app. 

 

1. preoptimizācija ir way worse.

2. Ja tik ļoti gribi optimizēt, RAND() * MAX(ID)

Link to comment
Share on other sites

PHP ir process-per-request

Koa?

 

Tālāk, kāds labums tieši ir no tā, ka tu ielasi visus datus aplikācijā no DB un pārcel darbu no DB uz aplikāciju?

Atnest 10L spaini ar ūdeni ar vienu piegājienu, vai skraidīt ar 200ml glāzi.

 

1. preoptimizācija ir way worse.

Why? Un kurā vietā šeit ir preoptimizacija?

 

p. s. Iztēlojies, ka mysql serveris atrodās citā valstī, kā web serveris.

Link to comment
Share on other sites

Whatever, ja ir daudz datu, tad sadalam daļās ar sapratīgu limitu, sametam kešā un ar katru requestu velkam visu no cache.

Un limit nekādīgi neiespaido order by rand() ātrumam. Ja tabulā ir vairāki neindeksēti lauki, piemēram teksti utt, tad ātrums kritīsies jūtami.

Edited by Леший
Link to comment
Share on other sites

Koa?

 

Atnest 10L spaini ar ūdeni ar vienu piegājienu, vai skraidīt ar 200ml glāzi.

 

Why? Un kurā vietā šeit ir preoptimizacija?

 

p. s. Iztēlojies, ka mysql serveris atrodās citā valstī, kā web serveris.

 

Tu saproti, ka PHP skripti tiek izpildīti uz katru pieprasījumu no jauna? Tātad, katru reizi kad es atveru lapu tu man atnes nevis glāzi ūdens bet 10 litrus ūdens? Kāda velna pēc? Nu labi, nokešosi memcache vai vēl kaut kur tikai lai iegrūstu to tabulu pāris reizes atmiņā. Kas no tā mainīsies? Ātrāk tas toč nepaliks. Nepiemirsti, ka keši ir arī jāvalidē un jāatjauno, un ja vien tev pa ceļam nav disk backed patstāvīga memory db, kā piemēram couchbase, tad jēga no tā keša maza, jo visu tavu kešoto tabulu nāksies turēt vai nu apcu, vai nu memcached un abos gadījumos izmantojot proxy validator. Labi, tā pat nebūtu problēma, bet kāda velna pēc tad visu vēl vilkt katra pieprasījuma procesa atmiņā es toč nesaprotu? Tad jau labāk ievelc tai memcached vai kur nu, uzliec maping keys un pieglabā daudzumu zem citas atslēgas, tad mt_rand un saņem savu rand. Tikai kā tu atrisināsi problēmu ar svaru? Nu labi, pievienojam vēl vienu cache key lai to pieglabātu. Aiziet tavs memcached pie tēviem un protams tu pazaudē svaru visiem rakstiem vai arī raksti dīvainu rutīnu svaru pieglabāt db. Un pēc tam tu paskaties uz to monstru, ko esi radījis un lūdzies apustuļiem lai atpestī tevi no grēka ar svēto rm / -rf | yes

 

Īsāk sakot, tava optimizācija nav optimizācija. Figņa. 

 

3015062728_6b27f9a6ae.jpg

Link to comment
Share on other sites

Mainīsies tas, ka nebūs slodze uz bāzi. Konkrēti, izstrādājam pašlaik projektu, kur bāze, api un web ir 3 fiziski dažādi serveri. Dati tiek atlasīti ar vienu piegājienu un tiek glabāti api pusē zem konkrēta meklējuma tokena. Webs/citi projekti pēc tam taisa poll reqestus uz api. Šādu sistēmu horizontāli skeilot ir elementāri.

Bet jebkurš cits risinājums arī ir labāks par order by rand().

Link to comment
Share on other sites

Okei, mazināsies slodze uz bāzi, bet tai pat laikā palielināsies slodze uz web. Order by rand nav labākais risinājums, bet tas noteikti ir labāks par tevis ieteikto datu ielasi webā jo rodas overheed uz datu pārraidi, glabāšanu un sinhronizāciju jo svara parametrs šeit loģiski būtu bidirekcionāls. 

 

Tālāk, tavs keiss diez vai būs šeit vietā, jo OP'am ir konkrētas prasības - vajag atrādīt nejaušus rezultātus reallaikā kārtojot pēc svara un nejaušības. Pati prasība šeit ierobežo iespēju. Tavā gadījumā tu vari atļauties kešot kaut kādus rezultātus, šeit ir random read un update, kas nav gluži kešojams pasākums jau pēc definīcijas.

 

Tālāk, man loģika liek domāt, ka ziedinjsh neturēs tur 10m ierakstus un nebūs viņam 1000rpm. Tādējādi vienkāršākais risinājums derēs. Risinājums kas skeilotos konkrētajai problēmai ir agregators, kas saņem datus par skatījumiem, agreģē svaru un veido rādījumu rotācijas rindu (queue) no kuras ar katru pieprasījumu izvelk vienu rezultātu un paceļ nākamo rindā augstāk (FIFO, weighted queue, sauc kā gribi). 

 

Es pašlaik strādāju ar projektu, kur fizisko serveru skaits tuvojas pusotram tūkstotim. Pie tavas pieejas, random read no datubāzes būs mazākā no problēmām jo suddenly viss RAM būs aizsists ar memcached agreģētiem un replicētiem datiem un core switchi pakārsies šķūnī jo sinhronizācijas flūds būs diezgan nenormāls (atceries - real time read/update, un konsistenti). 

Edited by F3llony
Link to comment
Share on other sites

Tā, pirmkārt nekad neizmanto order by rand(). Ever.

Otrkārt, really? Katru reizi taisīt select?

Treškārt, pavisām vienkāršs risinājums:

$data = Table::getAll(); //šo rindu aizvieto ar savu kodu, kas nolasa visu ar vienu query un saliek ierakstus masīvā
shuffle($data);

 

Mhm, ļoti optimāls risinājums, ņemot vērā, ka viņa kodā es tur neredzu klasi Table utt., kas vispirms vēl jāimplementē, un tas viss tikai tāpēc, lai kverijā neliktu RAND(), kas viņa apjomiem pietieks nākamajiem 10000 gadiem.
Link to comment
Share on other sites

Jautājums/problēma ir gana interesants, taču grūti komentēt, ja nav zināmi visi apstākļi, jo pēc "random datus no datubāzes bez atkārtošanās kamēr nav apskatīti visi rezūltāti. Laikam vajadzētu to darīt ar sesijām, katru parādīto rezūltātu ielikt sesijā" nav skaidrs kāds ir datu apjoms 'answer' tabulā, vai "bez atkārtošanās" nozīmē katram lapas apmeklētājam individuāli vai arī vienkārši jebkurš ieraksts kam status ir 1 (var atkārtoties vienlaicīgi diviem un vairāk lietotājiem) utt.
 
 
Vispārīgam domu gājienam un idejām var palasīt piemēram http://jan.kneschke.de/projects/mysql/order-by-rand/ raksts gan pavecs (2007), bet, manuprāt, aizvien aktuāls gan no risinājuma, gan pašas problēmas viedokļa ..
 

 

Rly? Tādu gudrinieku dēļ pēc 1 - 2 gadiem saprotam ka 10L vairs nav 10L bet gan kārtīga cisterna un pārnesam tomēr visu uz db.

Pēc 1-2 gadiem (ja dati aug (nedod dies ar eksponenciālu ātrumu)), tad "cisterna" ir abos variantos - gan "kopējot" visus datus pa tīklu, gan arī pēc RAND() orderējot miljards ierakstus (lai atlasītu 1) .. atšķīrība ir tikai vai matus plēš DBA vai tīkla administrators (vai abi) :)
 

 

Ja tā answers tabula ir maza (izskatās pēc kaut kādas aptaujas) - nu, teiksim, līdz 1000 ierakstiem, tad patiesībā Леший, variants/komentārs par to, ka ātrāk būs selectēt visus datus un aplikācijā randomizēt ir diezgan pareizs, jo MySQLs nu gaužām slikti izpildās ar ORDER BY RAND(), proti, parasts izpildes scenārijs būs:
mysql> explain select * from table order by rand() limit 1;
+----+-------------+---------------------------+------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table                     | type | possible_keys | key  | key_len | ref  | rows | Extra                           |
+----+-------------+---------------------------+------+---------------+------+---------+------+------+---------------------------------+
|  1 | SIMPLE      | table                     | ALL  | NULL          | NULL | NULL    | NULL | 394  | Using temporary; Using filesort |
+----+-------------+---------------------------+------+---------------+------+---------+------+------+---------------------------------+
Un temporary ir "source of evil", protams, maza apjoma datiem MySQLs to tabulu var iebakstīt RAMā (tmp_table_size šķiet noklusēti 30Mb) un tad tas varbūt no lietotāja/programmētāja viedokļa nav īpaši manāms, bet, ja tas sāk pārsniegt, šo limitu, tad dati tiek "kopēti" uz temporāru diska tabulu un tad sākas dieva zīmes .. 
 
Par performances atšķirībām var izlasīt arī iepriekš minētajā rakstā - "As you can see the plain ORDER BY RAND() is already behind the optimized query at only 100 rows in the table." (pieņemu gan, ka, laikam ejot (uz jaunākām MySQL versijām), rezultāti varētu būt labāki arī ar visu tmp tabulu).
 
 
 

Attiecīgi F3llony minētais " RAND() * MAX(ID)" varētu būt tuvāka patiesībai, lai gan atsevišķos variantos anyway būs ir dažādi vipendroni, proti:

SELECT * FROM answers WHERE id = CEIL(RAND() * (SELECT MAX(id) FROM answers))

.. jēdzīgi strādā tikai tad, ja tabula ir monotoni augoša un nav nekādu pārtraukumu t.i. nav dzēstu ierakstu vai piemēram ierakstu, kas neatbilst vajadzīgajiem (šajā gadījuma tie kas status != 1).

 

 

To var itkā risināt šādi:

SELECT * FROM answers WHERE id >= RAND() * (SELECT MAX(id) FROM answers) AND status = 1 LIMIT 1;

taču šeit ar var gadīties ka, ja nejauši randomizētā id vērtība piemēram ~sakrīt ar maksimālo tabulas ID, taču tam ir kāds cits status kā 1, tad netiks atrasts neviens ieraksts:
 

Sanāk, ka būtu jaselectē arī tos ierakstus, kuri var būt id <= [mūsu rand] un tad no abiem ierakstiem jāņem 1:
SELECT * FROM (
               (SELECT * FROM answers WHERE id >= RAND() * (SELECT MAX(id) FROM answers) AND status = 1 LIMIT 1)
               UNION
               (SELECT * FROM answers WHERE id <= RAND() * (SELECT MAX(id) FROM answers) AND status = 1 LIMIT 1)
) AS t LIMIT 1;

var protams atmest ārējo SELECtu un fetchot tikai 1 rowu no aplikācijas, tāpat arī ja kādā veidā ir zināms MAX(id) random daļu aizstāt ar reālu vērtību no aplikācijas:

Ja ar php pseidokodu $id = random(1, 324234); iegūstam vērtību 1234, tad DB serverim kveriji izskatīsies apmēram šādi:

(SELECT * FROM answers WHERE id >= 1234 AND status = 1 LIMIT 1)
UNION
(SELECT * FROM answers WHERE id <= 1234 AND status = 1 LIMIT 1);

un MySQL pat varēs atsevišķos gadījumos izmantot savu Query cache, kas nav iespējams RAND() kverijiem.

 

 

Cik skatījos uz palielām tabulām (500m - 1bn ierakstu) izpildās 0.0x laikos .. ātrdarbībai protams būtu vēlams kombinēts indekss uz status+id ..

 

 

 

 

.. .. bik sanāca palags.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...