Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

...

Um arquivo de log (também chamado de foward log) armazena todas as alterações realizadas no banco a partir do momento em que o mesmo foi criado. Ele serve basicamente como um elemento de segurança para falhas no banco de dados principal e como um instrumento imprescindível para realização de sincronização entre bancos.
É fundamental que o banco de dados de produção tenha um log de transações ativo e numa unidade de disco diferente da do banco principal pois isso irá permitir restaurar o banco no caso de uma pane do banco de dados principal além de torná-lo mais rápido (pois se o banco não tem log, é obrigado a escrever todas as suas transações a partir do COMMIT no banco principal o que leva a uma sobrecarga no mesmo).
Este arquivo de log pode ser visualizado a partir do aplicativo dbtran. Este aplicativo transforma o arquivo de log num arquivo de texto contendo todos os comandos e transações salvas nele.

Bloco de código

dbtran scgwin.log
# conteúdo jogado para scgwin.sql
type scgwin.sql
--CHECKPOINT-0000-0030248238-2012-01-10 00:04
--CHECKPOINT-0000-0030248264-2012-01-10 00:05
--CONNECT-1011-0030248290-DBA-2012-01-10 00:39
--BEGIN TRANSACTION-1011-0030248301
BEGIN TRANSACTION
go
--UPDATE-1011-0030249452
UPDATE DBA.Deposito
   SET Descricao='BASE DEMO'
 WHERE CodDep='1'
go
--COMMIT-1011-0030249473
COMMIT WORK
go

...

  1. Imediatamente após executarmos o comando (e antes do SQLAnyhere executá-lo), o banco copia numa página do cache o dado antes de ser modificado (A) e cria uma página no Checkpoint Log (interno ao arquivo do banco) com o mesmo conteúdo (A).
  2. Então, ele altera o cache para conter a cópia mais recente do dado (B) e copia o comando executado para o Transaction Log.
    Image Modified

    Nota

    Note que, neste momento, o banco não possui o dado alterado (somente o cache e, como segurança, o Transaction Log).

  3. Ao realizar o checkpoint (procedimento em que todas as páginas "sujas" do cache são copiadas para o banco - procedimento este ocorre frequentemente durante o funcionamento normal do banco e sempre antes do mesmo ser finalizado), o banco copia o dado para seu local no banco de dados e limpa o Checkpoint Log.
Experiência
  • 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).
    1. Windows: Executar Ctrl+Alt+Del, ir para a aba Processos, selecionar o processo DbSrv12 e clicar em Finalizar Processo.
    2. 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.

...

Image Added
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).

...

Como foi dito anteriormente, um banco de dados é composto por pages (páginas) de um determinado tamanho (que pode variar de 1K, 2K, 4K, 8K, 16K, 32K - o Sispetro utiliza páginas de 4K). Estas páginas são utilizadas para salvar organizadamente tabelas, índices e até a estrutura do cache (em memória). Seu tamanho é definido no momento da criação do banco de dados e afeta desde o uso correto do cache até o desperdício de espaço em disco passando por velocidade de leitura de dados e outros aspectos do dia a dia do banco. Existem alguns fatores para se chegar ao tamanho correto da pagina porém o mais importante com relação à administração do banco é tentar deixar o arquivo do banco (.db) desfragmentado (o mais contíguo possível no sistema de arquivos).
Cada vez que o número de páginas livre fica próximo de acabar, o banco aumenta seu tamanho físico, aumentando seu número de páginas livres.
Nossa tarefa aqui é tentar antecipar isso aumentando o tamanho do arquivo .db e depois desfragmentando o disco de forma que o arquivo .db fique num espaço contíguo (ou o mais contíguo possível).

  • 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.

...

Depois que é feita a migração para o Sybase 12 não será mais possível com o mesmo scgwin.db voltara para a versão do Sybase 7.
Caso seja necessário voltar para a versão anterior, disponibilizaremos um sugestão, para isso você precisara de uma versão do banco de dados nas duas versões.

