2012年12月9日日曜日

SQLSERVER デットロックを防ぐ②

デットロックが発生原因について、もうひとつ付け加えたい。

■現象

2つのセッションがあり、1つのテーブルがあるとする。

①あるセッションは、テーブルにINSERTを実行

②あるセッションは、条件指定でSELECTを実行

ここで、デットロックが発生したとする。

①と②は、まったく条件が異なるため、デットロックしないはずでは。。

■原因

テーブルには、INDEXが設定されており、INSERTが
COMMITされるまでは、その INDEX を利用したSELECTがロック待ちになる。
(INDEXの木構造を再構築中のためではないか)

■対策

INDEXを参照しないでSELECTする。
利用頻度が低い INDEX は、削除した方が、INSERTも早くなる。
INDEXは、パフォーマンスを大幅に改善することがあるが、
デットロックを引き起こす原因になることも忘れてはならない。








SQLSERVER NOLOCKオプションの有効性

今回は、NOLOCKオプションについて、いくつか書いてみたいと思う。

■効果
あるテーブルがUPDATEされ、COMMIT待ちの状態であるが、
NOLOCKオプションをつけると、COMMIT前のデータがSELECTできてしまう。

つまり、複数のセッションで処理する場合、待ち時間がなくなるため
パフォーマンスアップにつながるわけである。

■弊害
COMMIT前のデータが読めるというのは、ダーティリードとも呼ばれる。
何が問題かというと、ROLLBACKされたときに、読み取ったデータが
存在しなくなるということである。または存在しても異なっている場合がある。

取得した値を利用して、処理をしてみたが、あるタイミングで、その値は
無効なものとなっている。前提が崩れているとすれば処理結果も信用できない。

また、NOLOCKオプションを付ける前だと、ロック待ちとして排他されていたが
付けた後だと、同時実行されるようになり、整合性に問題がでる場合がある。

■使用場所
①別記「SQLSERVER デットロックを防ぐ」にあるように、デットロック回避に使う。
 →NOLOCKオプションでSELECT後、一意キーでアクセスすることにより、
  ロック範囲を狭めるのと、整合性の確認、パフォーマンスアップにつながる。

②ROLLBACKがほぼなく、パフォーマンスアップしたい箇所につかう。
 または、ROLLBACKされても整合性に問題が起きない箇所。

在庫データの参照など、ダーティリード時の影響が大きい箇所などには適用せず
リスクが比較的少ない箇所から実験するのもひとつの手である。



 

SQLSERVER デットロックを防ぐ

2つのセッションが1つのテーブルにアクセスするとき、異なる抽出条件に
なっているにも関わらず、そこでデットロックが発生することはないだろうか?

■状況
たとえば、テーブルのカラムが
ID
GROUP
NAME
AGE
となっていて、

セッション①
WHERE ID = 1

セッション②
WHERE ID = 2

などとアクセスする。
このとき、別のデータを取得しようとしているので、ぶつからないはずである。

■原因
いくつかあるのだが、以下のような場合である。

①ID、GROUPのカラムで複合一意キーを指定していたが、どちらか片方で
 アクセスしていた。

②一意キー以外の条件でアクセスすると、関係ないレコードまでロックされる
 (SQLSERVERは、勝手に広範囲をロックしてしまうようだ)

■対応策

①複合一意キーの場合は、その定義順序で、定義されているカラムを必ず指定すること

② 一意キー以外の条件でアクセスする場合、WITH (NOLOCK) オプションでまず
 データを取得する。その後、取得結果をカーソルでまわしながら、一意キーで
 再度SELECTする。
 ※ WITH (NOLOCK) オプションは、ROLLBACK時の影響があることから乱用は避けたい

このような対応で、ロック範囲は意図した通りとなり、デットロックは軽減される。

■参考

ロック範囲が意図した範囲か確認する方法として、以下がある。

「Management Studio」を2つ起動させておく。

