Archivi categoria: Articoli

Missing Index… Cache

Facebooktwittergoogle_plusredditlinkedin

Qualche settimana fa un collega mi ha sottoposto un quesito la cui risposta coinvolge alcuni concetti molto interessanti. La domanda più o meno era:

SQL Server mi dice che manca un indice, l’ho creato. Perchè eseguendo una query su sys.dm_db_missing_index_details trovo ancora l’indice come mancante? Come dico a SQL di aggiornare la DMV?

La risposta alla domanda è abbastanza semplice: una DMV non può essere aggiornata dall’utente. Unica componente con diritti di “scrittura” di questa particolare DMV è il Query Optimizer, che però va ad “aggiungere” informazioni. Se invece si vuole “svuotare” il contenuto della DMV.. beh.. non ci sono santi: serve riavviare l’istanza del server (come peraltro chiaramente riportato nella documentazione online)

E quindi? Come possiamo ricavare un’informazione in qualche modo equivalente?

A venirci in aiuto è il preziosissimo Query Plan, che grazie al lavoro del Query Optimizer può contenere anche alcune segnalazioni di warning, ovvero delle piccole anomalie che, se risolte, consentirebbero al Query Processor di eseguire la nostra query in maniera più efficiente e performante.

Così come già indicato da Sergio Govoni nell’articolo Conversioni implicite: La plan cache ci svela quante sono e dove avvengono!”  i tipi di warning che possono essere segnalati dal query optimizer sono:

  • No statistics (ColumnsWithNoStatistics)
  • No join predicate (NoJoinPredicate)
  • Implicit conversions (PlanAffectingConvert)
  • Sort on tempdb (SpillToTempDb)
  • No matched indexes (UnmatchedIndexes)
  • Wait (WaitWarningType)

“No matched indexes”? Proprio quello che ci serviva!!

Possiamo dunque pensare di ricavare le informazioni inerenti ad eventuali indici mancanti tramite una semplice query che vada ad esplorare lo stato attuale della plan cache!

Come? Con questa semplice query:

WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
   , cte AS (
        SELECT QP.query_plan
             , Q.N.value('(//MissingIndexGroup/@Impact)[1]', 'FLOAT') AS Impact
             , Q.N.value('(//MissingIndex/@Database)[1]', 'SYSNAME') AS DatabaseName
             , Q.N.value('(//MissingIndex/@Schema)[1]', 'SYSNAME') AS SchemaName
             , Q.N.value('(//MissingIndex/@Table)[1]', 'SYSNAME') AS TableName
             , (
                 SELECT DISTINCT R.C.value('@Name', 'SYSNAME') + ', '
                 FROM Q.N.nodes('//ColumnGroup') AS T(CG)
                 CROSS APPLY T.CG.nodes('Column') AS R(C)
                 WHERE T.CG.value('@Usage', 'VARCHAR(10)') = 'EQUALITY'
                 FOR XML PATH('')
             ) AS EqualityColumns
             , (
                 SELECT DISTINCT R.C.value('@Name', 'SYSNAME') + ', '
                 FROM Q.N.nodes('//ColumnGroup') AS T(CG)
                 CROSS APPLY T.CG.nodes('Column') AS R(C)
                 WHERE T.CG.value('@Usage', 'VARCHAR(10)') = 'INEQUALITY'
                 FOR XML PATH('')
             ) AS InequalityColumns
             , ( 
                 SELECT DISTINCT R.C.value('@Name', 'SYSNAME') + ', '
                 FROM Q.N.nodes('//ColumnGroup') AS T(CG)
                 CROSS APPLY T.CG.nodes('Column') AS R(C)
                 WHERE T.CG.value('@Usage', 'VARCHAR(10)') = 'INCLUDE'
                 FOR XML PATH('')
             ) AS IncludeColumns
        FROM sys.dm_exec_cached_plans CP
        CROSS APPLY sys.dm_exec_query_plan(CP.plan_handle) QP
        CROSS APPLY QP.query_plan.nodes('//StmtSimple') AS Q(N)
        WHERE Q.N.exist('QueryPlan/MissingIndexes') = 1
    ) 
