Jump to content
php.lv forumi

Small ID reuse


Recommended Posts

Sveiki,

varbūt kāds ir saskāries (ganjau ir) un ir kāds know-how par sekojošu lietu:

 

Ir DB (MySQL) tabula ar IDentifikatoru (piem primary key autoinc) - kā jēdzīgāk būtu panākt pēc iespējas mazāku ID lietojumu?

Proti lietotājam jāoperē ar ieraksta ID taču ja ierakstu daudz tad standarta autoincrement vērtībā paliek par sešciparu-septiņciparu skaitli un ir neerti.

 

Teiksim ir ieraksti

1 | Babah

2 | Zazah

...

10000 | Tadah

 

Viena doma bija ka tajā brīdi kad lietotājs beidz darbības piemēram ar '2' objektu id tiek pacelts par kaut kādu lielu skaitli teiksim 2 + 10000000, taču tas neko būtisku nedod jo MySQLs pats neprot rejuzot pa vidu esošās tukšās vērtības bet gan tikai no beigām (MAX(id)+1)..

 

Bija doma izmantot papildus tabulu kurā būtu čupa ar mazajiem ID (teiksim 1 - 10000) kuriem temporāri tiek piesaistīti lielās tabulas identifikatori - tas ir no mazās tabulas tiek atselectēts MIN(id) WHERE lielais_id = '', tad iesetojam konkrētjam ID saikni ar lielo id un līdz ko lietotājs darbības beidz atbrīvojam..

 

Taču rodas problēma ar konkurenci proti līdzko uz mazo tabulu tiek pieprasīta čupa ar MIN(id) loģiski ka sanāk ka daļai no lielās tabulas ID tiek piesaistīts viens mazās tabulas ID, kas nav pareizi.

Lockojot tabulas pie SELECTa sanāk ziepes ar parāk daudz pieprasījumiem rindā..

 

So varbūt ir kāds hints?

Link to post
Share on other sites
Proti lietotājam jāoperē ar ieraksta ID taču ja ierakstu daudz tad standarta autoincrement vērtībā paliek par sešciparu-septiņciparu skaitli un ir neerti.

Kas tieši ir neērti?

Link to post
Share on other sites

ja useri raksta tos id ar roku un problēma ir ka tas skaitlis ir garš(..tā sapratu, sorry ja pārpratu :) ), tad varbūt, lai viņi raksta id nevis decimālajā skaitīšanas sistēmā, bet kaut kādā citā, piem ar bāzi 36. gan jau ka pārēķināt skaitli no vienas skaitīšanas sistēmas uz citu nebūs īpaši smagi serverim.

Link to post
Share on other sites

Ja godīgi nesapratu jautājumu. Lietotājs operē ar ID?! wtb. Sapratu.

A kāpēc burti neērti? Imho, HEX-ā ir OK. (7 simb -> 5 simb)

 

Ja pareizi sapratu domu, tad mana ideja šāda.

 

...tāpat kā tavējā, tikai atšķirība ir tāda, ka tu kopē `izlietoto ierakstu/ID` uz citu tabulu - steka princips.

{LIELA_TABULA} = {MAZA_TABULA} (Postgre vismaz var uztaisīt inheritance)

 

Pie updeita (lietotājs "izmanto" numuru)

Kopējam no MAZĀ uz LIELĀ (lielā jau būs savs cits lielais ID, atsauce uz originālo ID arī var palikt, bet tā nebūs unikāla)

Nomainam kolonnas FREE vērtību uz 1 iekš MAZĀ

Veidojam query `select min(ID) where FREE = 1` no MAZĀS

IF (no_row) insert new_ID else ID=NULL (t.b. auto) (iekš MAZĀS)

 

Cerams, ka saprati

 

PS: tikai jāuzmanās ar to min(ID)... Liekas, ka būs jālocko tabula pirms selekta un atlokot pēc updeita... Kaut gan tie it kā būs 2 SQL pēc kārtas.. bet ni pie liela lietotāju skaita var notikt jebkas

Edited by Delfins
Link to post
Share on other sites
A kāpēc burti neērti? Imho, HEX-ā ir OK. (7 simb -> 5 simb)

Tas ir mobils pakalpojums līdz ar to switchoties no cipariem uz burtiem ir ķeska :)

 

Es te skatos kaut ko par SELECT * ... FOR UPDATE;

 

Ja tas nolockos MIN(id) ierakstu un citai konekcijai atgriezīs nākošo min(id) būs ideāli..

Link to post
Share on other sites

