In breve, sarei d'accordo con il tuo CTO. Probabilmente hai ottenuto alcune prestazioni a spese della scalabilità (se quei termini sono confusi, chiarirò di seguito). Le mie due maggiori preoccupazioni sarebbero la manutenibilità e la mancanza di opzioni per ridimensionare orizzontalmente (supponendo che ne avrete bisogno).
Vicinanza ai dati: facciamo un passo indietro. Ci sono alcuni buoni motivi per inserire il codice in un DB. Direi che il più grande sarebbe la vicinanza ai dati - ad esempio, se ti aspetti che un calcolo restituisca una manciata di valori, ma queste sono aggregazioni di milioni di record, che inviano i milioni di record (su richiesta) la rete da aggregare altrove è estremamente dispendiosa e potrebbe uccidere facilmente il tuo sistema. Detto questo, è possibile ottenere questa vicinanza di dati in altri modi, essenzialmente utilizzando cache o DB di analisi in cui parte dell'aggregazione viene eseguita in anticipo.
Esecuzione del codice nel DB:Gli effetti secondari sulle prestazioni, come la "memorizzazione nella cache dei piani di esecuzione", sono più difficili da discutere. A volte, i piani di esecuzione memorizzati nella cache possono essere una cosa molto negativa, se il piano di esecuzione errato è stato memorizzato nella cache. A seconda del tuo RDBMS, potresti ottenere il massimo da questi, ma nella maggior parte dei casi non otterrai molto rispetto all'SQL parametrizzato (anche quei piani vengono generalmente memorizzati nella cache). Direi anche che la maggior parte dei linguaggi compilati o JIT in genere funzionano meglio dei loro equivalenti SQL (come T-SQL o PL / SQL) per operazioni di base e programmazione non relazionale (manipolazione di stringhe, loop, ecc.), Quindi non perderai nulla lì, se hai usato qualcosa come Java o C # per fare il crunching dei numeri. Anche l'ottimizzazione a grana fine è piuttosto difficile: sul DB, spesso bloccato con un B-tree generico (indice) come unica struttura di dati. Ad essere onesti, un'analisi completa, tra cui cose come avere transazioni a più lungo termine, escalation dei blocchi, ecc., Potrebbe riempire i libri.
Manutenibilità: SQL è un linguaggio meraviglioso per ciò per cui è stato progettato. Non sono sicuro che si adatti perfettamente alla logica dell'applicazione. La maggior parte degli strumenti e delle pratiche che rendono sopportabili le nostre vite (TDD, refactoring, ecc.) Sono difficili da applicare alla programmazione di database.
Prestazioni contro scalabilità:Per chiarire questi termini, intendo questo: le prestazioni sono la velocità con cui ti aspetteresti che una singola richiesta passi attraverso il tuo sistema (e torni all'utente), per il momento assumendo un carico ridotto. Questo sarà spesso limitato da cose come il numero di livelli fisici che attraversa, quanto sono ottimizzati quei livelli, ecc. La scalabilità è come le prestazioni cambiano con l'aumentare del numero di utenti / carico. Potresti avere prestazioni medio / basse (diciamo, 5 secondi + per una richiesta), ma una scalabilità eccezionale (in grado di supportare milioni di utenti). Nel tuo caso, probabilmente sperimenterai buone prestazioni, ma la tua scalabilità sarà limitata da quanto è grande un server che puoi costruire fisicamente. Ad un certo punto, colpirai quel limite e sarai costretto a ricorrere a cose come lo sharding, che potrebbe non essere fattibile a seconda della natura dell'applicazione.
Ottimizzazione precoce: in definitiva, penso che tu abbia fatto l'errore di ottimizzare prematuramente. Come altri hanno sottolineato, in realtà non hai misurazioni che mostrano come funzionerebbero gli altri approcci. Bene, non possiamo sempre costruire prototipi su larga scala per dimostrare o confutare una teoria ... Ma in generale, sarei sempre restio a scegliere un approccio che scambia la manutenibilità (probabilmente la qualità più importante di un'applicazione) per le prestazioni .
EDIT: su una nota positiva, il ridimensionamento verticale può allungare abbastanza lontano in alcuni casi. Per quanto ne so, SO è stato eseguito su un singolo server per un bel po 'di tempo. Non sono sicuro di come corrisponda ai tuoi 10.000 utenti (immagino che dipenderebbe dalla natura di ciò che stanno facendo nel tuo sistema), ma ti dà un'idea di cosa si può fare (in realtà, ci sono esempi più impressionanti, questo sembra essere un popolare che la gente può facilmente capire).
EDIT 2: Per chiarire e commentare alcune cose sollevate altrove:
- Ri: Consistenza atomica - La coerenza ACID potrebbe essere un requisito del sistema. Quanto sopra non discute davvero, e dovresti capire che la coerenza ACID non richiede di eseguire tutta la tua logica aziendale all'interno del DB. Spostando il codice che non ha bisogno di essere presente nel DB, lo si sta vincolando per essere eseguito nell'ambiente fisico del resto del DB - è in competizione per le stesse risorse hardware dell'effettiva porzione di gestione dei dati del proprio DB. Per quanto riguarda il ridimensionamento del codice solo su altri server DB (ma non sui dati effettivi), questo può essere possibile , ma cosa guadagni esattamente qui, a parte i costi di licenza aggiuntivi nella maggior parte dei casi? Tieni le cose che non hanno bisogno di essere sul DB, fuori dal DB.
- Ri: prestazioni SQL / C # - poiché questo sembra essere un argomento di interesse, aggiungiamo un po 'alla discussione. Puoi certamente eseguire il codice nativo / Java / C # all'interno dei DB, ma, per quanto ne so, non è quello che è stato discusso qui - stiamo confrontando l'implementazione del codice tipico dell'applicazione in qualcosa come T-SQL rispetto a qualcosa come C #. Esistono numerosi problemi che in passato sono stati difficili da risolvere con il codice relazionale, ad esempio si consideri il problema "accessi concomitanti massimi", in cui sono presenti record che indicano un accesso o una disconnessione e l'ora e è necessario capire quali era il numero massimo di utenti che hanno effettuato l'accesso contemporaneamente. La soluzione più semplice possibile è quella di scorrere i record e continuare a incrementare / decrementare un contatore quando si verificano accessi / disconnessioni e tenere traccia del massimo di questo valore.Maggio, Non lo so), il meglio che puoi fare è un CURSORE (le soluzioni puramente relazionali sono tutte su diversi ordini di complessità e il tentativo di risolverlo usando un ciclo while si traduce in prestazioni peggiori). In questo caso, sì, la soluzione C # è in realtà più veloce di quella che puoi ottenere in T-SQL, punto. Ciò può sembrare inverosimile, ma questo problema può manifestarsi facilmente nei sistemi finanziari, se si lavora con righe che rappresentano modifiche relative e è necessario calcolare aggregazioni con finestre su quelle. Anche le invocazioni di proc memorizzate tendono ad essere più costose: invocare un SP banale un milione di volte e vedere come si confronta con la chiamata a una funzione C #. Ho accennato ad alcuni altri esempi sopra - non ho ancora incontrato nessuno implementare una tabella hash corretta in T-SQL (uno che in realtà offre alcuni vantaggi), mentre è abbastanza facile da fare in C #. Ancora una volta, ci sono cose in cui i DB sono fantastici e cose in cui non sono così fantastici. Proprio come non vorrei fare JOIN, SUM e GROUP BY in C #, non voglio scrivere nulla di particolarmente intenso sulla CPU in T-SQL.