ETAPA 1

Para que o procedimento seja feito é necessário que se faça o Unload do banco na versão do Sybase 7, da estrutura e dos dados.
Abra o programa scview.exe que poderá ser encontrado em "C:\Arquivos de programas\Sybase\Shared\Sybase Central 3.2\win32\scview.exe" e conecte no banco que está na versão 7 do Sybase.

  • Clique com o botão direito em cima do banco de dados e escola a opção Unload
    Image Modified
  • Em seguida aparecerá uma tela com informações do banco. É só avançar.
    Image Modified
  • Na próxima tela é o local onde será salvo o script.sql que o procedimento criará.
    Image Modified
  • Escolher qual será o Unload a ser feito. Neste caso iremos escolher estrutura e dados.
    Image Modified
  • Escolha do local onde serão salvos os dados extraídos.
    Image Modified
  • Nesta tela informe 5 no campo de dependências.
    Image Modified

    Aviso

    Não digite o número 5, aumente as dependências com a setinha.

  • Resumo da configuração do Unload.
    Image Modified
    Ao termino deste procedimento foi criado um script e vários arquivos .dat contendo os dados na verssão 7.
ETAPA 2

Para que o procedimento seja feito é necessário que se faça o Unload do banco na versão do Sybase 12 da estrutura e dos dados.
Abra o programa Sybase Central 6.1.0 e escolha o ODBC do banco de dados na versão do Sybase 12.

  • Clique com o botão direito em cima do banco de dados e escola a opção Unload
    Image Modified
  • Será apresentado uma tela de boas vindas
    Image Modified
  • Escolha o banco que será utilizado
    Image Modified
  • Escolha de como será a extração dos dados. Neste caso escolheremos a opção para arquivos
    Image Modified
  • Escolha do local onde será salvo o script RELOAD.sql
    Image Modified
  • Escolha do que será feito o Unload. Neste caso escolheremos estrutura e dados.
    Image Modified
  • Deixaremos este passo com a opção de todos os objetos
    Image Modified
  • Escolheremos agora onde serão salvos os arquivos .dat contendo os dados.
    Image Modified
  • Neste momento será apresentado em forma de código o resultado das configurações anteriores.

    Bloco de código
    dbunload \-v \-c "DSN=scgwin12" \-r "C:\dado12\reload.sql" \-ii "C:\dados12\unload"
  • Ao termino deste procedimento foi criado um script e vários arquivos .dat contendo os dados na verssão 12.
    Image Modified
ETAPA 3

Nesta fase iremos trabalhar com os scripts gerados nas etapas anteriores.
Criaremos três scripts:

  • Estrutura.sql
  • Dados.sql
  • Geral.sql
    Script 1 – Estrutura

    Para criação deste script pegaremos do Unload do banco de dados do Sybase 7 somente a parte da estrutura.
    Do início do arquivo até antes da parte onde começa os dados, marcado com a palavra:

    Bloco de código
    //-------------------------------------------------
    //-- Reload data
    //-------------------------------------------------

    Será necessário alterar no script a senha do Usuário DBA

    para "mara97"

    para  (senha default) caso não possua, solicitar ao suporte técnico futura.
    Salve este script como ESTRUTURA.SQL

    Script 2 – Dados
    Pegaremos do script do Unload do banco de dados Sybase 12 somente a parte de INPUT de dados. Tudo que tiver dentro do comentário do texto "Reload data"“Reload data”
    Nesta etapa serão necessários alguns ajustes neste script para que ele seja compatível com a estrutura criada anteriormente.
    Iremos documentar algumas linhas, são elas:

    Observe que utilizamos o caminho dos dados extraídos do Unload do banco na versão Sybase 12.
    Salve este script como DADOS.SQL
    Script 3 – Configurações gerais
    Para criação deste script pegaremos do Unload do banco de dados em Sybase 7 tudo que for posterior a parte de dados.
    Ao final deste script a apague o último "work" e "go"“work” e “go”, pois não será mais necessário.
    Salve este script como GERAL.SQL