SELECT cte.query_plan
     , cte.Impact
     , cte.DatabaseName
     , cte.SchemaName
     , cte.TableName
     , LEFT(cte.EqualityColumns  , LEN(cte.EqualityColumns)   - 1) AS EqualityColumns
     , LEFT(cte.InequalityColumns, LEN(cte.InequalityColumns) - 1) AS InequalityColumns
     , LEFT(cte.IncludeColumns   , LEN(cte.IncludeColumns)    - 1) AS IncludeColumns
FROM cte;

 

OK.. non proprio semplicissima, ve lo concedo!… ma il bello è che l’ho già scritta io per voi! 😉

Alla prossima!

 

 

 

 

 

 

 

Da qualche tempo hai usato l’interconnessione in Auto: Nei prossimi anni la utilizzerai di più

Facebooktwittergoogle_plusredditlinkedin

In un mondo sempre più interconnesso, anche il settore delle “quattro ruote” sta facendo notevoli passi avanti, basti osservare gli ultimi modelli prodotti dalle diverse case costruttrici, dotati di “mini” sistemi operativi, computer di bordo sempre più sofisticati che oggi sono già disponibili presso gli stock delle Concessionarie di auto usate dislocate lungo tutto il […]

Veeam Backup & Replication v9.5 Update 3: Integrazione con gli Agent

Facebooktwittergoogle_plusredditlinkedin

Grazie all’update 3 di Veeam Backup & Replication v9.5, tutti gli utenti hanno beneficiato di una feature inizialmente prevista per la versione 10, che introduce un modo nuovo di gestire in modo centralizzato Veeam Agent.

Fino a poco tempo fa, infatti, la gestione di client e server fisici erano obbligatoriamente da svolgere manualmente, a meno di costruire script in PowerShell, e questo significava investire molto tempo per installare, configurare e gestire i vari endpoint, senza considerare il fatto che alcune macchine potevano non essere sempre disponibili (come i notebook di utenti sempre in mobilità).

La nuova sezione chiamata Pysical & Cloud Insfrastructure, aggiunge la capacità di distribuire e gestire in modo centralizzato le macchine, Windows e Linux, che a bordo hanno Veeam Agent.

Creazione Protection Group

La creazione di un nuovo Protection Group, così si chiama il servizio di gestione degli agent, è disponibile all’interno della sezione Inventory – figura 1.

Figura 1 – Nuovo Protection Group

Gli oggetti possono essere aggiunti manualmente, tramite la scansione in Active Directory oppure attraverso un file .csv (perfetto per grandi volumi).

Figura 2 – Installazione Agent Manuale

Figura 3 – Installazione Agent da AD

Come in ogni distribuzione di massa, è disponibile fare delle esclusioni basate sul fatto che la macchina sia virtuale, non disponibile da più di 30 giorni o più semplicemente specificando gli oggetti da non considerare in fase di scansione.

La capacità di distribuire gli agent in modo centralizzato ha introdotto un nuovo ruolo, chiamato Distribution Server, che ha il compito di fare la discovery (in caso sia impostato di analizzare Active Directory) e gestire gli agent anche in termini di aggiornamenti – figura 4.

Figura 4 – Opzioni Protection Group

Per quanto riguarda gli ambienti Windows Server, grazie alla versione 2.1 di Veeam Agent, è possibile installare il nuovo modulo di Change Block Tracking, che consente di eseguire backup incrementali a livello di blocco, mentre attualmente vengono identificati i blocchi cambiati ma il file deve essere letto per intero. Nota importante è che questa funzionalità richiede una licenza di tipo Server.

Conclusa la fase di configurazione iniziale, verrà avviata la discovery che avvierà l’installazione degli agent all’interno delle varie macchine selezionate – figura 5.

Figura 5 – Installazione Agent

Configurazione Backup Plan

Archiviata la discovery delle macchine da proteggere, è necessario creare il backup plan che verrà distribuito in modo centralizzato agli endpoint dei vari Protection Group configurati – figura 6.

La modalità di gestione del plan può essere gestito in due modi:

  • By Agent: indicato per un modello workstation (es. Windows 10)
  • By Server: indicato per un modello server (es. SQL Server)

Figura 6 – Nuovo Backup Job

Gli step successivi sono già noti a chi utilizza Veeam Agent e riguardano:

  • Tipologia di backup: Computer, Volume o Folder
  • Destinazione: Local Storage, Shared, Veeam Repository
  • Retention
  • Schedule Backup Time

Quello che si può notare nella schermata relativa a dove posizionare il backup è che non si può scegliere OneDrive come repository, così come il salvataggio tramite Veeam Cloud Connect, funzioni disponibili nella versione standalone di Veeam Agent 2.1.

Figura 7 – Backup Mode

Figura 8 – Destinazione Backup

Figura 9 – Backup Repository

Figura 10 – Schedule Time by Agent

Utilizzando la versione Managed by Server, i passaggi cambiano leggermente perché vengono proposte funzionalità diverse: scompare la possibilità di scegliere la destinazione (mandatorio quello Veeam B&R) e si rende possibile fare backup Application Aware e Guest File Index – figura 11; inoltre il job di backup viene eseguito con una logica più simile a quella di Backup & Replication – figura 12.

Per sapere quali sono le differenze tra i tre modelli di licensing di Veeam Agent, potete consultare la guida disponibile a questo link: https://www.veeam.com/veeam_agent_windows_2_1_editions_comparison_ds.pdf

Figura 11 – Job Server – Guest Processing

Figura 12 – Job Server – Schedule Time

Backup Job

Il job di backup verrà inviato alle macchine di destinazione non appena possibile – figura 13 – e con esso anche la licenza che sbloccherà le funzionalità richieste.

Figura 13 – Configurazione Inviata al Client

Lato client troveremo due aspetti che ci faranno capire la tipologia di gestione, tra cui l’impossibilità di modificare le impostazioni del backup – figura 14.

Figura 14 – Gestione Centralizzata

L’unica attività abilitata è quella di eseguire job di backup manuali, che si aggiungono a quelli schedulati dall’amministratore, che comunque vengono riportati all’interno della console di Backup & Replication.

Restore

Il recupero dei file può essere effettuato dal client diretto oppure dalla console di B&R.

Figura 15 – Restore da Agent

Facendo il ripristino dei dati dal server Veeam è possibile eseguire più task – figura 16 – come il restore su Microsoft Azure o la conversione della macchina fisica in virtuale.

Figura 16 – Restore da Console B&R

Conclusioni

La gestione centralizzata dei client è sicuramente l’elemento mancante che l’Update 3 ha introdotto in grande stile. Sicuramente mancano delle funzionalità ma non è da escludere che con la versione 10, anche questo gap verrà colmato.

Primi passi con Ansible – Parte 1

Facebooktwittergoogle_plusredditlinkedin

Ansible è un software open source utilizzato per l’automazione di parchi macchine. Può gestire l’installazione e la configurazione di qualsiasi componente del sistema così come la definizione di procedure di deploy automatizzate.

Creato nel 2012 da Michael DeHaan, già autore di Cobbler e co-autore di Func, è balzato subito tra i sistemi di configuration management più utilizzati grazie ad alcuni punti focali su cui è stato realizzato:

  • Non richiede l’installazione di un agent sulle macchine da gestire, basta un accesso ssh e la presenza di Python 2 (versioni 2.6 o 2.7), oppure di Python 3 (versioni 3.5 o successive), generalmente disponibili su qualsiasi OS;
  • Non richiede conoscenza di linguaggi di programmazione, la sintassi YAML con cui si scrivono le istruzioni è di semplice lettura e comprensione;
  • E’ scritto in python, il che lo rende multipiattaforma e non dipendente dalla distribuzione, leggero e performante;

Ha preso così tanto piede che è stata fondata la Ansible Inc. per commercializzare il supporto e sponsorizzare la soluzione e, successivamente, l’azienda è stata acquistata da Red Hat a fine 2015.

In questo primo articolo vedremo come installare Ansible, iniziando ad utilizzarlo per eseguire operazioni di base.

Terminologia

