technik: Volltext-Indizes von press_releases in eine eigene Suchtabelle auslagern
Ein FULLTEXT-Index zwingt MySQL bei JEDEM ADD COLUMN auf ALGORITHM=COPY. Auf press_releases (>1 GB) bedeutet das einen vollständigen Rebuild, während dessen kein Schreibzugriff möglich ist — im August hat genau das die Seite zum Stehen gebracht, weil die wartenden Requests PHP-FPM-Worker und DB-Verbindungen belegten. Die durchsuchbaren Felder liegen jetzt gespiegelt in `press_release_search`, die die Indizes trägt. Ihr Schema ist per Konstruktion stabil und wird nie erweitert; neue Felder gehören nach press_releases, wo sie ohne Volltext-Index Millisekunden kosten. Synchron hält sie ein Observer (alle Schreibzugriffe laufen über Eloquent, auch der Importer), reparieren lässt sie sich mit press-releases:reindex-search. Der Umstieg ist über mehrere Migrationen geteilt und schaltet erst um, wenn press_releases seine Indizes verloren hat — bis dahin sucht die Anwendung unverändert weiter. Die Drop-Migration verweigert den Dienst, solange der Suchindex unvollständig ist, und verwirft die gecachte Umschalt-Prüfung: Stünde dort noch der alte Stand, setzte der Rückfallpfad sein MATCH() auf einen soeben entfernten Index ab (MySQL-Fehler 1191) und jede Suche endete im 500er. Dazu die betriebliche Absicherung: - DB_LOCK_WAIT_TIMEOUT (Default 10 s) statt MySQLs einem Jahr. Requests scheitern bei einer laufenden Schemaänderung nach Sekunden, statt sich aufzustauen, bis keine Verbindung mehr frei ist. - Sessions und Cache nach Redis, damit eine blockierte MySQL-Tabelle nicht die gesamte Anwendung mitreißt. - db:online-alter als Notausgang (pt-online-schema-change), falls eine der kleinen FULLTEXT-Tabellen doch einmal groß wird. Percona-Toolkit ist bewusst nicht Teil des Standard-Setups. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9afbedddeb
commit
f8728c5bca
14 changed files with 1076 additions and 5 deletions
|
|
@ -2,6 +2,30 @@
|
|||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/*
|
||||
* Wartezeit auf Tabellen-Sperren (Metadata Lock).
|
||||
*
|
||||
* MySQL wartet per Default 31536000 Sekunden — ein Jahr. Läuft eine
|
||||
* Schemaänderung (auf press_releases/companies erzwingen die FULLTEXT-Indizes
|
||||
* einen vollständigen COPY-Rebuild), blockiert jeder Schreibzugriff so lange
|
||||
* und belegt dabei einen PHP-FPM-Worker samt DB-Verbindung. Sind die
|
||||
* Verbindungen aufgebraucht, steht die gesamte Anwendung — auch Sessions,
|
||||
* Cache und Queue, die hier ebenfalls in MySQL liegen.
|
||||
*
|
||||
* Requests scheitern deshalb nach wenigen Sekunden, statt sich aufzustauen.
|
||||
* Begrenzt wird nur das WARTEN auf die Sperre — eine bereits laufende
|
||||
* Migration wird davon nicht abgeschnitten.
|
||||
*
|
||||
* Bewusst ein einziger Wert aus der Umgebung statt einer Fallunterscheidung
|
||||
* über PHP_SAPI: `config:cache` friert den zur Cache-Zeit (CLI!) ermittelten
|
||||
* Wert ein, eine solche Weiche würde also im Web still den falschen Wert
|
||||
* liefern. Braucht ein Lauf mehr Geduld — etwa eine Migration auf einer
|
||||
* großen Tabelle —, wird er explizit hochgesetzt:
|
||||
*
|
||||
* DB_LOCK_WAIT_TIMEOUT=300 php artisan migrate --force
|
||||
*/
|
||||
$lockWaitTimeout = 'SET SESSION lock_wait_timeout = '.(int) env('DB_LOCK_WAIT_TIMEOUT', 10);
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|
|
@ -59,6 +83,7 @@ return [
|
|||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => $lockWaitTimeout,
|
||||
]) : [],
|
||||
],
|
||||
'mysql_presseecho' => [
|
||||
|
|
@ -78,6 +103,7 @@ return [
|
|||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => $lockWaitTimeout,
|
||||
]) : [],
|
||||
],
|
||||
'mysql_businessportal' => [
|
||||
|
|
@ -97,6 +123,7 @@ return [
|
|||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => $lockWaitTimeout,
|
||||
]) : [],
|
||||
],
|
||||
'mariadb' => [
|
||||
|
|
@ -116,6 +143,7 @@ return [
|
|||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => $lockWaitTimeout,
|
||||
]) : [],
|
||||
],
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue