...
- Subir banco normalmente (com log).
Bloco de código DBSRV12 @<nome arquivo parametro> # scgwin.par: # -c120M # -xNone # -nScgwin12 # scgwin.db
- Executar o ISQL e executar um comando UPDATE.
Bloco de código SELECT descricao FROM DEPOSITO WHERE CODDEP='1' // pode ser qq código, somente memorize o código e descrição UPDATE DEPOSITO SET DESCRICAO = DESCRICAO + '1' WHERE CODDEP='1' // para acrescentar 1 ao final do campo descricao
- Dar COMMIT ao UPDATE.
Bloco de código COMMIT
- Derrubar banco de forma anormal (através do gerenciador de processos do Windows - aba Processos ou executando kill no Linux).
- Windows: Executar Ctrl+Alt+Del, ir para a aba Processos, selecionar o processo DbSrv12 e clicar em Finalizar Processo.
- Linux: Executar pgrep DBsrv12, anotar o número do processo e executar Kill <numero do processo>.
- Transcrever log de transações.
Bloco de código dbtran scgwin.log // vai jogar o arquivo de log transcrito para scgwin.sql
- Copiar banco e log para um outro diretório para realizar uma segunda experiência com ele.
Bloco de código // jogar para o subdiretorio backup COPY SCGWIN.* BACKUP/SCGWIN.*
- Subir o banco original.
Bloco de código DBSRV12 @<nome arquivo parametro> # scgwin.par: # -c120M # -xNone # -nScgwin12 # scgwin.db
- Verificar se UPDATE foi honrado (deve ter sido).
Bloco de código SELECT descricao FROM DEPOSITO WHERE CODDEP='1' // pode ser qq código, somente memorize o código e descrição // deve mostrar DESCRICAO ORIGINAL + '1'
- Derrubar (shutdown) banco normalmente.
- Ir para diretório onde o banco foi copiado (passo 6).
- Excluir arquivo Log.
- Forçar subir o banco sem log (cláusula -f).
Bloco de código dbsrv12 -f scgwin.db
- Subir banco e verificar UPDATE (não deve ter sido honrado).
...
É importante considerar algo que nem sempre nos atemos quando instalamos o banco de dados: que ele está no topo de uma estrutura ao qual não tem controle direto (ver estrutura abaixo). Assim, quando ele solicita que um dado seja gravado no disco, está usando uma chamada de função de um sistema operacional. Em última instância, o que o Banco deseja é ver o dado gravado no disco porém o responsável por este comando funcionar são os níveis abaixo e como eles interagem entre si.
...
Existe um documento da própria Sybase para analisar a influência de cada elo na cadeia da gravação de um dado e é importante que o administrador do banco de dados o leia. O nome do documento é SQL Anywhere I/O Requirements for Windows and Linux. Resumidamente, existem duas preocupações importantes com relação ao processo de gravação do banco: cache em cada nível da pilha (disco rígido, controladora e sistema de arquivos em Linux) e o tipo do sistema de arquivos em Linux (no Windows, como o sistema de arquivos é normalmente NTFS, não existe uma preocupação neste sentido como é o caso do Linux onde existem diversos tipos de sistemas de arquivos).
...
- Obtendo informações do banco.
Bloco de código # -u -> pedir informacoes de todas as tabelas inclusive da de sistema # -c -> parametros de conexão # -q -> quiet (nao mostrar informações na tela) # -o -> jogar informações para um arquivo DBINFO -u -c "server=<nome servidor>;uid=DBA;pwd=<senha banco>" -q -o "<nome arquivo output>"
- Calcular aumento do tamanho do banco.
O número de páginas livre deve ser ajustado conforme a velocidade de aumento do banco. Assim, a idéia é executar o procedimento acima durante uma semana para encontrar uma taxa de decréscimo de páginas livre de forma a realizar esta tarefa mensalmente. Vamos supor que um banco diminui seu número de páginas livre a uma média de 1000 páginas por dia (1000 x 4k = 4Mb por dia). Assim, se quisermos ter uma folga de 30 dias (assumindo que o banco esteja com um número mínimo de páginas livre neste momento), teremos de acrescentar 30 x 1000p = 30000 páginas ou 30000p x 4k (tamanho de 1 página) = 120Mb ao tamanho do banco. - Aumentar banco.
Executar no ISQL o seguinte comando (ajustando o tamanho a ser acrescido conforme o resultado no seu banco):Bloco de código ALTER DBSPACE SYSTEM ADD 120MB
- Desfragmentar Sistema operacional.
Nota O ideal é criar um evento que rode à noite para fazer este trabalho de forma automática.
...
- Criar banco de dados.
Bloco de código title 02_run_dbinit12.bat "%SQLANY12%\bin32\dbinit.exe" ddd12.db
- Subir banco de dados criado.
Bloco de código title 03_run_dbspawn_dbsrv12.bat "%SQLANY12%\bin32\dbspawn.exe"^ -f "%SQLANY12%\bin32\dbsrv12.exe"^ -o dbsrv12_log_ddd12.txt^ -oe dbsrv12_log_fatal_ddd12.txt^ -os 10M^ -x tcpip^ -zl^ -zp^ -zt^ ddd12.db # -o: log do console do servidor vai para arquivo # -oe: erros fatais vão para outro arquivo de log # -x TCPIP: protocolo TCP/IP # -os: renomear log qdo tamanho arquivo atingir tamanho especificado # -zl: relembrar ultimo comando executado para cada conexão # -zp: relembrar plano da última query executada de cada conexão # -zt: monitorar tempo de execução de cada processamento de cada conexão
- Preencher banco de dados com algumas tabelas e dados.
sendo que o conteúdo do script 04s_script_to_create_table_and_event.sql contém:Bloco de código title 04_run_script_to_create_table_and_event.bat "%SQLANY12%\bin32\dbisql.com"^ -c "ENG=ddd12;DBN=ddd12;UID=dba;PWD=sql"^ READ ENCODING Cp1252 04s_script_to_create_table_and_event.sql
Bloco de código title 04s_script_to_create_table_and_event.sql CREATE TABLE rapid ( pkey INTEGER NOT NULL DEFAULT AUTOINCREMENT PRIMARY KEY ); CREATE EVENT rapid_insert HANDLER BEGIN WHILE 1 = 1 LOOP INSERT rapid VALUES ( DEFAULT ); COMMIT; WAITFOR DELAY '00:00:00.1'; END LOOP; END; TRIGGER EVENT rapid_insert;
- Criar diretório e realizar backup full para ele.
- Criar diretório temporário e copiar backup (somente o arquivo .db) para ele.
- Subir a cópia do banco aplicando o log de transações validando o backup.
- Copiar diretório de backup validado para um outro diretório a fim de preservar até 10 diretórios de backups.
Bloco de código title 05_run_dbbackup12_full.bat REM ****************************************************************** REM Criar sub-diretorio backup\generation_temp MD backup CD backup RD /S /Q generation_temp MD generation_temp CD .. ECHO ********************************************^ *********************************************>>backup\generation_temp\dbbackup_log.txt ECHO ***** Iniciado backup full >>backup\generation_temp\dbbackup_log.txt SET ERRMSG= DATE /T >>backup\generation_temp\dbbackup_log.txt TIME /T >>backup\generation_temp\dbbackup_log.txt REM ****************************************************************** ECHO ***** Passo 1 Backup Full >>backup\generation_temp\dbbackup_log.txt "%SQLANY12%\bin32\dbbackup.exe"^ -c "ENG=ddd12;DBN=ddd12;UID=dba;PWD=sql"^ -o backup\generation_temp\dbbackup_log.txt^ -x^ backup\generation_temp IF ERRORLEVEL 1 SET ERRMSG=Passo 1 Backup Full falhou com ERRORLEVEL = %ERRORLEVEL% IF NOT "%ERRMSG%z" == "z" GOTO backup_failed REM ****************************************************************** ECHO ***** Passo 2 Aplicar log >>backup\generation_temp\dbbackup_log.txt CD backup\generation_temp MD validate XCOPY ddd12.db validate /V /Q /K CD ..\.. REM Note: O diretório da cláusula -ad é relativo ao folder contendo o bando de dados. "%SQLANY12%\bin32\dbsrv12.exe"^ -o backup\generation_temp\dbbackup_log.txt^ backup\generation_temp\validate\ddd12.db^ -ad "%CD%\backup\generation_temp" IF ERRORLEVEL 1 SET ERRMSG=Passo 2 Aplicar log falhou com ERRORLEVEL = %ERRORLEVEL% IF NOT "%ERRMSG%z" == "z" GOTO backup_failed REM ****************************************************************** ECHO ***** Passo 3 Iniciar cópia com cláusula read-only >>backup\generation_temp\dbbackup_log.txt "%SQLANY12%\bin32\dbspawn.exe"^ -f "%SQLANY12%\bin32\dbsrv12.exe"^ -n readonlycopy^ -o backup\dbsrv12_readonlycopy_log.txt^ -r^ backup\generation_temp\validate\ddd12.db IF ERRORLEVEL 1 SET ERRMSG=Passo 3 Iniciar banco cópia com cláusula read-onlyu falhou com ERRORLEVEL = %ERRORLEVEL% IF NOT "%ERRMSG%z" == "z" GOTO backup_failed REM ****************************************************************** ECHO ***** Passo 4 Validar >>backup\generation_temp\dbbackup_log.txt "%SQLANY12%\bin32\dbvalid.exe"^ -c "ENG=readonlycopy;DBN=ddd12;UID=dba;PWD=sql"^ -o backup\generation_temp\dbbackup_log.txt^ -q IF ERRORLEVEL 1 SET ERRMSG=Passo 4 Validar falhou com ERRORLEVEL = %ERRORLEVEL% REM ****************************************************************** ECHO ***** Passo 5 Parar cópia read-only >>backup\generation_temp\dbbackup_log.txt "%SQLANY12%\bin32\dbstop.exe"^ -c "ENG=readonlycopy;DBN=ddd12;UID=dba;PWD=sql"^ -y IF NOT "%ERRMSG%z" == "z" GOTO backup_failed REM ****************************************************************** ECHO ***** Passo 6 Mover arquivos >>backup\generation_temp\dbbackup_log.txt CD backup RD /S /Q generation1 RENAME generation2 generation1 RENAME generation3 generation2 RENAME generation4 generation3 RENAME generation5 generation4 RENAME generation6 generation5 RENAME generation7 generation6 RENAME generation8 generation7 RENAME generation9 generation8 RENAME generation10 generation9 RENAME generation_temp generation10 CD .. REM ****************************************************************** REM Backup OK DIR /S backup\generation10\*.* >>backup\generation10\dbbackup_log.txt ECHO ***** Backup full OK >>backup\generation10\dbbackup_log.txt DATE /T >>backup\generation10\dbbackup_log.txt TIME /T >>backup\generation10\dbbackup_log.txt GOTO end :backup_failed REM ****************************************************************** REM Backup falhou ECHO ***** Error: Backup full falhou... >>backup\generation_temp\dbbackup_log.txt ECHO ***** %ERRMSG% >>backup\generation_temp\dbbackup_log.txt DATE /T >>backup\generation_temp\dbbackup_log.txt TIME /T >>backup\generation_temp\dbbackup_log.txt GOTO end :end
Nota A cláusula -ad é uma das únicas cláusulas onde o diretório passado é relativo ao local onde está o banco de dados e não (como é o padrão normal do SQLAnywhere) o local onde o servidor (executável) está sendo executado ou o folder corrente. Assim, utiliza-se a variável %CD% (que retorna o diretório corrente) para se criar um caminho completo a partir da raiz.
- Realizar backup incremental.
- Validar backup incremental subindo a cópia do backup realizado no passo anterior.
Bloco de código title 07_run_dbbackup12_incremental.bat ECHO ********************************************^ *********************************************>>backup\generation10\dbbackup_log.txt ECHO ***** Backup incremental iniciado >>backup\generation10\dbbackup_log.txt SET ERRMSG= DATE /T >>backup\generation10\dbbackup_log.txt TIME /T >>backup\generation10\dbbackup_log.txt REM ****************************************************************** ECHO ***** Passo 1 Verificar backup full >>backup\generation10\dbbackup_log.txt IF EXIST "backup\generation10\ddd12.db" ( GOTO check_full_log_backup ) ELSE ( GOTO full_backup_is_missing ) :check_full_log_backup IF EXIST "backup\generation10\ddd12.log" ( GOTO full_backup_exists ) ELSE ( GOTO full_backup_is_missing ) :full_backup_is_missing SET ERRMSG=Passo 1 Verificar backup full falhou GOTO backup_failed :full_backup_exists REM ****************************************************************** ECHO ***** Passo 2 Backup Incremental >>backup\generation10\dbbackup_log.txt CD backup\generation10 MD logs CD ..\.. "%SQLANY12%\bin32\dbbackup.exe"^ -c "ENG=ddd12;DBN=ddd12;UID=dba;PWD=sql"^ -o backup\generation10\dbbackup_log.txt^ -n^ -t^ -x^ backup\generation10\logs IF ERRORLEVEL 1 SET ERRMSG=Passo 2 Backup Incremental falhou com ERRORLEVEL = %ERRORLEVEL% IF NOT "%ERRMSG%z" == "z" GOTO backup_failed REM ****************************************************************** ECHO ***** Passo 3 Aplicar log >>backup\generation10\dbbackup_log.txt REM Nota: O diretório da cláusula -ad é relativo ao diretório contendo o banco de dados. "%SQLANY12%\bin32\dbsrv12.exe"^ -o backup\generation10\dbbackup_log.txt^ backup\generation10\validate\ddd12.db^ -ad "%CD%\backup\generation10\logs" IF ERRORLEVEL 1 SET ERRMSG=Passo 3 Aplicar log falhou com ERRORLEVEL = %ERRORLEVEL% IF NOT "%ERRMSG%z" == "z" GOTO backup_failed REM ****************************************************************** REM Backup OK DIR /S backup\generation10\*.* >>backup\generation10\dbbackup_log.txt ECHO ***** Backup Incremental OK >>backup\generation10\dbbackup_log.txt DATE /T >>backup\generation10\dbbackup_log.txt TIME /T >>backup\generation10\dbbackup_log.txt GOTO end :backup_failed REM ****************************************************************** REM Backup falhou ECHO ***** Error: Backup Incremental falhou >>backup\generation10\dbbackup_log.txt ECHO ***** %ERRMSG% >>backup\generation10\dbbackup_log.txt DATE /T >>backup\generation10\dbbackup_log.txt TIME /T >>backup\generation10\dbbackup_log.txt GOTO end :end
Nota Opção: Repetir o último passo 10 vezes preservando o conteúdo de até 10 diretórios.
- Mostrar o conteúdo do banco de dados a cada backup incremental.
Bloco de código title 06_display_table.bat "%SQLANY12%\bin32\dbisql.com"^ -c "ENG=ddd12;DBN=ddd12;UID=dba;PWD=sql"^ SELECT COUNT(*), MAX ( pkey ) FROM rapid PAUSE Nota: COUNT deve ser igual ao MAX
- Parar o banco simulando um problema grave.
Bloco de código title 08_stop_drop_database.bat "%SQLANY12%\bin32\dbstop.exe" -c "ENG=ddd12;DBN=ddd12;UID=dba;PWD=sql" -y PAUSE Esperar enquando o banco para e entao... ERASE /F ddd12.db REM Nota: O log de transacao não é apagado.
- Restaurar o banco a partir do primeiro diretório. É importante salientar que a ordem em que os logs são aplicados é fundamental para o sucesso da restauração. Assim, deve-se sempre aplicar os logs na seguinte ordem:
- Log do backup full que iremos utilizar como ponto de partida da restauração.
- Logs incrementais a partir de então.
- Log do banco de produção (utilizando a cláusula -a).
Bloco de código title 09_restore_database.bat ECHO ********************************************^ *********************************************>>backup\generation10\dbbackup_log.txt ECHO Iniciado Restore >>backup\generation10\dbbackup_log.txt DATE /T >>backup\generation10\dbbackup_log.txt TIME /T >>backup\generation10\dbbackup_log.txt COPY backup\generation1\ddd12.db REM Nota: A cláusula -o grava os dados do diagnostico do backup deste exemplo. REM Nota: O diretório da cláusula -ad é relativo ao diretório contendo o banco de dados. "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation1 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation1\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation2 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation2\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation3 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation3\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation4 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation4\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation5 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation5\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation6 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation6\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation7 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation7\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation8 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation8\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation9 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation9\logs "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation10 "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -ad backup\generation10\logs REM Usar a cláusula -a para aplicar o log mais recente. "%SQLANY12%\bin32\dbsrv12.exe" -o backup\generation10\dbbackup_log.txt ddd12.db -a ddd12.log REM ****************************************************************** REM Fim do Restore DATE /T >>backup\generation10\dbbackup_log.txt TIME /T >>backup\generation10\dbbackup_log.txt
- Subir o banco e mostrar o seu conteúdo executando novamente as batches 03_run_dbspawn_dbsrv12.bat e 06_display_table.bat.
...
- Preparando os dois servidores (primario=server1 e secundario=server2).
Basicamente iremos utilizar o banco de dados que vem com o SQLAnywhere (demo) para esta demonstração. Assim, iremos criar diretórios para cada banco, instalar uma stored procedure no banco (para trazer informações sobre o banco numa outra etapa) e copiar o mesmo banco para os diretorios server1 e server2.Bloco de código title 1_create_ha.bat REM Criando os subdiretorios... MD arbiter MD server1 MD server2 PAUSE REM Copiando o banco e o log no subdiretorio server1... PAUSE CD server1 COPY "C:\Documents and Settings\All Users\Documentos\SQL Anywhere 12\Samples\demo.db" COPY "C:\Documents and Settings\All Users\Documentos\SQL Anywhere 12\Samples\demo.log" "%SQLANY12%\bin32\dblog.exe" -t demo.log demo.db CD .. PAUSE REM Preparando os bancos para uso... PAUSE "%SQLANY12%\bin32\dbspawn.exe" -f "%SQLANY12%\bin32\dbeng12.exe" -n temp server1\demo.db "%SQLANY12%\bin32\dbisql.com" -c "ENG=temp;DBN=demo;UID=dba;PWD=sql" READ additional_DDL.sql "%SQLANY12%\bin32\dbstop.exe" -y -c "ENG=temp;UID=dba;PWD=sql" PAUSE REM Copiando o banco preparado para o server2... PAUSE copy server1\demo.db server2 copy server1\demo.log server2 PAUSE PREPARACAO OK
Bloco de código title additional_ddl.sql CREATE OR REPLACE FUNCTION which_database ( IN @verbosity VARCHAR ( 7 ) DEFAULT 'verbose' ) -- or 'concise' RETURNS LONG VARCHAR BEGIN IF @verbosity = 'concise' THEN RETURN STRING ( 'Connection Number / Name ', CONNECTION_PROPERTY ( 'Number' ), ' / ', CONNECTION_PROPERTY ( 'Name' ), ', Server ', PROPERTY ( 'Name' ), ', Database ', DB_PROPERTY ( 'Name' ), IF PROPERTY ( 'Name' ) <> PROPERTY ( 'ServerName' ) THEN IF DB_PROPERTY ( 'ReadOnly' ) = 'On' THEN ' (HA secondary)' ELSE ' (HA primary)' ENDIF ELSE '' ENDIF, ' on ', PROPERTY ( 'MachineName' ), ' at ', IF CONNECTION_PROPERTY ( 'CommLink' ) = 'TCPIP' THEN '' ELSE STRING ( CONNECTION_PROPERTY ( 'CommLink' ), ' ' ) ENDIF, IF CONNECTION_PROPERTY ( 'CommNetworkLink' ) = 'TCPIP' THEN PROPERTY ( 'TcpIpAddresses' ) ELSE CONNECTION_PROPERTY ( 'CommNetworkLink' ) ENDIF, ' using ', DB_PROPERTY ( 'File' ) ); ELSE RETURN STRING ( ' Connection number: ', CONNECTION_PROPERTY ( 'Number' ), '\x0d\x0a Connection name: CON=', CONNECTION_PROPERTY ( 'Name' ), '\x0d\x0a Server name: ENG=', PROPERTY ( 'Name' ), '\x0d\x0a Database name: DBN=', DB_PROPERTY ( 'Name' ), IF PROPERTY ( 'Name' ) <> PROPERTY ( 'ServerName' ) THEN STRING ( '\x0d\x0a Actual HA server name: ', PROPERTY ( 'ServerName' ), IF DB_PROPERTY ( 'ReadOnly' ) = 'On' THEN ' (read-only HA secondary)' ELSE ' (updatable HA primary)' ENDIF, '\x0d\x0a HA arbiter is: ', DB_PROPERTY ( 'ArbiterState' ), '\x0d\x0a HA partner is: ', DB_PROPERTY ( 'PartnerState' ), IF DB_PROPERTY ( 'PartnerState' ) = 'connected' THEN STRING ( ', ', DB_PROPERTY ( 'MirrorState' ) ) ELSE '' ENDIF ) ELSE '' ENDIF, '\x0d\x0a Machine name: ', PROPERTY ( 'MachineName' ), '\x0d\x0a Connection via: ', IF CONNECTION_PROPERTY ( 'CommLink' ) = 'TCPIP' THEN 'Network ' ELSE STRING ( CONNECTION_PROPERTY ( 'CommLink' ), ' ' ) ENDIF, CONNECTION_PROPERTY ( 'CommNetworkLink' ), IF CONNECTION_PROPERTY ( 'CommNetworkLink' ) = 'TCPIP' THEN STRING ( ' to ', PROPERTY ( 'TcpIpAddresses' ) ) ELSE '' ENDIF, '\x0d\x0a Database file: ', DB_PROPERTY ( 'File' ) ); END IF; END; -- FUNCTION which_database
- Iniciando os bancos
Iremos iniciar cada banco com uma configuração específica salva em um arquivo texto (arbitro=arbiterconfig; servidor primário=server1config; servidor secundário=server2config).Bloco de código title 2_start_HA.bat REM Iniciando os servidores... "%SQLANY12%\bin32\dbspawn.exe" -f "%SQLANY12%\bin32\dbsrv12.exe" @arbiterconfig.txt "%SQLANY12%\bin32\dbspawn.exe" -f "%SQLANY12%\bin32\dbsrv12.exe" @server1config.txt "%SQLANY12%\bin32\dbspawn.exe" -f "%SQLANY12%\bin32\dbsrv12.exe" @server2config.txt PAUSE TUDO OK
Bloco de código title arbiterconfig.txt -n arbiter -o arbiter\arbiterlog.txt -su sql -x tcpip(port=55500) -xa auth=dJCnj8nUx3Lijoa8;dbn=demo -xf arbiter\arbiterstate.txt #-su sql: para executar dbstop no utility_db (banco de dados fantasma). #-xa <senha>: senha para todos os bancos participantes do esquema (todos devem ter a mesma senha). #-fx: designar um arquivo texto de controle do estado do banco (read-only). # dbn=demo (deve ser igual ao nome do banco de dados utilizado pelo servidor primario e secundário)
Bloco de código title server1config.txt # server options -n server1 -o server1\server1log.txt -su sql -x tcpip(port=55501;dobroadcast=no) -xf server1\server1state.txt # dobroadcast=no é usado somente para garantir que ele não irá responder a outros broadcasts ou participar de outros esquemas HAs # database options server1\demo.db -sm secondarydemo -sn primarydemo -xp partner=(eng=server2;links=tcpip(host=localhost;port=55502;timeout=1)); mode=sync;auth=dJCnj8nUx3Lijoa8;arbiter=(eng=arbiter;links=tcpip(host=localhost;port=55500;timeout=1)) #-sm: nome logico da engine para quando se quiser conectar no banco secundário (read-only). #-sn: nome logico da engine para quando se quiser conectar no banco primário. # Os nomes são lógicos pois eles decidirão o papel de cada um no esquema conforme a disponibilidade de cada banco. #-xp: *partner:* para se designar o parceiro (na configuração do banco primário é o secundário e vice-versa), # *mode*=synch para só liberar a transação no servidor primário quando o secundário receber # e aplicar a transação de forma síncrona, *auth*= deve ser igual para todos os bancos do esquema HA, # *arbiter:*= designar quem irá ser o árbitro do esquema.
A configuração acima é idêntica à do servidor primário somente trocando o server1 pelo server2 e as portas de comunicação.Bloco de código title serverconfig2.txt # server options -n server2 -o server2\server2log.txt -su sql -x tcpip(port=55502;dobroadcast=no) -xf server2\server2state.txt # database options server2\demo.db -sm secondarydemo -sn primarydemo -xp partner=(eng=server1;links=tcpip(host=localhost;port=55501;timeout=1)); mode=sync;auth=dJCnj8nUx3Lijoa8;arbiter=(eng=arbiter;links=tcpip(host=localhost;port=55500;timeout=1))
Principais participantes:- Arbitro: A função principal do árbitro é decidir quando um banco deverá ser primário e outro secundário e quando alterar estas configurações num caso de parada de algum banco ou de falha de comunicação entre eles. Este banco não detém uma cópia do banco principal. Ele pode participar de mais de um esquema de HA (com outros bancos). Assim, suas funções se manifestam em dois momentos principais:
- Inicialização do ambiente: Pois ele pode resolver quem irá assumir o banco de dados primário. Quando não se tem o árbitro, pode ocorrer de um servidor ter tido transações quando era o servidor principal e, por algum problema de comunicação, não ter enviado estas transações ao servidor secundário. Quando este (secundário) for iniciado sozinho (sem o primário), aceitará transações e inviabilizará o esquema HA com o primário pois ambos agora possuem transações que necessitam ser enviadas para o outro. Quando existe um árbitro, isso não ocorre pois ele não permitiria ao banco secundário iniciar e receber conexões. Segue o passo a passo do processo de inicialização do esquema normal:
- O árbitro espera pelo servidor primário (server1) e secundário (server2).
- Server 1 procura pelo árbitro ou Server 2.
- Server 1 se conecta com o árbitro.
- Server 1 negocia com o árbitro para ser o servidor primário.
- Árbitro e Server 1 concordam que o Server 1 deve ser o servidor primário.
- Server 1 passa a aceitar conexões.
- Server 2 procura pelo Server1 e pelo Árbitro.
- Server 2 se conecta ao Árbitro e Server 1.
- Server 2 requisita ser o servidor primário. Ele não recebe esta atribuição pois o Server 1 já é o servidor primário portanto fica como secundário (read-only) aguardando as transações vindas do servidor primário (Server 1).
- Server 1 envia as transações para o Server 2.
- Falha de Comunicação: No caso de falha de comunicação entre os servidores (porém com seus bancos rodando normalmente), o árbitro caso consiga se comunicar com ambos, permite ao esquema continuar funcionando normalmente. Ele serve como um ponto de redundância no tocante à comunicação entre os servidores.
- Inicialização do ambiente: Pois ele pode resolver quem irá assumir o banco de dados primário. Quando não se tem o árbitro, pode ocorrer de um servidor ter tido transações quando era o servidor principal e, por algum problema de comunicação, não ter enviado estas transações ao servidor secundário. Quando este (secundário) for iniciado sozinho (sem o primário), aceitará transações e inviabilizará o esquema HA com o primário pois ambos agora possuem transações que necessitam ser enviadas para o outro. Quando existe um árbitro, isso não ocorre pois ele não permitiria ao banco secundário iniciar e receber conexões. Segue o passo a passo do processo de inicialização do esquema normal:
- Server Primário: Serve como o banco que irá receber as transações enviando-as para o servidor secundário para espelhamento.
- Server Secundário: Serve como um espelho do banco primário. Sobe como um banco read-only recebendo as transações enviadas pelo servidor primário.
- Arbitro: A função principal do árbitro é decidir quando um banco deverá ser primário e outro secundário e quando alterar estas configurações num caso de parada de algum banco ou de falha de comunicação entre eles. Este banco não detém uma cópia do banco principal. Ele pode participar de mais de um esquema de HA (com outros bancos). Assim, suas funções se manifestam em dois momentos principais:
- Abrindo janelas ISQL para mostrar algumas características do esquema.
A partir daqui, devemos ter 5 janelas abertas: 3 dos servidores (arbiter, server1 e server2) e duas janelas de ISQL (conexao1 conectado ao servidor primário e conexao2 conectado ao servidor secundário).Bloco de código title 3_connect_HA.bat SETLOCAL SET MORE=DBN=demo;UID=dba;PWD=sql;LINKS=TCPIP(HOST=localhost:55501,localhost:55502;DOBROADCAST=NONE) "%SQLANY12%\bin32\dbisql.com" -c "ENG=primarydemo;CON=Conexao1;%MORE%" PAUSE Aguardar conexao para continuar.... "%SQLANY12%\bin32\dbisql.com" -c "ENG=secondarydemo;CON=Conexao2;%MORE%" PAUSE Tudo OK
Podemos executar a procedure which_database criada no passo 1 para retornar algumas propriedades do servidor conectado.
Notar o nome e papel de cada banco no esquema. O server1 é considerado como primário (atualizável) e o server2 como secundário (read-only).
Vamos agora fazer um UPDATE no servidor primário (server1) e ver que o servidor secundário recebe imediatamente as alterações.
Por fim, vamos dar um shutdown no servidor primário e ver que o servidor secundário passa a ficar atualizável e receber todas as requisições sem termos de alterar a configuração de conexão.
Note que podemos nos conectar ao banco secundário que ficou atualizável com a mesma string de conexão do servidor primário. Além disso, o ISQL tem um esquema especial que não necessita nem solicitar a reconexão.Nota A regra final para que o esquema de HA funcione e os usuários tenham conexão com um banco é que existam pelo menos dois servidores ativos e se comunicando no esquema. Pode ser o servidor primário e o secundário, o servidor primário e o árbitro ou o servidor secundário e o árbitro porém devem existir dois servidores e que um enxergue o outro, isto é, seja possível uma comunicação entre eles.
...
Bloco de código | ||
---|---|---|
| ||
SELECT Number AS connection_number, CONNECTION_PROPERTY ( 'Name', Number ) AS connection_name, CONNECTION_PROPERTY ( 'Userid', Number ) AS user_id, CAST ( Value AS NUMERIC ( 30, 2 ) ) AS approximate_cpu_time FROM sa_conn_properties() WHERE PropName = 'ApproximateCPUTime' ORDER BY approximate_cpu_time DESC; # connection_number connection_name user_id approximate_cpu_time # ================= ================ ========== ==================== # 33 SQL_DBC_5071260 LongRunner 97.20 # 35 SQL_DBC_3aa4a78 DBA 2.09 # 12 Sybase Central 1 DBA 1.09 # 37 SQL_DBC_504b0f0 LotsOfLocks 0.00 |
...
- Criar um banco novo.
Bloco de código title cria_banco_teste.sql dbinit.exe banco.db dbspawn.exe -f dbeng11.exe -o banco_msg.txt -oe banco_fatal.txt -x tcpip banco.db dbisql.com -c "ENG=banco;DBN=banco;UID=dba;PWD=sql
- Iniciar Sybase Central e se conectar ao banco.
- Clicar em Work with server "Banco".
- Clicar com o botão da direita do mouse em cima do ícone que representa o banco (à direita).
- Irá aparecer a tela abaixo. Clique em Next.
- Escolha a opção Custom no nível de detalhe do tracing. Clique em Next.
- Aperte o botão New para definir um novo nível de detalhe.
- Escolha a opção Database no scope, plans_with_statistics no tracing type e none nas conditions. Clique no botão Add. Você pode adicionar outros planos de tracing porém o mais importante é o plans_with statistics.
- Clique em Next.
- Escolha a opção Create a new tracing database com o nome banco_trace. Preencha o nome do usuário e senha com dba e sql respectivamente.
- Marque o checkbox "Start database in this server".
- Clique em Create database.
- Quando o comando voltar à tela, clique em Next.
- O banco foi iniciado no mesmo server que estamos monitorando (banco). Numa situação normal (em produção), é interessante não sobrecarregarmos demais o servidor iniciando o banco num outro computador. Assim, num ambiente de produção, desmarque o checkbox "Start database in this server" e digite o código abaixo.
Bloco de código title inicia_trace.sql dbspawn.exe -f dbeng11.exe -o banco_trace_msg.txt -oe banco_trace_fatal.txt -x tcpip banco_trace.db dbisql.com -c "ENG=tracing;DBN=tracing;UID=dba;PWD=sql"
- Clique no botão Finish.
- A partir daqui, o banco monitorado já está enviando dados para o banco de tracing.
- Vamos gerar algumas instruções para ver o que pode ser monitorado. Normalmente, este passo é desnecessário num ambiente de produção já que os próprios usuários estarão gerando estas instruções.
- Abra o dbISQL e digite as instruções abaixo (pelo menos duas vezes para que o cache seja inicializado).
Bloco de código title instrucao_monitorar.sql SELECT 'query 1', SYSTABCOL.domain_id, COUNT(*) FROM SYSTABCOL CROSS JOIN SYSTAB CROSS JOIN SYSGROUP GROUP BY SYSTABCOL.domain_id ORDER BY SYSTABCOL.domain_id;
- Se a instrução acima não foi suficientemente lenta, podemos executar uma outra mais lenta.
Bloco de código SELECT 'query 2', SYSTABCOL.domain_id, COUNT(*) FROM SYSTABCOL CROSS JOIN SYSTABCOL AS b CROSS JOIN SYSGROUP GROUP BY SYSTABCOL.domain_id ORDER BY SYSTABCOL.domain_id;
- Após a instrução ter sido executada pelo menos duas vezes, execute o Sybase Central e pare o Trace clicando com o botão da direita em cima do banco Banco escolhendo a opção Stop tracing with Save
- Clique no menu "Application Profiling" escolhendo a opção Open Analysis Or Connect To Tracing Database.
- Escolha a opção In a tracing database e clique no botão Open.
- Os dados devem ser preenchidos em relação ao banco tracing, isto é, ao banco criado especificamente para armazenar os dados de tracing. Assim, preencha o campo banco com banco_trace e o servidor banco (caso esteja executando o banco de tracing no banco criado acima - em produção deve-se ter um servidor específico para o tracing). Digite nome e senha (dba/sql) e clique em Connect.
- O painel de Tracing deve aparecer no Sybase Central (abaixo). Tome nota do Logging session Id para que possamos salvar os dados deste tracing mais abaixo.
- Clique na aba Database Tracing Data para ver os últimos comandos executados.
- Existem inúmeras outras opções para ver mais detalhes da query (clicando na aba Details numa determinada query) e, em seguida, clicando com o botão da direita em cima dos detalhes. O principal ponto a ser observado é que podemos analisar as queries mais demoradas para ver se podemos melhorá-las e, principalmente, podemos ver onde o servidor está gastando mais tempo de CPU.
- Para enviar estes dados para análise (de uma query), necessitamos do Log Session Id (anotado num passo anterior) e do request ID da query (abaixo - selecione a query, clique na aba Details e, em seguida, com o botão da direita do mouse, escolha a opção View more SQL statement details for the selected Statement.
- Com os dois dados acima, execute a instrução abaixo no dbISQL ajustando os dois parâmetros conforme o valor dos mesmos colhidos anteriormente.
Bloco de código title salva_plano.sql UNLOAD SELECT sa_diagnostic_cursor.plan_xml FROM dbo.sa_diagnostic_cursor INNER JOIN dbo.sa_diagnostic_request ON sa_diagnostic_request.logging_session_id = sa_diagnostic_cursor.logging_session_id AND sa_diagnostic_request.cursor_id = sa_diagnostic_cursor.cursor_id WHERE sa_diagnostic_request.logging_session_id = <logging session id> AND sa_diagnostic_request.request_id = <request Id> TO 'c:\temp\<nome desejado>.saplan' DELIMITED BY '' ESCAPES OFF HEXADECIMAL OFF QUOTES OFF;
- Com o plano salvo num arquivo, basta abri-lo através do dbISQL em outra máquina.
...
Bloco de código | ||
---|---|---|
| ||
select connection_property('ReqStatus', sci.Number) as ReqStatus, usu.usuario, sci.* from sa_conn_info() sci INNER JOIN usuarioConexaoBanco usu on (sci.number = usu.IdConexao) WHERE USUARIO = <NOME USUARIO INVESTIGADO> |
O resultado segue abaixo.
Note que, como não existe nenhuma informação nas informações vindas da conexão (pois todas se logam como DBA), o Sispetro registra, quando o usuário faz o login, na tabela UsuarioConexaoBanco o número da conexão (Number) juntamente com o nome do usuário sempre sobreescrevendo o último valor apresentado.
Podemos ver acima que o usuário Futura não está realizando nenhum trabalho no banco (pois a coluna ReqStatus está com o valor Idle e a coluna LastReqTime está com uma data/hora muito distante de quando foi executado o comando). Além disso, ele não está travado por nenhuma outra conexão (apareceria o número da conexão na coluna BlockedOn).
Uma outra alternativa é utilizar o FoxHound sendo que antes devemos pesquisar o número da conexão (ou números pois o Sispetro utiliza 2 conexões ativas).
Um outro SELECT interessante para se ter uma noção de quais usuários estão consumindo mais CPU do banco de forma rápida é o seguinte:
...
- Sem FoxHound.
Bloco de código title usuario_travado.sql SELECT NUMBER(*) AS #, Name, usu.usuario, UserId, Number, BlockedOn FROM sa_conn_info() as con INNER JOIN usuarioConexaoBanco usu ON ( con.Number = usu.IdConexao ) WHERE con.BlockedOn <> 0 OR EXISTS( SELECT * FROM sa_conn_info() as con2 WHERE con2.BlockedOn = con.BlockedOn ) ORDER BY BlockedOn, Name, UserId, number;
- Com FoxHound
O mais importante a saber sobre travamento é que eles irão ocorrer (pois existem recursos compartilhados que serão atualizados simultaneamente) e serão ou de baixa frequência com uma duração de até 10 segundos ou com uma frequencia que o usuário não irá notar (serão frequentes porém de curta duração).
...