ETAPA 4
  •  Crie no Sybase Central 4 um banco de dados "scgwin.db" sem estrutura. Em seguida crie um ODBC para o mesmo.
  • Abra o dbisqlc.exe conectando ao banco vazio criado anteriormente e rode os script na mesma seqüência da criação acima.
    Este procedimento poderá demora um pouco dependendo do tamanho do banco de dados.

...

É a forma mais simples de se ter uma cópia do banco de dados em segurança. Todos os dados são copiados para um outro local criando uma cópia idêntica do banco de dados num determinado ponto no tempo.
Podemos executar o backup tanto no servidor diretamente quanto de uma outra máquina qualquer.

  • Usando a linha de comando

    Bloco de código
    
    dbbackup -c "SERVER=scgwin12;UID=DBA;PWD=mara97"(senha default) caso não possua, solicitar ao suporte técnico futura. "c:\backup"
    # opções
    #   Host=192.168.0.1
    #   DBN=scgwin.db
    
  • Usando o ISQL

    Bloco de código
    
    BACKUP DATABASE DIRECTORY 'C:\\backup';
    
    Nota

    Tanto o arquivo principal (.DB) quanto o arquivo de log de transações (.LOG) são copiados e necessários no caso de uma restauração.

...

Nota

Quando implementamos este tipo de esquema de backup, normalmente realizamos o truncamento do log já no primeiro backup full utilizando a cláusula -x no comando ou adicionando TRANSACTION LOG TRUNCATE

Bloco de código

dbbackup -c "SERVER=scgwin12;UID=DBA;PWD=mara97 (senha default) caso não possua, solicitar ao suporte técnico futura." -x -t  "c:\backup"
# opções
#   Host=192.168.0.1
#   DBN=scgwin.db

Aqui devemos tomar um cuidado adicional pois como devemos preservar todos os logs, não podemos simplesmente sobreescrever os logs anteriores. Caso o comando acima seja executado novamente, ele irá sobreescrever o log inutilizando todo o esquema planejado. Para tanto, devemos adicionar mais duas cláusulas importantes:

    • -r: para renomear o log de transações (esquema DDMMYYxx sendo xx igual a AA,AB e assim por diante) da cópia.
    • -n: para que o nome do arquivo copiado seja o mesmo do arquivo renomeado no servidor.

      Bloco de código
    • dbbackup -c "SERVER=scgwin12;UID=DBA;PWD=
  • mara97
    •  (senha default) caso não possua, solicitar ao suporte técnico futura." -x -t -r -n "c:\backup"
      # opções
      #   Host=192.168.0.1
      #   DBN=scgwin.db
      
  • Usando o ISQL

    Bloco de código
    
    BACKUP DATABASE DIRECTORY TRANSACTION LOG ONLY LOG TRANSACTION LOG RENAME MATCH 'C:\\backup';
    

...

Nota

Quando implementamos este tipo de esquema de backup, normalmente realizamos o truncamento do log já no primeiro backup full utilizando a cláusula -x no comando ou adicionando TRANSACTION LOG TRUNCATE

Bloco de código

dbbackup -c "SERVER=scgwin12;UID=DBA;PWD=mara97" (senha default) caso não possua, solicitar ao suporte técnico futura. -t -y "c:\backup"
# opções
#   Host=192.168.0.1
#   DBN=scgwin.db
# A clausula -y serve para que o comando não pergunte se deseja sobreescrever o arquivo pois o nome será o mesmo do arquivo de log já copiado anteriormente
  • Usando o ISQL

    Bloco de código
    
    BACKUP DATABASE DIRECTORY TRANSACTION LOG ONLY LOG 'C:\\backup';
    
    Nota

    Importante: Em todos os comandos de backup acima (full, incremental ou diferencial), utilizando a cláusula *-s faz com que o comando de backup seja executado no servidor (como se estivéssemos executando o mesmo comando via ISQL). Isso, em testes que fiz, faz com que o tempo de backup seja reduzido em quase 150%. Lembre-se somente que o diretório de backup nestes casos deverão ser especificados a partir do servidor, isto é, o diretório "c:\backup" dos exemplos acima serão criados ou utilizados no servidor independente do computador onde está sendo executado o comando.