Per prima cosa, andiamo ad analizzare alcuni dei termini utilizzati dal software, così da comprendere meglio le sue componenti:

  • Inventario: Si tratta della lista di macchine sulle quali Ansible può operare. Opzionalmente tali macchine possono essere raggruppate in modo da avere più gruppi di host. L’inventario può essere scritto sia in formato ini che in yaml, ed il path in cui risiede di default è /etc/ansible/hosts, ma è chiaramente possibile effettuare l’override di questa posizione, passando a riga di comando locazioni differenti;
  • Moduli: i moduli possono essere visti come i comandi che eseguiamo sulle macchine. Esistono moduli per l’installazione e la rimozione di pacchetti, deployment di file (o generazione del loro contenuto), gestione dei servizi e tanto altro. La lista completa è davvero corposa;
  • Task: i task sono le operazioni che, di fatto, eseguiamo sulle macchine. Possono essere visti come la serie di comandi, in sequenza, che verranno lanciati sulla macchina. Un task è nella sostanza il richiamo di un modulo con particolari opzioni.
  • Handler: le operazioni associate agli handler si differenziano dai task poiché non vengono sempre eseguite, ma vengono richiamate come reazione ad un evento di un task. Un banale esempio è quello di avviare un servizio dopo aver installato il software necessario, o riavviarlo dopo la modifica del suo file di configurazione.
  • Playbook: i playbook sono una lista di task, handler e delle relative variabili legati a particolari gruppi di macchine in inventario. Vengono utilizzati per avere il “manuale di istruzioni” delle operazioni che Ansible dovrà eseguire.
  • Ruoli: i ruoli sono dei componenti dei playbook che raggruppano operazioni legate tra di loro con uno scopo specifico. Queste vengono di solito unificate per avere più riusabilità delle stesse. Ad esempio, l’installazione, la configurazione e la gestione del servizio NTP possono essere raggruppate in un ruolo, avendo la possibilità di riutilizzare questo ruolo in diversi Playbook.

Tutto il codice Ansible (e, volendo, anche l’inventario) è scritto in sintassi YAML, che permette una semplice scrittura e lettura del codice con qualisasi editor di testo. Il formato YAML ha precise regole: ad esempio la tabulazione non è accettata, in favore di un’indentazione a 2 spazi.

Installazione

Diverse sono le opzioni per l’installazione ed in molti casi i repository ufficiali delle nostre distribuzioni preferite già contengono i pacchetti necessari per installare Ansible:

RHEL/CentOS/Fedora
Su questi sistemi l’installazione è molto semplice. Se avete la versione 7 trovate gli rpm sul canale Extra, mentre per le versioni 6 degli OS potete aggiungere il repository EPEL ed avrete tutto il necessario. Dopodichè basterà installare il tutto:

controller# yum install ansible

Debian/Ubuntu
Ansible fornisce anche un repository PPA contenete i deb per una veloce installazione su Debian/Ubuntu. Basterà lanciare i seguenti comandi:

controller$ sudo apt-get update
controller$ sudo apt-get install software-properties-common
controller$ sudo apt-add-repository ppa:ansible/ansible
controller$ sudo apt-get update
controller$ sudo apt-get install ansible

Altre opzioni
Alternativamente, nel caso non siate soddisfatti dei pacchetti o vogliate installare l’ultimissima versione disponibile, la procedura di installazione dal repository GitHub del progetto è molto semplice:

controller$ git clone https://github.com/ansible/ansible.git --recursive
controller$ cd ./ansible
controller$ source ./hacking/env-setup
controller$ sudo easy_install pip
controller$ sudo pip install -r ./requirements.txt

Per l’installazione su altri OS o per l’aggiornamento, la documentazione ufficiale è molto esaustiva.

Prepariamo l’ambiente

Una volta installato, la prima cosa da fare è andare a creare un Inventario, ovvero andare a censire gli host che saranno gestiti da Ansible.

A differenza di altri software di automazione, come ad esempio Puppet, Ansible necessita esclusivamente di una connessione SSH tra la macchina “controller” (ad esempio il nostro laptop o un server centralizzato installato a tale scopo) ed i nodi da controllare; l’”agent” della macchina controllata sarà Python (normalmente già presente).