①片方で、範囲外と思われるレコードをUPDATE

②もう片方で、確認したいSELECT文を流す( 調べたいWHERE句を指定して )

以上を実行してみて、固まる(ロック待ち)となれば、ぶつかっているし、
無事にSELECTできれば、ロック範囲は重なっていない。




2012年8月22日水曜日

SQLSERVER 3414 エラー

昨日まで使えたのに今日立ち上げたらつながらなくなった
ということはないだろうか?

Management Studioでデータベースを開こうとすると以下のエラー出る

・エラー 3414、Error: 3414 ( MSSQLSERVER_3414 )
・データベースにアクセスできません。接続できない
・復元中にエラーが発生したので、データベース は再開されません
・利用できません、利用不可
・An error occurred during recovery, preventing the database

これは、データベース起動中にエラーが発生しオープンできずにいる状態で
Management Studioからどうすることもできないように見える。

が、しかし、そこはデータベースなので万が一のディスクエラーにもある程度
対処できるようコマンドが用意されているのだ。

■手順 (Resolution)

①ステータスリセット
exec master..sp_resetstatus データベース名

②データベースをリカバリ
DBCC DBRECOVER ('データベース名', IGNOREERRORS)

③復旧後の整合性チェック
DBCC CHECKDB ('データベース名') WITH NO_INFOMSGS, ALL_ERRORMSGS;

④ ③でエラーが出ず、データベースが開ければ無事完了である

■結果

このような事態が本番環境で発生するとサービスが停止してしまうので影響は甚大である。
普段から障害発生時を想定して、エラー原因と対処方法などを調べておいたり、
実際、テスト環境で発生したときにじっくり対処方法を検討し、まとめておくとよいだろう。


2012年3月31日土曜日

ORA-12516 エラーの原因・対処

■エラー内容
ORA-12516: TNS: リスナーは、一致するプロトコル・スタックが使用可能なハンドラを
検出できませんでした。

■原因
停電などでHUBの電源が落ちた際、クライアントPCとデータベースが一度にたくさん
切断される場合がある。このとき、データベース側には、これまで接続していた
プロセス・セッションが残っている。復旧後に、クライアントPCから接続されると
新たにプロセス・セッションがデータベースのメモリに割り当てされる。
つまり、サーバ上のメモリ使用量が倍近くに膨らんでしまい、もうメモリ不足(正確には
データベースのプロセス・セッション用に割り当て可能な上限値を超えてしまう)
で新たな接続は認めないというエラーが返ってきている。
いわゆるゾンビプロセスは、時間が経てば無くなっていくため、下記の対処を
行わなくても、待てば解決する場合もある。(端末の台数によるが1時間半程度)

■方針
接続が残っているセッションを強制終了する

■手順

①SYSでログインする

sqlplus sys/パスワード@detabase名 as sysdba

②表示幅の調節
set linesize 1000

③セッションを確認する

最大値に達していれば、エラー原因と考えてよい
(新たな接続が制限され、なかなかつながらない状況)

SELECT
   RESOURCE_NAME リソース名,
   CURRENT_UTILIZATION 現在数,
   MAX_UTILIZATION 最大数,
   LIMIT_VALUE 上限
FROM
   V$RESOURCE_LIMIT
WHERE
   RESOURCE_NAME IN ('processes','sessions');

リソース名 現在数 最大数 上限
------------------------------------------
processes   198       200       200
sessions      197       200       200

④具体的なセッションを一覧表示する

machine が端末名、 LOGON_TIME がログイン時間、
sid と serial# がセッションを強制終了する際に必要となる項目

SELECT
   S.machine,
   TO_CHAR(S.LOGON_TIME, 'YYYY/MM/DD HH24:MI:SS') LOGON_TIME,
   S.sid,
   S.serial#,
   P.PID ORACLE_PID,
   P.SPID OS_PID,
   S.STATUS,
   S.USERNAME ORACLE_USER,
   S.OSUSER OS_USER,
   S.TERMINAL,
   S.PROGRAM