...

O backup live é um tipo diferente de backup. Na realidade, é um procedimento que coloca no ar (num outro computador normalmente), um processo que irá espelhar o arquivo de log de transações.
Ele serve para que tenhamos sempre uma cópia atualizada do arquivo de transações atual num outro local para que possamos, num processo de restauração, recuperar os dados até o exato momento da falha. Caso este procedimento não seja executado, corremos o risco de, por exemplo, ter uma falha no disco rígido onde está o arquivo de log de transações e, neste caso, conseguiríamos recuperar os dados até o último backup deste arquivo de log de transações (já que não conseguiríamos aplicar o log de produção na última etapa de recuperação).

Bloco de código

dbbackup -l scgwin.log -c "SERVER=scgwin12;UID=DBA;PWD=mara97" (senha default) caso não possua, solicitar ao suporte técnico futura. c:\backup
DbValid

Um procedimento fundamental para aumentar a segurança do procedimento de backup é testá-lo, isto é, saber se o arquivo principal do banco copiado através do backup full está integro. Isso pode ser feito de duas formas:

  1. Restaurando o backup: É recomendável realizar o procedimento de restauração uma vez por semana ou mês pelo menos para que, primeiro, possamos treinar o procedimento sem a pressão de um ambiente de produção parado no nosso pescoço e, segundo, verificar se os arquivos do backup (por amostragem) estão íntegros. Esta etapa está descrita mais adiante.
  2. Utilizar o dbvalid: O Dbvalid é um utilitário que serve para validar as estruturas do banco de dados. Normalmente, utilizamos ele para validar um banco de dados e, neste caso, podemos acrescentar este passo logo após a realização do backup full de forma a validá-lo toda vez que o mesmo for executado.

    Aviso

    Importante: É fundamental entender que, sempre que executamos o dbValid sobre um backup, ele altera o banco de dados pois deve subir o banco para verificar as tabelas e, no procedimento de subir o banco, o checkpoint log é restaurado e o transaction log aplicado (mesmo que não aplicássemos o transaction log, o checkpoint log é restaurado) fazendo com que qualquer tentativa futura de se utilizar o banco junto com backups incrementais posteriormente se torne mal sucedida. Para usar o dbvalid portanto, devemos utilizá-lo sobre uma cópia do backup.

    Nota

    Nunca tente executar o dbvalid num banco com outros usuários conectados e com permissão de alteração pois ele dará resultados espúrios. Sempre execute o dbvalid subindo o banco com a cláusula -r (read-only) porém atenção: logo após realizar um backup, é fundamental subir a cópia do backup sem a cláusula -r aplicando o log (com a cláusula -ad) e depois subindo o banco normalmente com a cláusula -r pois o banco necessita se alterar quando sobe pela primeira vez após a realização de um backup.

...