Di default Ansible utilizza sftp per scambiare le informazioni, ma se le vostre policy non lo consentono potete configurare l’uso di scp molto facilmente.

Per prima cosa, quindi, dobbiamo assicurarci che l’host sul quale girerà Ansible possa accedere via ssh senza password ai sistemi in inventario. Generiamo quindi la coppia di chiavi sull’host controller:

controller$ ssh-keygen -t rsa
...

dopodichè nasterà copiare la chiave su tutti i nodi interessati:

controller$ scp ~/.ssh/id_rsa.pub \
> utente@host1:~/.ssh/authorized_keys
Password: ...
controller$ ssh utente@host1 "chmod 600 ~/.ssh/authorized_keys"
Password: ...
controller$ ssh utente@host1
host1$ exit
controller$
...

Come vedete abbiamo abilitato l’accesso via ssh senza password dalla macchina controller ai vari host, con un utente non privilegiato. Adesso dobbiamo dire ai vari nodi che l’utente in questione (per il quale dovrà essere scelta una password forte) è abilitato a lanciare comandi amministrativi su questo sistema:

host1# echo "utente ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

Questo permetterà nel nostro inventario di impostare le seguenti variabili:

ansible_become: true
ansible_become_method: sudo

Così da assicurare la connessione con utenti non privilegiati e permettere ad Ansible di fare comunque le operazioni sulle macchine utilizzando sudo.

Il censimento degli host nell’inventario avviene popolando il file /etc/ansible/hosts, anche se è possibile utilizzare diversi file (magari per separare meglio la propria infrastruttura) e passarli come opzione ai comandi Ansible.

L’Inventario può essere scritto in due formati differenti; il formato INI è estremamente semplice, ed appare grosso modo così:

host1.example

[webservers]
web1.example 
web2.example
web3.example 

[dbservers] 
db1.example 
db2.example 
db3.example

Come si può notare host1 è fuori dai eventuali gruppi, gli host web1, web2 e web3 sono raggruppati sotto il gruppo webservers e così via.

L’inventario può essere definito anche in formato YAML. A volte si utilizza questo formato per la sua capacità di essere interoperabile con gli inventari definiti su Ansible Tower (o AWX, se preferite la versione community), un’interfaccia per la gestione di Ansible via web. Un altro motivo per scegliere il formato YAML è dato dalla complessità delle variabili che si possono inserire in esso, e per la sua uniformità con quella che sarà la sintassi utilizzata nelle altre componenti (Playbook e Ruoli).

Lo stesso inventario scritto con la sintassi YAML appare in questo modo

all:
  hosts:
    host1:
  vars:
    ansible_user: utente
    ansible_become: true
    ansible_become_method: sudo
  children:
    webservers:
      hosts:
        web1:
        web2:
    dbservers:
      hosts:
        db1:
        db2:
        db3:

Come notiamo, seppur questa sintassi introduce keyword non presenti nel formato ini (quali hosts, children, etc.), è comunque molto parlante, non richiede particolari nozioni di programmazione e mostra, anche visivamente, il raggruppamento dei vari host.

Usiamo i moduli

Finita la creazione dell’inventario, possiamo procedere a provare subito l’esecuzione diretta di un modulo su una o più macchine.

Uno dei primi moduli che normalmente vengono utilizzati per testare l’ambiente è il modulo ping. Questo modulo verifica il collegamento e la versione Python dell’host, e risponde pong nel caso tutto sia funzionante.

Vediamo quindi se abbiamo configurato bene l’inventario e caricato correttamente la chiave sulla macchina web1

controller$ ansible web1 -m ping
web1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

Ottimo, la risposta conferma come la connessione sia avvenuta con successo. Nel l’inventario sia in un file dedicato, è possibile passarlo come opzione a riga di comando (-i):

controller$ ansible -i inventory.yaml web1 -m ping
web1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