FROM
   V$PROCESS P
       LEFT OUTER JOIN V$SESSION S
       ON P.ADDR = S.PADDR
WHERE
    P.BACKGROUND IS NULL  AND
    P.PID > 1
ORDER BY
    S.machine,
    TO_CHAR(S.LOGON_TIME, 'YYYY/MM/DD HH24:MI:SS');

⑤重複しているセッションを強制終了する

 同一マシーンで、古いログイン時間で、STATUSがACTIVEでなければ、
強制終了の対象として検討する。
間違えた場合は、危険を伴うためユーザーの責任で実行すること

文法:alter system kill session 'sid,serial#';

例:alter system kill session '100,123'
実行後、commitする

■対策

物理メモリに余裕があれば、プロセス数、セッション数の上限を増やす。
ただし、PGAのメモリ割り当てが少ないと別のエラー原因になるため、
単純に増やせばいいわけではない。また、ORACLEインスタンスの再起動が
必要になるため、実行する際は注意が必要である。まずは自分のローカル環境で
試しに増やして実験してみるとよいだろう。

SYSユーザーにて以下を実行
alter system set processes = xxx scope=spfile;
alter system set sessions = xxx scope=spfile;

データベースにいきなり接続できなくなると影響がかなり大きいため
日頃から、プロセス数、セッション数、SGA、PGAのメモリ状況などは
チェックするようにしておきたい。


2012年2月11日土曜日

共有フォルダにあるEXEを起動する方法

以前のVB6の時代では、サーバ上にあるEXEのショートカットを
ローカル端末に作成し、簡単に起動できていた。

.NETになってからは、セキュリティが厳しくなり、いくつか設定が必要である。

手順は以下の通りである。

まず起動させたいEXEのショートカットをローカルに作成する。

■実行してみると、以下のエラーが出る場合がある

「発行元を確認できませんでした。このソフトウェアを実行しますか?」
「このファイルには、発行元を検証できる有効なデジタル署名がありません。
信頼できる発行元のソフトウェアのみ実行して下さい。」

対処方法は、以下の2点

1.対象のEXEのパスは、IPアドレスでなく、PC名で指定する
  ショートカットを右クリックして、プロパティを選択
  リンク先が「\\192.168.XXX.XXX\・・・」となっている箇所を
  「\\自分のPC名\・・・」のように変更する

 ※自分のPC名とは、マイコンピュータを右クリックして
プロパティを選択し、コンピュータ名のタブを選択する。
   フルコンピュータ名のところに表示されている名称のことである。
    「.」が付いているがこれは不要

2.EXEがある場所をゾーン追加する
  コントロールパネル→インターネットオプション
  「セキュリティ」のタブを選択
  「ローカルイントラネット」を選択
  「サイト」のボタンを押下
  「詳細設定」のボタンを押下
  このWEBサイトをゾーンに追加する欄に、EXEがあるパス
  「\\自分のPC名\・・・」を貼り付けて追加ボタンを押下する。
  登録されると「file://自分のPC名」と表示される。

■再度、EXEを実行してみると、以下のエラーが出る場合がある

「System.Security.Policy.PolicyException: 必要なアクセス許可を取得できません。」

これは、.NETのセキュリティポリシー設定が不足している場合である。

対処方法は、以下の通り

  ・コマンドプロンプトを起動する

  ・以下のコマンドを実行する
   「cd c:\Windows\Microsoft.NET\Framework\v2.0.50727」

 ・以下のコマンドを実行する
  「caspol -m -ag 1 -url file://\\\自分のPC名\EXEがあるパス\* FullTrust」

以上の手順が完了すると、ローカル端末から無事EXEが起動するはずである。

今回の手順は、閉じたネットワークを想定しており、インターネットに接続できる
ような端末では、セキュリティ設定を十分考慮する必要がある。