Vamos juntar o que foi exposto até o momento e realizar através de comandos batch os seguintes procedimentos:

  1. Criar banco de dados.

    Bloco de código
    title02_run_dbinit12.bat
    
    "%SQLANY12%\bin32\dbinit.exe" ddd12.db
    
  2. Subir banco de dados criado.

    Bloco de código
    title03_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
    
  3. Preencher banco de dados com algumas tabelas e dados.

    Bloco de código
    title04_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
    

    sendo que o conteúdo do script 04s_script_to_create_table_and_event.sql contém:

    Bloco de código
    title04s_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;
    
  4. Criar diretório e realizar backup full para ele.
  5. Criar diretório temporário e copiar backup (somente o arquivo .db) para ele.
  6. Subir a cópia do banco aplicando o log de transações validando o backup.
  7. 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
    title05_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.

  8. Realizar backup incremental.
  9. Validar backup incremental subindo a cópia do backup realizado no passo anterior.

    Bloco de código
    title07_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.

  10. Mostrar o conteúdo do banco de dados a cada backup incremental.

    Bloco de código
    title06_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
    
  11. Parar o banco simulando um problema grave.

    Bloco de código
    title08_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.
    
  12. 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:
    1. Log do backup full que iremos utilizar como ponto de partida da restauração.
    2. Logs incrementais a partir de então.
    3. Log do banco de produção (utilizando a cláusula -a).

      Bloco de código
      title09_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
      
  13. Subir o banco e mostrar o seu conteúdo executando novamente as batches 03_run_dbspawn_dbsrv12.bat e 06_display_table.bat.

...

Nota

Crédito para o site http://sqlanywhere.blogspot.com ao qual tomei por base os meus testes e esta demonstração.

  1. 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
    title1_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
    titleadditional_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
    
  2. 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
    title2_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
    titlearbiterconfig.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
    titleserver1config.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.
    
    Bloco de código
    titleserverconfig2.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))
    

    A configuração acima é idêntica à do servidor primário somente trocando o server1 pelo server2 e as portas de comunicação.
    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:
        1. O árbitro espera pelo servidor primário (server1) e secundário (server2).
        2. Server 1 procura pelo árbitro ou Server 2.
        3. Server 1 se conecta com o árbitro.
        4. Server 1 negocia com o árbitro para ser o servidor primário.
        5. Árbitro e Server 1 concordam que o Server 1 deve ser o servidor primário.
        6. Server 1 passa a aceitar conexões.
        7. Server 2 procura pelo Server1 e pelo Árbitro.
        8. Server 2 se conecta ao Árbitro e Server 1.
        9. 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).
        10. 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.
    • 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.
  3. Abrindo janelas ISQL para mostrar algumas características do esquema.

    Bloco de código
    title3_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
    

    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).
    Image Modified
    Podemos executar a procedure which_database criada no passo 1 para retornar algumas propriedades do servidor conectado.
    Image Modified
    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.
    Image Modified
    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.
    Image Modified
    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
titleshow_counters.sql

CREATE VIEW v_Show_Counters AS
SELECT CAST( STRING (
           '1.Servidor ',
		   PROPERTY( 'Name' ) )
		   as VARCHAR(200) )        AS nome_propriedade,
		Propname					AS nome,
		Value						AS Valor,
		PropDescription				AS Descricao
	FROM sa_eng_properties()
	WHERE ISNUMERIC( valor ) = 1
UNION ALL
SELECT CAST( STRING (
           '2.Banco Dados ',
		   DB_NAME( Number) )
		   as VARCHAR(200) )        AS nome_propriedade,
		Propname					AS nome,
		Value						AS Valor,
		PropDescription				AS Descricao
	FROM sa_db_properties()
	WHERE ISNUMERIC( valor ) = 1
ORDER BY 1,2;
SELECT * FROM V_SHOW_COUNTERS;

...

Bloco de código
titlecpu_por_conexao.sql

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

...

