Tópicos a serem abordados
- Recapitulação sobre os tipos de arquivos do SQL Anywhere e sua estrutura.
- Novos recursos do Sybase 12.
- Migração Sybase 7 para Sybase 12.
- Aumentando a velocidade (profiling, logs, tuning geral, cache).
- Usuário está travado. O que fazer?
- Monitoramento do banco.
- Aumentando a disponibilidade. Mirror, Arbitro.
- Teste com diversas estruturas: 32 x 64bits, Windows x Linux, local arquivos.
- Futuro: banco de dados read-only, arquivo morto, novo servidor aplicação.
Recapitulação Estrutura Sybase.
Tipo de arquivos e locais.
O banco de dados é composto por um (ou mais dependendo se existem dbspaces adicionais) arquivo que chamamos de arquivo do banco de dados extensão db e um arquivo de log de transação extensão log.
O arquivo do banco armazena todas as informações de todos os objetos do banco de dados (tabelas, índices, stored procedures, etc). Ele é dividido em pages de tamanho fixo (ao se criar o banco é informado seu tamanho e a única forma de se alterar é recriando o banco) e qualquer estrutura dentro do banco (cache, arquivo de log, checkpoint log, etc) é lida ou gravada individualmente definida por este tamanho de page.
O page size do banco de dados do Sispetro é de 4Kb.
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.
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
Como o dado é gravado.
Para entender melhor como um dado é gravado e como as estruturas acima funcionam, segue uma descrição de como um dado é gravado no banco.
Vamos supor que nós iremos alterar um dado numa determinada tabela (UPDATE de A para B).
- 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).
- 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.
Note que, neste momento, o banco não possui o dado alterado (somente o cache e, como segurança, o Transaction Log).
- 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).
DBSRV12 @<nome arquivo parametro> # scgwin.par: # -c120M # -xNone # -nScgwin12 # scgwin.db
- Executar o ISQL e executar um comando UPDATE.
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.
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.
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.
// jogar para o subdiretorio backup COPY SCGWIN.* BACKUP/SCGWIN.*
- Subir o banco original.
DBSRV12 @<nome arquivo parametro> # scgwin.par: # -c120M # -xNone # -nScgwin12 # scgwin.db
- Verificar se UPDATE foi honrado (deve ter sido).
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).
dbsrv12 -f scgwin.db
- Subir banco e verificar UPDATE (não deve ter sido honrado).
Conclusões Importantes
- Devemos sempre utilizar o arquivo de log e, de preferência, num drive diferente do arquivo do banco.
- Devemos truncar o log conforme o plano de backup (seja um backup incremental ou diferencial). No mínimo, truncar o log após cada backup full.
- O banco estará sempre atualizado após o checkpoint ou após ser derrubado de forma normal (não precisa do log para subir).
- O arquivo de log é utilizado sempre que o banco é derrubado de forma anormal para executar as operações posteriores ao checkpoint.
Considerações sobre gravação do banco (flush cache).
É 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).
Sistema Operacional |
Sintoma |
Ação |
---|---|---|
Windows |
Falha para honrar FUA bit ou o comando de flush cache |
Testar performance com cache desligado (Device Manager, Propriedades Disk Drive, Tab Política, Desabilitar "Write Caching"), atualizar SO e drivers controladores |
Linux |
FS = ext2, ext3 ou ext4 sem cláusula de montagem Barrier=1, ext3 ou ext4 com versão do Kernel igual ou inferior a 2.6.32, xfs com cláusula de montagem /nobarrier, xfs com versão do Kernel igual ou inferior a 2.6.33, LVM com versão do Kernel igual ou inferior a 2.6.29 |
Desabilitar write cache usando sdparm ou hdparm conforme o documento |
Desfragmentação e tamanho do banco.
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.
# -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):ALTER DBSPACE SYSTEM ADD 120MB
- Desfragmentar Sistema operacional.
Acrescentar desfragmentação de tabelas e índices.
Tamanhos Máximos
Tipo |
Valor |
---|---|
Tamanho Banco |
13 arquivos (DB) por banco. Para cada arquivo, seu tamanho é determinado pelo Sistema Operacional (mais abaixo) |
Tamanho Arquivo (FAT 12) |
16 MB |
Tamanho Arquivo (FAT 16) |
2 GB |
Tamanho Arquivo (FAT 32) |
4 GB |
Tamanho Arquivo para NTFS, HP-UX 11.0 ou mais recentes, Solaris 2.6 ou mais recentes, Linux 2.4 ou mais recentes) |
512 GB for 2 KB pages |
Tamanho Arquivo (para todas as outras plataformas e sistemas de arquivos) |
2 GB |
Tamanho máximo cache (non-AWE cache) (Windows XP Home Edition, Windows XP Professional, Windows Server 2003 Web Edition, Windows Server 2003 Standard Edition, Windows Server 2008, Windows Server 2008 R2, Windows 7) |
1.8 GB |
Tamanho máximo cache (non-AWE cache) (Windows Server 2003 Enterprise Edition, Windows Server 2003 Datacenter Edition, Windows Vista Ultimate, Windows Vista Enterprise, Windows Vista Business, Windows Vista Home Premium, Windows Vista Home Basic) |
2.7 GB |
Tamanho máximo cache (AWE cache) (Windows XP Home Edition, Windows XP Professional, Windows Server 2003 Web Edition, Windows Server 2003 Standard Edition, Windows Server 2003 Enterprise Edition, Windows Server 2003 Datacenter Edition ) 100% da memória disponível |
128 MB |
Tamanho máximo cache (Windows Mobile) |
Limitado ao tamanho de memória do dispositivo |
Tamanho máximo cache (Unix---Solaris, x86 Linux, IBM AIX, HP) |
2 GB para servidor 32-bits |
Tamanho máximo cache (Win 64) |
Limitado pelo tamanho da memória física nos servidores 64-bits |
Tamanho máximo cache (Itanium HP-UX) |
Limitado pelo tamanho da memória física nos servidores 64-bits |
Novos recursos Sybase 12.
Migrando o banco para o Sybase 12.
Aumentando a velocidade.
Aumentando a disponibilidade.
A disponibilidade do banco deve ser calculada de forma que se tenha o melhor custo/benefício para o cliente. O primeiro passo é definir quanto tempo o cliente aguentaria sem o Sistema no ar. Isso pode variar de nem um minuto, alguns minutos, algumas horas ou muitas horas. É lógico que, em sã consciência, todo cliente iria preferir a primeira opção. Porém, o custo de se ter um servidor auxiliar, licença de uso de um outro Sybase, uma estrutura de rede redundante, todos os micros com no-break, Internet redundante enfim, ter todos os pontos que podem falhar duplicados pode não compensar comparando este cenário com um muito mais barato onde teríamos um servidor auxiliar destino dos backups e um funcionário para acioná-lo caso o servidor principal falhe.
Enfim, é questão de avaliação porém é necessário sempre simular o tempo de parada nos mais variados cenários para se chegar a uma decisão conjunta.
Por isso, iremos listar os mecanismos disponíveis de proteção e cada um poderá optar pelo cenário com melhor custo/benefício para a sua empresa.
Você deve avaliar o procedimento de backup particularmente em função de cinco variáveis:
- Cobertura: Deve ser todo o banco de dados + todas as alterações ocorridas.
- Frequência: Quanto frequente deve ser o backup full e os incrementais? Dica: a resposta está no tempo de parada no caso de uma restauração. Quanto mais frequentes, os backups full menor o tempo de parada porém mais carga será exigida do banco de dados.
- Separação: Colocar os backups em outros discos rígidos ou computadores para que uma pane não os afete. Tenha cópia do backup full em locais diferentes e que não estejam juntos fisicamente (de preferência, em endereços diferentes).
- Histórico: Ter mais de um conjunto (full + incrementais) de forma que se possa recuperar dados ou mesmo um backup mais antigo no caso de alguma emergência.
- Validação: Validar, validar, validar os backups sempre e, de preferência, de forma automática. Não se esquecer de realizar, de vez em quando, uma restauração manual para não esquecer como o procedimento é realizado (aprender na hora da emergência sempre leva a resultados desastrosos ou demorados).
- Segurança: Verifique quem tem acesso aos backups (não só fisicamente como na rede local). Não deixe os mesmos de forma que qualquer pessoa possa obtê-los ou copiá-los (e principalmente, danificá-los).
Backup full
É 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
dbbackup -c "SERVER=scgwin12;UID=DBA;PWD=mara97" "c:\backup" # opções # Host=192.168.0.1 # DBN=scgwin.db
- Usando o ISQL
BACKUP DATABASE DIRECTORY 'C:\\backup';
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.
Backup incremental
O backup incremental é um backup parcial somente do arquivo de transações (.LOG) obtido no momento em que é realizado o backup. É como se tirássemos uma foto do log no momento da execução do comando. É um backup bem mais rápido do que o backup full. É chamado incremental pois logo após a sua realização truncamos o arquivo de log original fazendo com que o log original tenha somente as transações a partir daquele momento (em que o mesmo fora truncado). Ele é utilizado num esquema de backups onde normalmente é realizado um backup full num primeiro passo e, em seguida, realizados backups incrementais num intervalo fixo de tempo (1h por exemplo). Assim, teríamos num plano de backup hipotético, um backup full programado para a meia-noite, outro para o meio-dia e backups incrementais de hora em hora (1:00am, 2:00am e assim por diante). Para se restaurar um banco de dados num esquema como este, precisaríamos ter o backup full e todos os backups incrementais até o ponto da falha.
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
dbbackup -c "SERVER=scgwin12;UID=DBA;PWD=mara97" -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.
dbbackup -c "SERVER=scgwin12;UID=DBA;PWD=mara97" -x -t -r -n "c:\backup" # opções # Host=192.168.0.1 # DBN=scgwin.db
- Usando o ISQL
BACKUP DATABASE DIRECTORY TRANSACTION LOG ONLY LOG TRANSACTION LOG RENAME MATCH 'C:\\backup';
Backup Diferencial
O backup diferencial na realidade segue a mesma linha do backup incremental tendo como única diferença o fato de não truncarmos o log de transações logo após realizarmos o backup incremental.
Assim, a cópia do log de transações realizada contém não só as últimas alterações ocorridas desde o último backup do log de transações como também todas as transações desde que o log foi truncado (normalmente pelo último backup full). Para se restaurar um banco de dados num esquema como este, precisaríamos ter o backup full e somente o último arquivo de log copiado até o ponto da falha.
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
dbbackup -c "SERVER=scgwin12;UID=DBA;PWD=mara97" -x -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
BACKUP DATABASE DIRECTORY TRANSACTION LOG ONLY LOG 'C:\\backup';
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.
Backup live
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).
dbbackup -l c:\\backup\scgwin.log -c "SERVER=scgwin12;UID=DBA;PWD=mara97"
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:
- 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.
- 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.
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.
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.
Prática - Fazendo o backup, restaurando e testando o backup de forma automática.
Vamos juntar o que foi exposto até o momento e realizar através de comandos batch os seguintes procedimentos:
- Criar banco de dados.
02_run_dbinit12.bat
"%SQLANY12%\bin32\dbinit.exe" ddd12.db
- Subir banco de dados criado.
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: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
04s_script_to_create_table_and_event.sqlCREATE 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.
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
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.
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
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.
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.
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).
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.
Database Mirror
Este é um esquema que permite ao Sybase ter fail-over, isto é, troca automática de banco em caso de pane de um banco principal. É um conceito simples e com um esquema simples de execução porém com alcance em termos de segurança muito profundos:
- Copia-se o banco principal (primário) para um outro servidor (secundário).
- Cria-se um terceiro servidor (árbitro) conectando os outros dois (primário e secundário).
- Sobe-se os três servidores. O secundário fica read-only e espelhando todas as operações do primário.
- Usuário se conecta no servidor primário e realiza todas as operações nele normalmente.
- Caso o servidor principal caia, o secundário assume as operações automáticamente e torna-se atualizável.
- Ao voltar o servidor primário, automaticamente os papéis voltam ao estado original.
Iremos fazer um teste sobre o assunto mais adiante, porém existem alguns pontos a se considerar:
- Comunicação entre servidores (síncrono/assíncrono): No modo síncrono, todas as transações realizadas no servidor primário são garantidas de estar no servidor secundário. Neste modo, todas as páginas que foram realizadas commit são enviadas ao servidor secundário que responde com um sinal positivo para o servidor primário e este só retorna o comando ao usuário após ter este sinal recebido. No modo assíncrono, não existe uma garantia que as páginas estejam escritas no servidor secundário pois o servidor primário não espera um retorno do servidor secundário para retornar o comando ao usuário, isto é, ele simplesmente envia as alterações assim que as mesmas tenham sido registradas (com um commit) sem ter garantia do sucesso na gravação no servidor secundário.
- É exigido que os servidores participantes do esquema sejam dbsrv12. A engine pessoal (dbeng12) não pode ser executada pois é necessário comunicação via rede entre os servidores (que a dbeng12 não permite - conexões de outros computadores).
- É necessário pelo menos dois servidores para o esquema funcionar (primário/secundário; primário/árbitro; secundário/árbitro).
- O log de transações não pode ser truncado. Pode-se renomear o log de transações deixando-os no mesmo diretório do log principal (para que sejam aplicados ao servidor secundário quando este se conectar ao sistema) e, num segundo momento, tendo-se a certeza que o log já não é mais necessário, deletá-lo a partir de algum evento que seja executado uma vez por semana por exemplo.
Prática - Configurando um esquema de espelhamento de banco (database mirroring).
Crédito para o site http://sqlanywhere.blogspot.com ao qual tomei por base os meus testes e esta demonstração.
- 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.1_create_ha.batREM 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
additional_ddl.sqlCREATE 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).2_start_HA.batREM 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
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)
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.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).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.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.
Monitoramento do Banco
O correto monitoramento do banco faz com que possamos antecipar algum problema de lentidão ou melhorar a performance do mesmo a partir de certos parâmetros do servidor, do banco ou da conexão.
Assim, é importante primeiro conhecermos alguns parâmetros importantes e, em seguida, vermos como podemos monitorá-los.
Abaixo, segue um exemplo da criação de uma stored procedure que irá (ao ser consumida através de um SELECT * FROM V_SHOW_COUNTERS depois de ter sido criada) mostrar todos os contadores numéricos tanto do servidor quanto do banco de dados.
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;
Como se pode ver (tela abaixo), ao ser executada, a procedure retorna uma infinidade de parâmetros sobre o banco e o servidor inclusive com uma breve descrição de cada uma delas.
O importante nesta análise é estabelecer uma baseline, isto é, monitorar alguns parâmetros e estabelecer quais são os números que indicam que o servidor ou o banco está trabalhando normalmente. Isto só pode ser estabalecido pelo próprio administrador já que cada ambiente tem suas próprias configurações de hardware, rede e número de clientes. Esta baseline deve ser estabelecida ao longo do tempo de forma que a média seja o número mais apropriado para se levar em consideração pois teremos momentos de pico ou vale que não irão caracterizar o comportamento normal do servidor ou banco.
Porém, quais são os mais importantes ou os primeiros a serem analisados e monitorados?
Principais Parâmetros
Nome Propriedade |
Descrição |
Tipo |
Observações |
---|---|---|---|
ActiveReq (activeReq) |
Número de requisições que estão sendo processadas no momento |
Servidor |
|
MaxReq (multiprogramminglevel) |
Número máximo de requisições possíveis de serem processadas ao mesmo tempo |
Servidor |
Corresponde ao parâmetro -gn (MultiProgrammingLevel) cujo default é 20. Este número pode ser aumentado automaticamente pelo servidor conforme o número de requisições pendentes, processadores e memória disponíveis. O número máximo é obtido pelo parâmetro MaxMultiProgrammingLevel e um valor razoável é igual a 80. |
WaitingReq (unSchReq) |
Número de requisições esperando para serem processadas |
Servidor |
|
DiskReads |
Número de páginas lidas pelo servidor |
banco de dados |
Pode-se obter a velocidade de leitura de páginas do servidor se calcularmos a diferença entre este parâmetro neste momento e seu valor a 1 minuto atrás. |
DiskWrites |
Número de páginas gravadas pelo servidor |
banco de dados |
Pode-se obter a velocidade de gravação de páginas do servidor se calcularmos a diferença entre este parâmetro neste momento e seu valor a 1 minuto atrás. |
BytesIn (bytesReceived) |
Bytes recebidos pelo servidor |
servidor |
Pode-se obter a velocidade de recebimento de dados via rede local do servidor se calcularmos a diferença entre este parâmetro neste momento e seu valor a 1 minuto atrás. |
BytesOut (bytesSend) |
Bytes enviados ao servidor |
servidor |
Pode-se obter a velocidade de envio de dados via rede local do servidor se calcularmos a diferença entre este parâmetro neste momento e seu valor a 1 minuto atrás. |
CacheHits |
Número de páginas que foram supridas pelo cache e não por uma leitura do disco |
Banco |
Satisfação Cache = CacheHits/CacheRead |
CacheRead |
Número de páginas que foram pesquisadas no cache |
Banco |
Qualquer dado manipulado pelo banco é trazido ao cache primeiro e este parâmetro conta o número de acessos ao cache independente se a página originalmente estava lá ou não. |
CachePanics |
Número de tentativas mal sucedidas de se alocar uma página ao cache |
Banco |
Deve ser sempre 0 ou próximo de 0 |
Low Memory (QueryLowMemoryStrategy) |
Número de vezes que o servidor teve de alterar seu plano de query por memória (cache) baixa |
Banco |
|
Como monitorar?
Existem algumas opções para o monitoramento destes parâmetros:
- DbMonitor: Ferramenta da Sybase que vem com o banco. A vantagem é que é "gratuita", isto é, já vem junto com a instalação do SQL Anywhere. Seu problema é que vc precisa saber quais são os parâmetros que precisa ou deseja monitorar.
- Windows Performance Monitor: Você pode cadastrar os parâmetros que deseja monitorar no WPM. Ele salva o histórico dos mesmos porém a desvantagem é a mesma que o DBMonitor, isto é, você precisa saber quais parâmetros precisa ou deseja monitorar. A vantagem dele é que você conseguirá obter parâmetros adicionais para o servidor independente do banco de dados.
- FoxHound: Ferramenta desenvolvida por pessoas que já trabalharam na Sybase, específica para o SQLAnywhere. Você pode obter uma versão demonstração no site da empresa Rising Road. É com ela que iremos demonstrar alguns destes parâmetros em ação. Infelizmente, esta ferramenta tem um custo (versao básica US$ 195.00, versão completa US$ 395.00 - base Jan/2012) porém pode-se obter uma licença temporária somente para testes.
Vamos mostrar algumas situações do dia a dia para ver como os parâmetros são alterados.- Situação de bloqueio. Aqui, simulamos uma conexão bloqueada por outra. Por exemplo, damos um UPDATE numa tabela via ISQL sem dar COMMIT abrindo o Sispetro e tentando atualizar este mesmo registro.
- Situação stress CPU (requisições OK).
Aqui, simulamos uma situação na qual elevamos a CPU para quase 100%. Notar como O SQLAnywhere cria conexões internas (INT - no painel de conexões) a fim de desmembrar uma requisição em pequenos pedaços rodando assíncronamente.
- Situação stress número requisições. Aqui, o problema é que o número de requisições está acima da capacidade do servidor em atendê-las (número alto de requisições em espera).
- Situação cache baixo.
Vamos subir um banco com pouquíssimo cache (4M, mínimo) através da cláusula -ch e ver como os parâmetros (cache satisfaction) do banco se comportam.
Repare abaixo como a atividade em disco aumentou significativamente.
- MultiProgramming Level (-gn).
Aqui, vemos um servidor em apuros: 35 conexões, 3000 commits por segundo, 94% CPU sendo utilizada e ainda assim muita gente esperando (2/3 das conexões).
Será que precisamos aumentar o parâmetro multiprogramming level? Ele é autoajustado pelo SQLAnywhere e normalmente não precisa ser ajustado pois um aumento deste valor pode inclusive diminuir a performance do banco pois ele terá que lidar com mais conexões, consumir mais memória e, como foi dito, normalmente o SQLAnywhere se auto ajusta.
Aumentando o número de CPU's neste caso acabou resolvendo o problema.
Note que o número máximo de requisições aumentou um pouco (mais 3 conexões) porém o número de commits por segundo aumentou muito mais (> 11.000) e o percentual de trabalho da CPU diminuiu (64%).
- Situação de bloqueio. Aqui, simulamos uma conexão bloqueada por outra. Por exemplo, damos um UPDATE numa tabela via ISQL sem dar COMMIT abrindo o Sispetro e tentando atualizar este mesmo registro.