My SQL Indexieren

775726

Cadet 2nd Year
Registriert
Jan. 2009
Beiträge
25
Hallo Liebe Gemeinde,

ich habe eine mysql Datenbank die ich umindexiere in folgendes Schema.
1. Suchwort-Tabelle

| swid| Suchwort |

| 1 | Berlin |
| 2 | London |
| 3 | Paris |

2. Such-Index-Tabelle

| id | swid doc_id count |

| 1 | 3 23 3 |
| 2 | 1 17 1 |
| 3 | 2 12 1 |

3. Such-Zeitstempel Tab

| id| doc_id timestamp |

| 1 | 23 2009-01-22 |
| 2 | 17 2009-01-22 |
| 3 | 12 2009-01-22 |


Da das indexiere sehr lange dauert habe ich überlegt ob es Sinn macht die Tabellen auf verschiedene Datenbanken zu verteilen sodass die( read, insert ,update) Operation auf verschiedene Datenbanken ausgeführt werden und dadurch ein Performance Vorteil entsteht.
Oder macht das alles keinen Sinn weil der Verbindungsaufbau bei mehreren Datenbanken länger dauern würde als wenn alles auf einer Datenbank liegen würde?
 
Hi,

das Indexieren machst du doch nur einmal, also da würde ich eher empfehlen "Augen zu und durch", ausser es handelt sich um extrem große Datenbanken und damit extrem hohen Zeitaufwand! Von wie viel Zeit sprechen wir hier für das Indexieren?

VG,
Mad
 
Von Etwa 22 Sekunden pro Datensatz Tendenz steigend weil die Suchwort Tabelle immer größer wird.
Ach ja es sind rund 22 Millionen Datensätze und wachsend.
 
Hi,

moment mal: 22 Sekunden PRO Datensatz? Bei 22 Millionen Datensätzen? Hast du mal überschlagen wie lange das dauern würde? Und ob das wirklich sinnhaftig ist und sein kann? Dir ist klar, dass du dabei dann 484.000.000 Sekunden brauchst? Das sind 15 Jahre!

Wie wird denn da bitte vorgegangen, da läuft etwas gewaltig schief würde ich sagen!

VG,
Mad
 
Wenn die DB derart groß ist/werden soll, könntest du auch mal über ElasticSearch nachdenken.
 
Hallo,
indizieren macht doch nur bei großen Datenmengen Sinn! Den Vorteil des schnelleren Lesens erkaufst du dir mit langsameren Schreibzugriffen und dass du die Indizes pflegen muss, da diese im Laufe der Zeit defragmentieren können.

Zudem muss ein Index richtig angewandt werden. Nur weil ein Index auf einer Tabelle liegt, wird eine Suche nicht automatisch schneller. Eine Suche muss schon auf einer indizierte Spalte ausgeführt werden (gleiches auch für joins) die indiziert ist. Sonst ist die Wirkung gleich Null.

Greetz
hroessler
 
Ja so etwa:
367801 Datensatz 13:14:05
367802 Datensatz 13:14:22
367803 Datensatz 13:14:31
367804 Datensatz 13:14:53
367805 Datensatz 13:14:58
367807 Datensatz 13:14:07

Man sieht halt das die Datensätze unterschiedlich viele Worte beinhalten.
 
Hi,

nochmal: wie wird vorgegangen? Was genau meinst du mit "Datenbank die ich umindexiere in folgendes Schema"? Wie geht ihr vor?

VG,
Mad
 
Also mir ist glaub noch nich ganz klar, was das Problem ist.

Musst Du eine neue Datenbank mit diesem Schema befüllen, und das einzelne Inserten dauert zu lange, da es zu viele Datensätze sind?
-> dann musst Du fürs initiale Befüllen die Indizierung ausstellen, oder einen Bulk-Insert durchführen. Anschließend wird einmal indiziert, das sollte dann in einer halben Stunde oder so erledigt sein.

Aber auch so erscheint mir ein Insert von 22sec als viel zu lange. Ich habe hier auch eine Tabelle mit 68 mio Zeilen, und in die werden pro Sekunde ein paar Dutzend Einträge geschrieben, wobei ein Insert in 5-10ms oder so gegessen ist. Die Tabelle hat so gut wie jede Spalte indiziert und auch Foreign Keys.
Die Indexe für die Tabelle werden 1x pro Woche neu defragmentiert, das dauert ca. 15 Minuten.
 