Roze, pārbaudiju savu variantu, strādā tā kā iecerēts (protams man nav nekādas pārbaudas, bet tas jau sīkums)

<?php

include 'dbconn.php';

$action = @ trim($_GET['action']);
$new_id = 0;

switch ($action)
{
case 'release' :
		$id = @ (int) $_GET['id'];
		if ($id)
		{
			$sql = 'insert into big_table(refid) values ('.$id.')';
			$res = mysql_query( $sql, $link );
			$sql = 'update small_table SET free=1 where id = '.$id;
			$res = mysql_query( $sql, $link );
		}
		break;
case 'reserve' :
		$sql = "select min(id) AS id from small_table where free = 1";
		$res = mysql_query( $sql, $link );
		$row = mysql_fetch_object($res);
		if ($row->id)
		{
			$sql = "update small_table SET free=0 where id = {$row->id}";
			mysql_query( $sql, $link );
			$new_id = $row->id;				
		} else {
			$sql = "insert into small_table(free) values (0)";
			mysql_query( $sql, $link );
			$new_id = mysql_insert_id( $link );				
		}
	break;
}

if ($action)
{
header('Location: index.php?');
}

?>

<a href="?action=reserve">Rezerveet ID</a>

<?

if ($new_id)
{
// Send SMS
print "<p>Tavs jaunais ID ir `{$new_id}`.</p>";
}

# Print `stack` table		
$sql = "select id, free from small_table";
$res = mysql_query( $sql, $link );
print '<hr />Stack table <table border="1">';
while ( $row = mysql_fetch_object($res) )
{
print '<tr><td>'.$row->id.'</td>';
print (!$row->free) ? '<td><a href="?action=release&id='.$row->id.'">Izmantot</a></td>' : '<td>(briivs)</td>';
print '</tr>';
}
print '</table>';


# Print `data` table		
$sql = "select id, refid from big_table";
$res = mysql_query( $sql, $link );
print '<hr />Data table <table border="1">';
while ( $row = mysql_fetch_object($res) )
{
print '<tr><td>'.$row->id.'</td><td>REF: '.$row->refid.'</td></tr>';
}
print '</table>';

?>

 

http://85.115.122.95/dbid/index.php?

Link to post
Share on other sites

Nja FOR UPDATE locko tikai uz update/delete ne SELECT (read).

 

Delfins tavā variantā ir problēma:

$sql = "select min(id) AS id from small_table where free = 1";

 

Kas notiek ja ienāk divas konekcijas reizē?

 

Abas izpilda konkrēto selectu .. dabū vienādu 'id' un sākas šaize, jo $sql = "update small_table SET free=0 where id = {$row->id}"; izpildās vēlāk..

 

Proti starp pirmo selectu un updeito pa starpu (kas nomaina small_table konkrētā ID statusu) var notikt jau N identiski selecti kuriem tiks atgriezts identisks "mazais" id ..

 

Vienkāršāk sakot ir problēma jo nav (vai es kaut kā nevaru atrast) row-level READ locks (es gan pieņemu varētu būt baigais overheds no servera puses ko tādu implementēt).

 

Ideāli būtu:

Konekcija1:

SELECT min(id) From table lock read;

id | 1 |

 

 

Konekcija2:

SELECT min(id) From table lock read;

id | 2 |

 

...

Link to post
Share on other sites
Proti lietotājam jāoperē ar ieraksta ID taču

??

Kaapeec lietotaajam vispaar buutu jaaredz ID?

Manupraat tas nav isti korekti ...

skjiet tomeer ka ID ir SQL "iekseejai lietoshanai" ???

Nevar to visu apiet un katram Lietotaajam pieskjirt Unikaalu vertiibu (kautvai skaitlis...)

un no taa izejot veikt pieprasijumus....

(un tad jau po ka ID ir milziigs....)

Godiigi sakot nevaru iedomaaties kur buutu nepiecieshams ar roku vadiit ieksaa ID ....

(Piedevaam kursh ir tikai pagaidu???)

---------------

Link to post
Share on other sites

nevar kautkā tā?

update small_table set free = 0, @id = id where id = ( select min(id) AS id from small_table where free = 1 );

select @id as id;

es nez tik vai tie updeiti nevar notikties vairāki reizē...

Link to post
Share on other sites
Kaapeec lietotaajam vispaar buutu jaaredz ID?

Manupraat tas nav isti korekti ...

skjiet tomeer ka ID ir SQL "iekseejai lietoshanai" ???