Una buona organizzazione dell’inventory permetterà di essere più selettivi nell’esecuzione dei moduli, garantendo l’esecuzione dello stesso task su un intero gruppo di macchine. Ad esempio, per verificare tutte e tre le macchine DB presenti nel nostro inventario, sarà possibile lanciare il modulo ping sul gruppo dbservers:

controller$ ansible dbservers -m ping
db1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
db2 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
db3 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

Passando, invece di un gruppo, la keyword all potremo eseguire il modulo su tutti gli host in inventario:

controller$ ansible all -m ping
host1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
web1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
web2 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
...

Una volta verificato il corretto funzionamento del tutto possiamo già riscontrare i benefici dell’avere Ansible integrato nell’infrastruttura. Ad esempio, nel caso abbiamo installato il webserver nginx sui nostri webserver e necessitiamo di sapere lo stato di questo servizio sulle varie macchine, possiamo finalmente dimenticare di collegarci manualmente su tutte le macchine (o scrivere script ad hoc che lo facciano per noi) e sfruttare Ansible per eseguire il comando su tutti gli host desiderati e fornirci il risultato:

controller$ ansible webservers -a "systemctl status nginx"
web1 | SUCCESS | rc=0 >>
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2017-11-08 11:00:28 CET; 2 months 3 days ago
 Main PID: 8173 (nginx)
   CGroup: /system.slice/nginx.service
           ├─8173 nginx: master process /usr/sbin/ngin
           ├─8174 nginx: worker proces
           ├─8175 nginx: worker proces
           ├─8176 nginx: worker proces
           └─8177 nginx: worker proces

Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.
web2 | SUCCESS | rc=0 >>
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2017-11-08 11:00:33 CET; 2 months 3 days ago
 Main PID: 31567 (nginx)
   CGroup: /system.slice/nginx.service
           ├─30765 nginx: worker process is shutting dow
           ├─31567 nginx: master process /usr/sbin/ngin
           ├─31568 nginx: worker proces
           ├─31569 nginx: worker proces
           ├─31570 nginx: worker proces
           └─31571 nginx: worker proces
controller$

Questa è la reale comodità di Ansible: che siano due o duecento macchine, se queste sono censite in inventario e con la chiave ssh inserita, l’effort per recuperare informazioni è assolutamente lo stesso.

Ovviamente utilizzando questo metodo possiamo fare praticamente tutto sulle nostre macchine. Assicuriamoci ad esempio che il servizio telegraf sia attivo su tutti i nostri server database:

controller$ ansible dbservers -m service -a 'name=telegraf enabled=yes state=started'
db1 | SUCCESS => {
    "changed": false,
    "enabled": true,
    "name": "telegraf",
    "state": "started"
}
db2 | SUCCESS => {
    "changed": false,
    "enabled": true,
    "name": "telegraf",
    "state": "started"
}
db3 | CHANGED => {
    "changed": true,
    "enabled": true,
    "name": "telegraf",
    "state": "started"
}

Abbiamo per questa attività utilizzato il modulo service che permette di controllare lo stato dei servizi sui sistemi, dicendo di verificare che il servizio di nome telegraf sia in stato enabled (quindi che si avvii automaticamente al boot del sistema) e started (quindi attualmente attivo). Su uno dei tre nodi (db3) il servizio risultava fermo, quindi è stato attivato. Potete notare lo stato CHANGED nell’output, che indica il fatto che il sistema non era conforme alle richieste e che una o più modifiche sono state fatte per portarlo allo stato richiesto dal modulo.

Conclusioni

Già questo primo assaggio di Ansible ci permette di sbirciare quelli che saranno i vantaggi dell’utilizzare il prodotto nella propria infrastruttura. Avere la possibilità di operare su interi ambienti senza doversi preoccupare di quelle che sono realmente le macchine nell’inventario è già di per se un passo avanti molto significativo.

Nelle prossime puntate vedremo come l’uso di Playbook e Ruoli permettano di definire stati completi delle macchine, andando ad avere dei file di “codice” che rappresentano lo stato degli host e rendendo qualsiasi ambiente estremamente replicabile in tempi brevissimi.

Restate sintonizzati, quindi, ne vedremo delle belle.

Esempi di SQL Graph Database su GitHub