Also was ich getan


Volltextsuche auf Basis eines Indizes mit allen Suchworten. Bei der vorherigen eingebauten MySQL Volltextsuche war das beim Erstellen der Tabelle mit dem Zusatz "fulltext" erledigt. Jetzt müssen wir uns da selber drum kümmern, gewinnen dadurch aber mehr Einfluss.

Nach diesem Prinzip arbeiten alle Suchmaschinen: Ein Crawler, Spider oder auch Bot (von Roboter) genannte Software hangelt sich von Link zu Link durchs Netz und sammelt alle Texte ein, die er findet und schreibt sie in eine Datenbank. Wir gehen davon aus, das wir schon eine Datenbank mit Inhalten haben und für diese bauen wir uns eine maßgeschneiderte Volltextsuche.
Volltextsuche mit drei Zusatz-Tabellen

+-------------------------+ +-----------------------+
| | | |
| v v |
+-----+----------+ +----+-------------------+ +---+------------------+
| swid| Suchwort | | id | swid doc_id count | | id| doc_id timestamp |
+-----+----------+ +----+-------------------+ +---+------------------+
| | | | | | | | |
| 1 | Berlin | | 1 | 3 23 3 | | 1 | 23 2009-01-22 |
| 2 | London | | 2 | 1 17 1 | | 2 | 17 2009-01-22 |
| 3 | Paris | | 3 | 2 12 1 | | 3 | 12 2009-01-22 |
| | | | | | | | |
+-----+----------+ +----+-------------------+ +---+------------------+
1. Suchwort-Tabelle 2. Such-Index-Tabelle 3. Such-Zeitstempel Tab.

Zu der vorhanden Artikel Tabelle brauchen wir drei zusätzliche Tabellen:

Suchwort-Tabelle (mit allen Suchworten wie Berlin, London, Paris – Stop-Worte wie "oder" kommen nicht in die Suchwort-Tabelle)
Such-Index-Tabelle (enthält die Verknüpfung zwischen den Suchworten und den Tabellen-Zeilen, in denen die Suchworte enthalten sind)
Such-Zeitstempel-Tabelle (wann wurde diese Dokument das letzte Mal indiziert)

Quelle:http://phpperformance.de/volltextsuche-in-datenbanken/


Dies habe ich nach programmiert aber ich vermute ich habe das Problem gefunden:

Momentan ist meine Suchwort-Tabelle und Such-Index-Tabelle in mysql type MySIAM wenn ich das umstelle auf InnoDB wird sich damit das Problem Lösen?
 
Hi,

also, um nochmal auf dein Problem zurück zu kommen: du machst das Ganze in PHP? Ist das richtig? Dann würde ich ganz ganz ganz massiv da dein Problem vermuten.

Was genau machst du in PHP und wie genau machst du es? Und was genau? Du erstellst diese drei Tabellen? Das ist alles? Oder was genau macht dein Code?

VG,
Mad
 
775726 schrieb:
Nach diesem Prinzip arbeiten alle Suchmaschinen: Ein Crawler, Spider oder auch Bot (von Roboter) genannte Software hangelt sich von Link zu Link durchs Netz und sammelt alle Texte ein, die er findet und schreibt sie in eine Datenbank. Wir gehen davon aus, das wir schon eine Datenbank mit Inhalten haben und für diese bauen wir uns eine maßgeschneiderte Volltextsuche.
Soweit so logisch, aber leider totaler Unsinn. Eine SQL Datenbank für Textsuchen zu verwenden ist einfach nur Quatsch. Die können so etwas einfahc nicht performant handlen, außerdem fehlen ihnen elementar wichtige Funktionen, die jede Suche haben sollte.

Was MySQL (und Co) z.B. nicht können, jeder User aber erwartet:
- Flexion (Beugung, z.B. von Verben)
- Singular/Plural
- Fehlertoleranz
- "meinten Sie vielleicht?"

Schmeiß weg, was du bisher hast. Schreib eine neue Lösung auf Basis einer ECHTEN Volltext-Suche. Sphinx, Lucene/SOLR,...