Nevar to visu apiet un katram Lietotaajam pieskjirt Unikaalu vertiibu (kautvai skaitlis...)

un no taa izejot veikt pieprasijumus....

(un tad jau po ka ID ir milziigs....)

Godiigi sakot nevaru iedomaaties kur buutu nepiecieshams ar roku vadiit ieksaa ID ....

(Piedevaam kursh ir tikai pagaidu???)

---------------

šoreiz ID nav jāsaprot obligāti kā tabulas relācijas lauks bet gan vienkārši unikāls keywords ar ko operē lietotājs.

Nu minēšu piemēru - apmaksa ar SMS .. tev patīk rakstīt sms ar 8 ciparu skaitļiem un nedod dies nenokļudīties?

 

update small_table set free = 0, @id = id where id = ( select min(id) AS id from small_table where free = 1 );

select @id as id;

Patestēsim.. bet ienāca šāda doma prāta. Ir gan zināms overheads bet varētu atrisināt konkurences un lockošanas problēmas, proti daram šādi.

 

1. Iegūstam lielais_id = mysql_insert_id();

2. UPDATE small_table SET bigid = 'lielais_id', free = 0 WHERE free = 1 LIMIT 1 (Sheit LIMIT 1 garantee ka tiks updeitots tikai viens ieraksts.. )

3. Un tad SELECT id FROM small_table WHERE bigid = 'lielais_id'

 

Otrajā punktā var vēl checkot ja affected rows ir 0 tad var taisīt INSERT (nu teiksim small_table ir izbeigušies visi mazie ID cipari).

Link to post
Share on other sites

1.) Roze, to select + lock for update, sauc par isolation_level (es nezinu vai MYSQL tāds ir).

 

2.) neviens tev neliedz updeitot ar pārbaudi

function recursive_getid()
{
 $ID = select min(id) ...
sql = update small_table set FREE = 1 where ID = $ID AND free = 0
if (!$affected_rows)
{
	 return recursive_getid();
 }
 return $ID;
}

 

Līdzīgi strādā Deadlock mehānisms ... taisa RETRY(rekursīvā izpilde)

 

3.) Grey_Wolf, problēma nav tajā, kā dabūt ciparu, bet tur, ka jūzeram jāievada garš simbols... pieļauju ka šis te tiks izamntots draugiem.lv un lietotāju pieprasījumu skaits liels, attiecīgi `SMS: DARI_SITO 2423423123 ` ir ļoti neerts...

Link to post
Share on other sites
1.) Roze, to select + lock for update, sauc par isolation_level (es nezinu vai MYSQL tāds ir).
Jā ir..

 

2.) neviens tev neliedz updeitot ar pārbaudi

Ja protams taču caveats ir tas SELECT min(ID) ..

 

3.) Grey_Wolf, problēma nav tajā, kā dabūt ciparu, bet tur, ka jūzeram jāievada garš simbols... pieļauju ka šis te tiks izamntots draugiem.lv un lietotāju pieprasījumu skaits liels, attiecīgi `SMS: DARI_SITO 2423423123 ` ir ļoti neerts...

Ne obligāti tur ;) Vienkārši ir diezgan daudz analogu pakalpojumu / risinājumu..

 

Mīnus ir gan ka papildus jātaisa kaut kāds history logs jo lietotājs vairs neoperē ar objekta īstajiem identifikatoriem bet gan temporāri piešķirtiem..

Link to post
Share on other sites

Nu kā, big_table saturēs id, user_id, ref_id, trans_date, trans_time, + papildus dati, kas bijā small_table

 

nav uzlikta tilde, tāpēc nesaprotu, kas ir ... `caveats`...

Katrā ziņā tā nebūs resurs-rijīga lieta... Jo ID būs tik daudz, cik būs konkurento lietotāju. parasti tā jau ir "Saņem ID, un uzreiz izmanto" (nezinu tieši kam tev vajag tos ID).

 

Turklāt netiek veidota grupēšana, sortēšana... Gandrīz tas pats, kas pa `shared atmiņu` rakņāties :)

Link to post
Share on other sites

Neiedziļinājos koda rindās, bet, nogurušām acīm lasot, ienāca doma. Table1 (id normals_id_lauks, uq_id unikals_pagaidu_id, citi_lauki). Tad nu redz - id netiek aiztikts, bet uz izmantošanas brīdi piešķir uq_id = minimālais pieejamais brīvais uq_id, bet pēc izmantošanas, uq_id = NULL.

Ceru, ka domu uztvēri.

Link to post
Share on other sites
×
×
  • Create New...