Facebooktwittergoogle_plusredditlinkedin

SQL Server offre da sempre gli strumenti per gestire i dati gerarchici e le relazioni tra le entità. A volte, però, le relazioni tra le entità possono diventare complesse. Pensiamo ad una relazione molti-a-molti, i database relazionali non dispongono di soluzioni native per questo tipo di legame, che viene comunemente implementato con una tabella di associazione.

SQL Server 2017, grazie all’introduzione di Graph Database, permette di esprimere certi tipi di query più facilmente rispetto ad un database relazionale puro.

Alcuni giorni fa, Perry Skountrianos (@pskountrianos) ha confermato il “pull request” che ho fatto sul repository Microsoft/sql-server-samples di GitHub. Questo repository contiene esempi di codice che spiegano come utilizzare alcune funzionalità di SQL Server, SQL Azure e Azure Data Warehouse. Ogni esempio include il relativo file README.md che spiega come eseguire ed utilizzare il codice.

Nella directory relativa a SQL Graph (sql-server-samples/samples/demos/sql-graph/recommendation-system) troverete tre demo.

La prima spiega come creare e popolare i nuovi oggetti “Graph” ovvero due nuovi tipi di tabelle “Node” e “Edge”. La seconda demo ha l’obiettivo di introdurre la nuova clausola MATCH (T-SQL) usata per eseguire query su Graph DB attraversando i nodi e le relazioni creati nella prima demo. Nell’ultima demo verrà mostrato come costruire un sistema di raccomandazione per le offerte di vendita (basato sul database WideWorldImporters), in grado di determinare i prodotti consigliati sulla base del prodotto che il cliente sta acquistando oppure ha appena acquistato sul nostro ipotetico e-commerce, uno scenario tipico di utilizzo di SQL Graph.

Hai sentito parlare di SQL Graph e vuoi saperne di più? Questi esempi, disponibili su GitHub, sono un ottimo punto di partenza!

Buon divertimento!

PowerShell Core 6.0: una nuova era ha inizio

Facebooktwittergoogle_plusredditlinkedin

Da qualche giorno è disponibile una nuova versione di Windows PowerShell, giunta ormai alla sesta versione. La grande novità risiede nell’introduzione della versione Core, che ha l’obiettivo di portare l’utilizzo della shell Microsoft su tutti […]

Conversioni implicite: La plan cache ci svela quante sono e dove avvengono!

Facebooktwittergoogle_plusredditlinkedin

Nell’articolo Conversioni implicite: cosa sono e che impatto hanno sulle nostre query, Luca Bruni (@mrg3d) ci ha parlato delle conversioni implicite che avvengono, a nostra insaputa, all’interno dell’Engine di SQL Server. Tali conversioni, possono costringere il Query Optimizer ad accedere alle tabelle con operazioni di scansione (Table Scan, Index Scan) in alternativa alle più efficienti operazioni di Seek (Index Seek), e quando una parte significativa del carico di lavoro è interessata da conversioni implicite, le performance degradano visibilmente!

Una conversione implicita tra due tipi di dato differenti avviene sempre dal tipo di dato meno preciso al tipo di dato più preciso. Per ovvie ragioni, SQL Server non può perdere precisione nei dati a causa di una conversione implicita, parlando quindi di tipi di dato testo, una conversione implicita potrà avvenire, ad esempio, dal tipo VARCHAR (meno preciso) al tipo NVARCHAR (più preciso), mai nella direzione opposta.

Probabilmente vi starete chiedendo se il vostro carico di lavoro è affetto da conversioni implicite e in che percentuale.

Le conversioni implicite, all’interno del piano di esecuzione, vengono segnalate con un warning. Il Query Optimizer può segnalare i seguenti tipi di warning:

  • No statistics (ColumnsWithNoStatistics)
  • No join predicate (NoJoinPredicate)
  • Implicit conversions (PlanAffectingConvert)
  • Sort on tempdb (SpillToTempDb)
  • No matched indexes (UnmatchedIndexes)
  • Wait (WaitWarningType)

..tra cui troviamo l’Implicit Conversions Warning (PlanAffectingConvert) di cui parleremo in questo articolo.