Momentan ist meine Suchwort-Tabelle und Such-Index-Tabelle in mysql type MySIAM wenn ich das umstelle auf InnoDB wird sich damit das Problem Lösen?
InnoDB ist schneller bei Inserts, da hier kein Table Lock sondern ein Row Lock durchgeführt wird. Aber tendenziell... nein. Dein Problem ist die Wahl der falschen Werkzeuge.
Außerdem kann InnoDB, soweit ich weiß, noch kein FULLTEXT INDEX.
 
Was ich gemacht habe

Mysql Datenbankübersicht:
Alle vom Typ MyISAM

Code:
CREATE TABLE IF NOT EXISTS `technology` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Title` char(250) CHARACTER SET utf8 NOT NULL,
  `URL` char(250) CHARACTER SET latin1 NOT NULL,
  `description` text CHARACTER SET utf8 NOT NULL,
  `extra` text CHARACTER SET latin1 NOT NULL,
  `date` datetime NOT NULL,
  `Points` bigint(20) NOT NULL,
  `userdescription` varchar(500) COLLATE utf8_unicode_ci NOT NULL,
  `htmlcode` longtext COLLATE utf8_unicode_ci NOT NULL,
  `IndexTimeTitle` timestamp NULL DEFAULT NULL,
  `IndexTimeHtmlCode` timestamp NULL DEFAULT NULL,
  `IndexTimeExtras` timestamp NULL DEFAULT NULL,
  `IndexTimeDescription` timestamp NULL DEFAULT NULL,
  `IndexTimeUserDescription` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1401016 


CREATE TABLE IF NOT EXISTS `search_word` (
  `pk` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `search_word` varchar(32) NOT NULL DEFAULT '',
  PRIMARY KEY (`pk`),
  UNIQUE KEY `un_sw_word` (`search_word`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=24469 ;


CREATE TABLE IF NOT EXISTS `crosstabelle_description` (
  `PK` int(11) NOT NULL AUTO_INCREMENT,
  `FKSearchWord` int(11) NOT NULL,
  `FKSource` int(11) NOT NULL,
  `sourceTable` varchar(200) NOT NULL,
  `postionInText` int(11) NOT NULL,
  `frequencyInText` int(11) DEFAULT '1',
  PRIMARY KEY (`PK`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=571710 ;

PHP Script welches sich selbst aufruft


PHP:
	<?php
include("/include/daten_inc.php");
include("/include/connect_inc.php");
@ini_set("memory_limit", '6500M');

$datensatz = is_numeric($_GET["datensatz"]) ? $_GET["datensatz"] : 0;
$max       = 139403;



$sourcedatabase = '**************';
$sourceTable    = 'technology';
$sourceColounm  = 'description';

$tagetdatabase = '***************';
$targetTable   = 'crosstabelle_description';


$select = "SELECT ID FROM `tablenames`  WHERE `TableName` = '$sourceTable'";

mysql_select_db($tagetdatabase, $con);

$result        = mysql_query($select, $con);
$idSourceTable = mysql_fetch_row($result);

if ($idSourceTable[0] == null) {
    mysql_select_db($tagetdatabase, $con);
    
    
    $insert = "insert into `" . $tagetdatabase . "`.`tablenames` SET `TableName` = '" . $sourceTable . "',`ID` = NULL";
    //echo $insert;
    mysql_query($insert);
    $select = "SELECT ID FROM `tablenames`  WHERE `TableName` = '$sourceTable'";
    
    mysql_select_db($tagetdatabase, $con);
    
    $result        = mysql_query($select, $con);
    $idSourceTable = mysql_fetch_row($result);
    echo "<h1>";
    echo $idSourceTable[0];
    echo "</h1>";
    
}




$wordIndextargetTable = 'search_word';
$locSearch[]          = "=Ã?=i";
$locSearch[]          = "=ä|�=i";
$locSearch[]          = "=ö|�=i";
$locSearch[]          = "=ü|�=i";
$locSearch[]          = "=á|à |â|�|�|�=i";
$locSearch[]          = "=ó|ò|ô|�|�|�=i";
$locSearch[]          = "=ú|ù|û|�|�|�=i";
$locSearch[]          = "=é|è|ê|�|�|�|ë=i";
$locSearch[]          = "=Ã*|ì|î|Ã?|Ã?|Ã?|ï=i";
$locSearch[]          = "=ñ=i";
$locSearch[]          = "=ç=i";
$locSearch[]          = "=([0-9/.,+-]*\s)=";
$locSearch[]          = "=([^A-Za-z])=";
$locSearch[]          = "= +=";

$locReplace[] = "ss";
$locReplace[] = "ae";
$locReplace[] = "oe";
$locReplace[] = "ue";
$locReplace[] = "a";
$locReplace[] = "o";
$locReplace[] = "u";
$locReplace[] = "e";
$locReplace[] = "i";
$locReplace[] = "n";
$locReplace[] = "c";
$locReplace[] = " ";
$locReplace[] = " ";
$locReplace[] = " ";




// in $inStopwords ist ein numerisch indiziertes Array mit allen Stoppworten

$stopwords["onestopword"][] = "about";
$stopwords["onestopword"][] = "above";

// ALLE STOP Wörter


$stopwords["onestopword"][] = "zwischst";


$locSearch[] = "=(\s[A-Za-z]{1,2})\s=";
$locSearch[] = "= " . implode(" | ", $stopwords['onestopword']) . " =i";
$locSearch[] = "= +=";

$locReplace[] = " ";
$locReplace[] = " ";
$locReplace[] = " ";


if ($datensatz < $max) {
    $datensatz++;
    $select = "SELECT ID," . $sourceColounm . " FROM `" . $sourcedatabase . "`.`" . $sourceTable . "` WHERE `ID` = " . $datensatz;
    //echo $select;
    
    mysql_select_db($tagetdatabase, $con);
    
    $result      = mysql_query($select, $con);
    $resultarray = mysql_fetch_array($result);
    //echo $resultarray[$sourceColounm];
    $ID          = $resultarray['ID'];
    //echo $ID;
    $arrayvalue  = $resultarray[$sourceColounm];
    //echo $testi;
    
    
    
    
    
    $outString = trim(strtolower(stripslashes(strip_tags($resultarray[$sourceColounm]))));
    $inString  = preg_replace($locSearch, $locReplace, $outString);
    
    $outString = " " . str_replace(" ", "  ", $inString) . " ";
    $outString = trim(preg_replace($locSearch, $locReplace, $outString));
    
    
    
    $inWords = preg_split("/[\s,]+/", $outString);
    //print_r($inWords);
    
    
    $locData = array();
    
    foreach ($inWords as $locKey => $locVal) {
        
        
        
        if (strlen($locVal) > 32) {
            $locVal = substr($locVal, 0, 32);
        }
        
        
        $select = "SELECT pk FROM `search_word`  WHERE `search_word` = '$locVal'";
        
        mysql_select_db($tagetdatabase, $con);
        
        $result = mysql_query($select, $con);
        $result = mysql_fetch_row($result);
        //echo"<hr>";
        //echo $result[0];
        //echo"<hr>";
        
        if ($result[0] == null) {
            
            
            
            
            //echo "Insert";
            //echo"<hr>";
            //echo "<br>";
            $insert = "insert into `" . $tagetdatabase . "`.`search_word` SET `search_word` = '" . $locVal . "',`PK` = NULL";
            //echo $insert;
            mysql_query($insert);
            $select = "SELECT pk FROM `search_word`  WHERE `search_word` = '$locVal'";
            
            mysql_select_db($tagetdatabase, $con);
            
            $result = mysql_query($select, $con);
            $result = mysql_fetch_row($result);
            //echo"<hr>";
            //echo $result[0];
            //echo"<hr>";
            
            $insert = "insert into `" . $tagetdatabase . "`.`" . $targetTable . "` SET `FKSource` = '" . $ID . "',`FKSearchWord` = '" . $result[0] . "',`sourceTable` = '" . $idSourceTable[0] . "',`pk` = NULL";
            //echo $insert;
            mysql_query($insert);
            
            
        } else {
            //echo"<hr>";
            //echo"warh drinne";
            $select = "SELECT pk FROM `search_word`  WHERE `search_word` = '$locVal'";
            
            mysql_select_db($tagetdatabase, $con);
            
            $result = mysql_query($select, $con);
            $pk2    = mysql_fetch_row($result);
            //echo $pk2[0];
            
            $select = "SELECT pk FROM `" . $targetTable . "`  WHERE `FKSearchWord` = '" . $pk2[0] . "' and `FKSource` = '" . $ID . "'";
            //echo $select;
            mysql_select_db($tagetdatabase, $con);
            
            $result = mysql_query($select, $con);
            $result = mysql_fetch_row($result);
            //echo $result[0];
            if ($result[0] == null) {
                //echo"Leeer";
                $insert = "insert into `" . $tagetdatabase . "`.`" . $targetTable . "` SET `FKSource` = '" . $ID . "',`FKSearchWord` = '" . $pk2[0] . "',`sourceTable` = '" . $idSourceTable[0] . "',`pk` = NULL";
                //echo $insert;
                mysql_query($insert);
                //echo"<hr>";
            } else {
                //echo"Drinne";
                $select = "SELECT frequencyInText FROM `" . $targetTable . "`  WHERE `FKSearchWord` = '" . $pk2[0] . "' and `FKSource` = '" . $ID . "'";
                //echo $select;
                mysql_select_db($tagetdatabase, $con);
                
                $result          = mysql_query($select, $con);
                $result          = mysql_fetch_row($result);
                //echo "<h1>";
                //echo $result[0];
                $frequencyInText = $result[0];
                //echo "</h1>";
                
                
                $update = "update `" . $targetTable . "`set `frequencyInText` ='" . $frequencyInText = $frequencyInText + 1 . "' WHERE `FKSearchWord` = '" . $pk2[0] . "' and `FKSource` = '" . $ID . "'";
                //echo     $update ;
                mysql_query($update);
                
                // hier account
                //echo"<hr>";
            }
              
            
            
            
        }
        
        
        
    }
    $timestamp = date("Y-m-d  H:i:s", time());
    $insert    = "UPDATE `" . $sourcedatabase . "`.`" . $sourceTable . "` SET `IndexTimeDescription` = '" . $timestamp . "' where ID =" . $ID;
    ////echo $insert;
    mysql_query($insert);
    
?>
         <script type="text/javascript">
            window.onload = function() {
            
              window.document.location.href = '<?php
    echo $_SERVER["PHP_SELF"];
?>?datensatz=<?php
    echo $datensatz;
?>';
            }
          </script>
  <?php
}
echo $datensatz;


?>
 
Hi,

hab es nur überflogen, aber ein foreach mit 'select' und 'insert' drin ist nie eine gute Idee!

VG,
Mad
 
Madman1209 schrieb:
Hi,

hab es nur überflogen, aber ein foreach mit 'select' und 'insert' drin ist nie eine gute Idee!

VG,
Mad
Richtig. Siehe meine Empfehlung mit den Bulk-Inserts.

@Daaron: das kommt dann auf die Ansprüche an. Deine Kritik bezieht sich auf die Funktionalität, er sucht allerdings eine Antwort auf die Performance.
Rudimentär kann man sowas zB auch mit Ähnlichkeitsfunktionen erreichen. MS SQL-Server kann sowas zB nativ, in MySQL gibts da auch was (zB http://stackoverflow.com/questions/5322917/how-to-compute-similarity-between-two-strings-in-mysql)
 
Zuletzt bearbeitet:
captmcneil schrieb:
@Daaron: das kommt dann auf die Ansprüche an. Deine Kritik bezieht sich auf die Funktionalität, er sucht allerdings eine Antwort auf die Performance.
Eine FT-Suchengine indiziert dir ne halbe Bibliothek in 10 Sekunden. Soviel zum Thema Performance.
 
Daaron schrieb:
Eine FT-Suchengine indiziert dir ne halbe Bibliothek in 10 Sekunden. Soviel zum Thema Performance.
Das mag sein. Jedoch kannst Du nicht davon ausgehen, dass soetwas aktuell eine Option ist.
Außerdem liegt offensichtlich ein Problem vor, das nicht durch die Technologie-Wahl bedingt wird.

Kurzum: er sagt quasi, mein Golf fährt nur noch 50 km/h auf der Autobahn, und Deine Antwort ist "dann kauf Dir halt nen Porsche". Das behebt zwar das Problem, ist aber keine Antwort auf die Frage.
 
bin dein script auch nur überflogen...es macht beim lesen schon einen sehr undurchdachten und unperformanten eindruck.

1] wieso kein pdo?
2] wieso andauern eine mysql_select_db wenn du doch scheinbar immer die gleiche benutzt?
3] prepared statements!! vor allem innerhalb von schleifen wird dir das extrem viel bringen
4] würde ein insert on duplicated key empfehlen [ist zwar nicht sonderlich performant, dafür sparst du dir deine abfrage ob so ein eintrag schon existiert

optimiere deinen code, besonders deine datenbank-zugriffe...am besten das ganze script komplett neu schreiben, unter den oben genannten aspekten.

erst danach solltest du an andere optimierungen angehen!

ps: wie sieht die performance auf wenn du die fertigen querys direkt an den db-server sendest, ohne den ganzen php kram? daten vom server wären auch interessant. vermute mal du kannst noch sehr viel rausholen indem du die config von diesem anpasst
 
Zuletzt bearbeitet:
captmcneil schrieb:
Kurzum: er sagt quasi, mein Golf fährt nur noch 50 km/h auf der Autobahn, und Deine Antwort ist "dann kauf Dir halt nen Porsche". Das behebt zwar das Problem, ist aber keine Antwort auf die Frage.
Nein, er sagt eher: Mein Golf, mit dem ich Möbel transportiere, fährt nur noch 50. Meine Antwort ist da tatsächlich: Nimm einen LKW, der trägt deine Möbel wenigstens.

An dem hier geschriebenen PHP-Code ist so viel falsch, dass es sich einfach nicht lohnen würde, hier zu reparieren. Mit einer FT-Engine neu schreiben ist nicht so viel aufwändiger als den Ramsch hier auszumisten.

Was am PHP-Code falsch ist...
- deutlich veraltete MySQL-Implementierung. mysql_... ist DEPRECATED (und nicht erst seit gestern). Der Mist fliegt bald aus PHP raus. Entweder nimmt an MySQLi oder man macht gleich Nägel mit Köpfen und setzt auf PDO, wie es sich gehört
- aus diesem Punkt folgt: Keine Prepared Statements... dabei würden die hier sehr viel bringen.
- haufenweise irgendwie vollkommen unnötige Datenbank-"Wechsel". Das kostet alles Zeit und bringt nichts.

PHP:
$insert = "insert into `" . $tagetdatabase . "`.`search_word` SET `search_word` = '" . $locVal . "',`PK` = NULL";
//echo $insert;
mysql_query($insert);
$select = "SELECT pk FROM `search_word`  WHERE `search_word` = '$locVal'";
Was soll das bitte vorstellen? Ich verstehe das INSERT-Statement, aber warum danach die ID des gerade eingefügten Wertes über einen SELECT auslesen? Das ist ein vollkommen unsinniger Request, der nur Zeit kostet, da er garantiert nicht im Query-Cache liegt. Das kann man mit PDO::lastInsertId() (& Co.) deutlich billiger haben.

PHP:
$result = mysql_query($select, $con);
$result = mysql_fetch_row($result);
Tja... kein Wunder, dass alle immer behaupten, PHP würde schlechten Code magisch anziehen. Ist $result jetzt ein Query-Result? Oder ist es nur die erste Zeile des gesamten Results als Array? Da sieht doch keine IDE mehr durch, geschweige denn ein Mensch.

Oder hier: if ($result[0] == null){}... Davor wird "SELECT pk FROM `search_word` WHERE `search_word` = '$locVal'" durchgeführt... und unabhängig, ob nun NULL oder nicht, wird derselbe Query nochmal ausgeführt. SChön, immerhin kann hier der DB-Cache arbeiten. Aber macht das Sinn? Nein.


Und das ist eben nur ein Teil dessen, was hier elementar falsch läuft. Da lohnt es sich nicht, überhaupt was zu reparieren. Da wird abgerissen und mit korrekten Werkzeugen neu angefangen.
 
Zurück
Oben