O banco "estar lento" pode representar uma série de pequenos problemas que, somados, podem fazer o banco ficar lento. Devemos lembrar que o banco roda em cima de um sistema operacional, que é executado em cima de um hardware e se comunica com outros computadores através de uma rede local portanto qualquer um destes pontos pode estar contribuindo para a percepção de lentidão. Outro ponto a se considerar é: lento em relação a que? É importante termos parâmetros para podermos comparar, por exemplo, o número de commits por segundo de um servidor para ver e comparar em dois momentos diferentes.
Mesmo isso pode não dar a noção exata do motivo da lentidão pois, como foi falado antes, existem outros fatores que não foram mencionados aqui que influenciam a performance sentida pelo usuário, portanto, é fundamental conhecer o seu ambiente. Todos os pontos que se interligam (rede, switch, servidor, etc) devem ser monitorados através de poucos porém confiáveis parâmetros de forma que a investigação sobre a lentidão seja bem sucedida (e inclusive investimentos possam ser realizados baseados na realidade dos números e não em achismos).
Isso leva tempo porém a mesma mecânica que fazemos com o banco deve ser ampliada para o servidor em si, para a rede local de forma a monitorar todos os pontos entre o usuário e o servidor.
Iremos demonstrar aqui como funciona uma outra ferramenta importante neste gerenciamento que é o tracing.
Ele permite capturar todos os comandos realizados pelo banco ordenando-os e analisando-os de forma que se tenha uma noção do que o servidor está fazendo. Estas informações podem, inclusive, serem transmitidas para outra pessoa analisar (setor de suporte, por exemplo).
Em seguida, vamos dar algumas dicas de otimização do banco.

Tracing
  1. Criar um banco novo.

    Bloco de código
    titlecria_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
    
  2. Iniciar Sybase Central e se conectar ao banco.
  3. Clicar em Work with server "Banco".
  4. Clicar com o botão da direita do mouse em cima do ícone que representa o banco (à direita).
  5. Irá aparecer a tela abaixo. Clique em Next.
  6. Escolha a opção Custom no nível de detalhe do tracing. Clique em Next.
  7. Aperte o botão New para definir um novo nível de detalhe.
  8. 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.
  9. Clique em Next.
  10. 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.
  11. Marque o checkbox "Start database in this server".
  12. Clique em Create database.
  13. Quando o comando voltar à tela, clique em Next.
  14. 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
    titleinicia_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"
    
  15. Clique no botão Finish.
  16. A partir daqui, o banco monitorado já está enviando dados para o banco de tracing.
  17. 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.
  18. Abra o dbISQL e digite as instruções abaixo (pelo menos duas vezes para que o cache seja inicializado).

    Bloco de código
    titleinstrucao_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;
    
  19. 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;
    
  20. 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
  21. Clique no menu "Application Profiling" escolhendo a opção Open Analysis Or Connect To Tracing Database.
  22. Escolha a opção In a tracing database e clique no botão Open.
  23. 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.
  24. 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.
  25. Clique na aba Database Tracing Data para ver os últimos comandos executados.
  26. 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.
  27. 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.

  28. 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
    titlesalva_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;
    
    
  29. Com o plano salvo num arquivo, basta abri-lo através do dbISQL em outra máquina.