Il warning PlanAffectingConvert viene segnalato proprio in presenza di conversioni implicite, quando viene utilizzata la funzione CONVERT_IMPLICIT. È quindi possibile fare indagini sulla presenza di conversioni implicite nel carico di lavoro analizzando la plan cache, ovvero l’area di memoria utilizzata da SQL Server per memorizzare i piani di esecuzione delle query.

Utilizzando la metodologia “top-down” per analizzare il problema, il primo dato che vorremmo avere è la percentuale dei piani di esecuzione in cache affetti da conversioni implicite. Per ottenerlo dovremo contare i piani di esecuzione in cache, e di questi, quelli affetti da conversioni implicite… la seguente CTE estrae le informazioni che cerchiamo: il numero dei piani di esecuzione in cache, il numero dei piani di esecuzione in cui viene utilizzata la funzione CONVERT_IMPLICIT ed il calcolo della percentuale.

WITH XMLNAMESPACES
(
 DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'
),
Cached_Plans AS
(
 SELECT
 COUNT(cp.plan_handle) AS Total_Plan_Cached
 FROM
 sys.dm_exec_cached_plans AS cp
),
Cached_Plans_with_Convert AS
(
 SELECT
 COUNT(cp.plan_handle) AS Total_Plan_Cached_with_Convert
 FROM
 sys.dm_exec_cached_plans AS cp
 CROSS APPLY
 sys.dm_exec_query_plan(cp.plan_handle) AS qp
 CROSS APPLY
 sys.dm_exec_sql_text(cp.plan_handle) AS qt
 WHERE
 (qp.query_plan.exist('//Warnings') = 1)
 AND CAST(qp.query_plan.query('//Warnings') AS VARCHAR(MAX)) LIKE '%CONVERT_IMPLICIT%'
)
SELECT
 Total_Plan_Cached
 ,Total_Plan_Cached_with_Convert = (SELECT Total_Plan_Cached_with_Convert FROM Cached_Plans_with_Convert)
 ,CAST(CAST((SELECT Total_Plan_Cached_with_Convert FROM Cached_Plans_with_Convert) AS DECIMAL(10,3)) / 
 CAST(Total_Plan_Cached AS DECIMAL(10,3)) AS DECIMAL(6,3)) * 100 [% Plan_Cached_with_Convert]
FROM
 Cached_Plans;

L’output di esempio è illustrato nelle figura seguente, dove circa il 4% del carico di lavoro è affetto da conversioni implicite.

Qualora venisse rilevato un numero considerevole di piani di esecuzione affetti da conversioni implicite, la seguente CTE ci potrà aiutare nell’identificazione delle query specifiche in cui avvengono le conversioni implicite, tra le colonne estratte vi è il testo della query, il piano di esecuzione completo e la porzione relativa ai warning di tipo PlanAffectingConvert in cui è stata utilizzata la funzione CONVERT_IMPLICIT.

WITH XMLNAMESPACES
(
 DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'
),
PlanAffectingConvert AS
(
 SELECT
 query_text = qt.text
 ,cp.objtype
 ,cp.refcounts
 ,cp.usecounts
 ,Plan_Warnings = qp.query_plan.query('//Warnings')
 ,qp.query_plan
 FROM
 sys.dm_exec_cached_plans AS cp
 CROSS APPLY
 sys.dm_exec_query_plan(cp.plan_handle) AS qp
 CROSS APPLY
 sys.dm_exec_sql_text(cp.plan_handle) AS qt
 WHERE
 (qp.query_plan.exist('//Warnings') = 1)
)
SELECT
 *
FROM
 PlanAffectingConvert
WHERE
 CAST(Plan_Warnings AS VARCHAR(MAX)) LIKE '%CONVERT_IMPLICIT%';

Concludendo, se avete il dubbio che il vostro carico di lavoro sia affetto da conversioni implicite, un buon punto di partenza è l’utilizzo delle query descritte in questo articolo… se pensate di non avere conversioni implicite nelle vostre query, fate lo stesso un controllo… non costa nulla!

Buon divertimento!