Otimização Banco
  • Desfragmentação Arquivo .DB. Este é um passo que deve ser executado no Sistema operacional, isto é, após uma análise dos fragmentos no arquivo .db, deve-se utilizar um desfragmentador via sistema operacional para diminuir o número de fragmentos do arquivo .db. Para saber quantos fragmentos ele possui, existe uma propriedade do banco chamada DbFileFragments.

    Bloco de código
    
    SELECT DB_PROPERTY('DBFILEFRAGMENTS');
    
    Nota

    Alguns desfragmentadores trabalham melhor com arquivos criados do que com espaço livre, isto é, ao executar o desfragmentador antes de se criar o banco, o banco acaba fragmentado desnecessariamente. Assim, a ordem correta é criar o banco e depois desfragmentar o disco rígido.

  • Aumentar espaço DBSPACE antes do banco fazer isso automaticamente. Pode-se programar uma tarefa noturna que verifique o espaço livre (pages free) do banco e, caso atinja um limite mínimo (por exemplo, 10%), aumente em 20% (por exemplo) o número de páginas livre. É importante fazer isso antes do banco fazer automaticamente pois podemos programar este crescimento num horário fora do pico de uso. Além disso, podemos automatizar a execução da desfragmentação do SO em seguida.

    Bloco de código
    
    ALTER DBSPACE SYSTEM ADD 100MB;
    
  • Fragmentação Tabelas. Existe uma instrução para se verificar a fragmentação das tabelas dentro do banco.

    Bloco de código
    
    CALL sa_table_fragmentation;
    

    Image Modified
    Um valor 1,5 no campo Segs_per_Row por exemplo significa que 50% das linhas estão fragmentadas. O número ideal é 1.
    Para reorganizar uma tabela desfragmentando-a, existe o comando REORGANIZE TABLE.

    Bloco de código
    
    REORGANIZE TABLE <nome_tabela>;
    
  • Fragmentação Índices. Os índices podem ser reorganizados utilizando-se o mesmo comando acima (REORGANIZE TABLE) especificando-se o nome do índice. Para saber quais índices podem ser otimizados, utilize o seguinte comando.

    Bloco de código
    titleverifica_plano_index.sql
    
    SELECT * FROM SA_INDEX_LEVELS() ORDER BY SA_INDEX_LEVELS.TABLENAME, SA_INDEX_LEVELS.INDEXNAME;
    

    O ideal é termos o campo Level igual a 1 ou 2. Pode ocorrer que este campo seja maior que 2 o que pode significar que o índice é grande ou que o mesmo está fragmentado. Utilizando o comando abaixo nos certificamos que o índice foi otimizado (mesmo que o nível não diminua).

    Bloco de código
    
    REORGANIZE TABLE <nome_tabela> INDEX <nome_indice>;
    
  • Executar UNLOAD/RELOAD. Este procedimento faz com que o banco fique no estado ótimo, com suas tabelas e índices desfragmentados. É um comando que, num banco de produção de 30Gb, leva em torno de 2-5h para ser executado completamente portanto não é um procedimento que possa ser executado diaria ou semanalmente. O ideal é realizarmos este procedimento semestral ou anualmente.
  • Usar cache. Não existe um número mágico. Alguns recomendam 10% do tamanho do banco, outros a maior quantidade de memória disponível no servidor. Sugerimos iniciar com 10% do tamanho do banco ou a maior quantidade disponível (o que for menor) e verificar (através dos parâmetros de performance descritos acima) como o banco se comporta. Em seguida, ir aumentando (se possível) o tamanho do cache voltando a verificar os parâmetros para ver como eles se comportam a fim de obter o valor ótimo.
  • Sempre utilizar o arquivo .log. Conforme foi descrito (tópico: como o dado é gravado), quando um dado é gravado no banco, o custo disso ao banco é mínimo pois ele grava o dado sequencialmente no log de transações (arquivo .log) deixando para o evento CHECKPOINT salvar o dado no local correto. Quando não trabalhamos com o arquivo .log, o banco é obrigado a executar o CHECKPOINT a cada transação dispendendo um esforço enorme quando o número de COMMITS é alto.
  • Colocar o arquivo .db e .log em discos distintos.
  • Utilizar RAID 1+0.

...

Bloco de código
titlelast_req.sql

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:

Bloco de código
titlecpu_por_conexao.sql

SELECT con.Number AS connection_number, CONNECTION_PROPERTY ( 'Name', Number ) AS connection_name, usu.usuario , CONNECTION_PROPERTY ( 'Userid', Number ) AS user_id,
CAST ( Value AS NUMERIC ( 30, 2 ) ) AS approximate_cpu_time
FROM sa_conn_properties() con
INNER JOIN usuarioConexaoBanco usu ON ( con.NUmber = usu.IdConexao )
WHERE PropName = 'ApproximateCPUTime' ORDER BY approximate_cpu_time DESC;

...

Para identificar que o usuário está travado por outro usuário, podemos recorrer ao script abaixo ou ao foxhound novamente.

  • Sem FoxHound.

    Bloco de código
    titleusuario_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;
    

    Image Modified

  • 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).

...