http://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html 에서 참조

 

11.2.2. Fixed-Point Types (Exact Value) – DECIMAL, NUMERIC

The DECIMAL and NUMERIC types store exact numeric data values. These types are used when it is important to preserve exact precision, for example with monetary data. In MySQL, NUMERIC is implemented as DECIMAL, so the following remarks about DECIMAL apply equally to NUMERIC.

MySQL 5.5 stores DECIMAL values in binary format. See Section 12.18, “Precision Math”.

In a DECIMAL column declaration, the precision and scale can be (and usually is) specified; for example:

salary DECIMAL(5,2)

In this example, 5 is the precision and 2 is the scale. The precision represents the number of significant digits that are stored for values, and the scale represents the number of digits that can be stored following the decimal point.

Standard SQL requires that DECIMAL(5,2) be able to store any value with five digits and two decimals, so values that can be stored in the salary column range from -999.99 to 999.99.

In standard SQL, the syntax DECIMAL(M) is equivalent to DECIMAL(M,0). Similarly, the syntax DECIMAL is equivalent to DECIMAL(M,0), where the implementation is permitted to decide the value of M. MySQL supports both of these variant forms of DECIMAL syntax. The default value of M is 10.

If the scale is 0, DECIMAL values contain no decimal point or fractional part.

The maximum number of digits for DECIMAL is 65, but the actual range for a given DECIMAL column can be constrained by the precision or scale for a given column. When such a column is assigned a value with more digits following the decimal point than are permitted by the specified scale, the value is converted to that scale. (The precise behavior is operating system-specific, but generally the effect is truncation to the permissible number of digits.)

11.2.3. Floating-Point Types (Approximate Value) – FLOAT, DOUBLE

The FLOAT and DOUBLE types represent approximate numeric data values. MySQL uses four bytes for single-precision values and eight bytes for double-precision values.

For FLOAT, the SQL standard permits an optional specification of the precision (but not the range of the exponent) in bits following the keyword FLOAT in parentheses. MySQL also supports this optional precision specification, but the precision value is used only to determine storage size. A precision from 0 to 23 results in a 4-byte single-precision FLOAT column. A precision from 24 to 53 results in an 8-byte double-precision DOUBLE column.

MySQL permits a nonstandard syntax: FLOAT(M,D) or REAL(M,D) or DOUBLE PRECISION(M,D). Here, “(M,D)” means than values can be stored with up to M digits in total, of which D digits may be after the decimal point. For example, a column defined as FLOAT(7,4) will look like -999.9999 when displayed. MySQL performs rounding when storing values, so if you insert 999.00009 into a FLOAT(7,4) column, the approximate result is 999.0001.

Because floating-point values are approximate and not stored as exact values, attempts to treat them as exact in comparisons may lead to problems. They are also subject to platform or implementation dependencies. For more information, see Section C.5.5.8, “Problems with Floating-Point Values”

For maximum portability, code requiring storage of approximate numeric data values should use FLOAT or DOUBLE PRECISION with no specification of precision or number of digits.

GNOME (GNU Network Object Model Environment)

GNOME[그놈]은 유닉스 기반의 운영체계 사용자들을 위한 그래픽 사용자 인터페이스와 일련의 컴퓨터 데스크탑 애플리케이션들이다. GNOME은 유닉스 기반의 운영체계를 프로그래머가 아닌 사람들도 쉽게 사용할 수 있고, 일반적으로 윈도우 데스크탑 인터페이스와 그것의 대부분의 보편적인 애플리케이션 들에 상응하도록 만드는데 목적이 있다. 실제로, GNOME은 사용자들이 여러 개의 데스크탑 외관 중 하나를 선택할 수 있게 해주는데, 예를 들면 사용자 인터페이스를 윈도우나 맥OS처럼 보이도록 만들 수 있다. 게다가, GNOME은 워드프로세서, 스프레드시트, 데이터베이스 관리자, 프레젠테이션 개발도구, 웹브라우저전자우편 프로그램 등, 윈도우의 오피스97 제품에 있는 것들과 같은 형태의 애플리케이션들을 포함한다.

GNOME은 리처드 스톨먼에 의해 설립된 조직인 FSF의 후원 아래, 자원자들의 장시간에 걸친 노력의 산물이다. 스톨먼과 FSF의 동료 회원들은 소프트웨어소스코드가 다른 사람들에 의해 계속적으로 개선될 수 있도록 항상 공개되어야 한다고 여기고 있다. GNOME은 데스크탑 운영체계 시장이 어떤 한 회사에 의해 통제되지 않도록, 리눅스와 다른 유닉스 시스템들을 윈도우에 대한 존립 가능한 대안으로 만들기 위한 노력의 일부이다. GNU는 FSF의 운영체계와 일련의 응용프로그램들이다 (리눅스는, 리누스 토발즈가 GNU로부터 나온 부가적인 운영체계 요소에 커널을 추가함으로써 개발된 운영체계이다).

GNOME은 GNOME 프로그램들과 다른 운영체계 플랫폼 프로그램들이 상호운용성을 가질 수 있도록 CORBA를 지원하는 ORB와 함께 나온다. GNOME은 또한 프로그래머들이 GNOME 사용자 인터페이스를 사용하는 응용프로그램들을 개발하는데 사용할 수 있도록 위짓 라이브러리를 포함한다. GNOME은 데스크탑 버전 외에도, 포켓용 PalmPilot 등을 위한 사용자 인터페이스와 일련의 응용프로그램도 나온다.

http://cafe.daum.net/gamja-net/foWo/13?docid=1LdQhfoWo1320100911103346 에서 참조

MySQL 클라이언트/서버 개념
 
 • MySQL 클라이언트/서버 모델 설명
 • 어떻게 서버가 스토리지 엔진을 지원하는 지에 대한 이해
 • MySQL이 메모리와 디스크 공간을 기본적으로 얼마나 사용하는 지에 대한 설명
 
2-1. MySQL 일반적인 구조
 
 • 클라이언트/서버를 사용하기 위한 네트워크 환경
 • MySQL 설치 구성 요소
   – MySQL 서버
   – 클라이언트 프로그램
   – 넌-클라이언트 프로그램
 
2-2. MySQL 서버
 
  • mysqld
  • 싱글 프로세스 구조
  • 데이터베이스를 위한 엑세스 관리
  • 멀티 쓰레드 접속
  • 멀티 스토리지 엔진 지원
  • 서버와 호스트
   – 서버는 소프트웨어
   – 호스트는 소프트웨어가 동작하는 물리적인 장비
 
2-3. 클라이언트 프로그램
 
  • 데이터베이스를 다루기 위해 서버에 접속
  • 일반 클라이언트 프로그램
   – MySQL Query Browser
   – MySQl Administrator
   – mysql
   – mysqlimport!
   – mysqldump
   – mysqladmin
   – mysqlcheck
 
2-4. MySQL 클라이언트/서버 모델
 
  • 서버
   – 주요 데이타베이스를 다루는 프로그램
  • 클라이언트
   – 데이타를 수정하거나 반환하기 위해 그 서버에 접속하는 프로그램
 
2-5. 프로토콜 통신
 
 • TCP/IP
   – 트랜스미션 컨트럴 프로토콜
   – 인터넷 프로토콜
 • 유닉스
   – Unix Socket
 • 윈도우
   – Shared Memory
   – Named Pipes
 
2-6. MySQL 넌-클라이언트 유틸리티
 
 • 서버에 독립된 프로그램
   – myisamchk
   – myisampack
 • 직접 MyISAM 테이블에 엑세스
 
2-7. 디스크 공간을 사용하는 MySQL
 
 • 주요 디렉토리와 파일
 • 서버는 데이타 디렉토리 사용
   – 데이터베이스 디렉토리
   – 테이블 포맷 파일(.frm)
   – 데이타와 인덱스 파일
   – InnoDB는 그 자신만의 테이블스페이스와 로그 파일을 가짐
   – 서버 로그 파일과 상태 파일
   – 다른 데이터베이스 오프젝트(트리거, 뷰 등..)
   – 인증 정보
   – mysqld
 
2-8. 메모리를 사용하는 MySQL
 
 • 주요 데이타 구조
   – 서버는 클라이언트와 데이터베이스를 관리하기 위해 설정 함
 • 서버는 정보를 위해 메모리를 할당함
   – 쓰레드 핸들러
   – MEMORY 타입 테이블을 생성
   – 임시 테이블
   – 각각의 클라이언트 접속을 위한 버퍼
   – 전역(global) 버퍼와 캐쉬

http://www.hanb.co.kr/network/view.html?bi_id=931 에서 참조

저자: 임영규 / Gnome Linux Institute / imlinux70@hanafos.com

1. 서론

현재 데이트베이스 이해와 학습 그리고 일반적인 웹 사이트 구성에 가장 많이 적용되고 있는 데이트베이스 엔진이 MySQL이다. 많은 데이터베이스 엔진이 있음에도 불구하고 일반인들이 접근 하기가 가장 쉬운 것이 MySQL이라고 하겠다. 이번 과제에서는 MySQL 엔진이 탑제 서버에 어떻게 접슨하여 데이터를 억세스 하는지 그 과정을 알아보고자 한다.

일반적으로 데이터베이스 접근을 위한 방법은 OS Shell에서 데이터베이스 클라이언트를 이용하여 접근한다. MySQL은 mysql이라는 클라이언트 프로그램을 제공한다. 그럼 본론에서 클라이언트와 서버 간의 구체적인 접근 방법에 대한 과정을 살펴 보기로 한다.

2. 본론

하나의 데이터베이스 서버를 구축하고, 편의상 본 과제에서는 MySQL을 다운받아 설치하기로 한다, 다른 PC에서 이 데이터베이스 서버에 접근해 보기로 하자. 우선 서버는 리눅스 시스템을 사용하기로 한다. 현재 본 연구소에서는 레드햇 리눅스 9.0을 사용한다. 리눅스 설치시 데이터베이스를 모두 설치하면 MySQL을 설치할 수 있다. 학습을 목적으로 근사모가 제공하는 APM_SETUP이라는 윈도우용 페키지를 이용할 수도 있겠다. 이번 과제의 클라이언트는 윈도우를 사용하기로 한다. MySQL 페키지는 크게 다음과 같이 3그룹의 프로그램으로 구성된다.

. MySQL 서버 엔진 (mysqld)
. 클라이언트 프로그램 (mysql)
. 그 외 utility 프로그램

이번 과제에서는 mysql 이라는 클라이언트 프로그램과 OS Shell을 이용하는 방법과 OS Shell이 제공하는 표준 출력을 재 가공하기 위한 PHP 스크립트를 이용하여 데이트베이스 엑세스 방법을 이해하고 한다.

2.1 서버와 클라이언트의 상호 데이트 송수신 관계

mysql 클라이언트 프로그램은 서버로 sql 명령어를 전달하고 서버엔진이 그 결과를 산출해 주면 이것을 받아 user에게 feekback하는 역할을 한다. [그림 1]은 이러한 관계를 그림으로 표시한 것이다.

2.2 SQL 명령어 전달 방법

클라이언트 측면, 즉 사용자 입장에, sql 명령을 전달하는 방법은 크게 3가지로 나눌 수 있는데 그 방법은 다음과 같다.

. mysql 서버의 shell을 얻어서 직접 명령을 입력하는 경우
. mysql 클라이언트에 sql 명령어군 직접 실어 보내는 경우
. mysql 클라이언트에 미리 작성해 둔 sql명령군의 파일을 전달하는 경우
. 웹 기반에서 OS shell을 통하여 명령을 전달하는 경우

2.2.1 서버 shell 직접 이용 방식

MySQL 서버의 shell을 얻어서 직접 명령을 전달하는 경우에 대해 살펴보자. 우선 사용자는 OS가 제공하는 shell 프롬프트에서 다음고 같은 명령어를 사용하여 데이터 베이스 서버 엔진에 접속 할 수 있다.

# mysql -h host_name -u user_id -p

-h는 데이터베이스 서버이며 -u는 데이터베이스를 사용할 수 있는 권한을 가진 사용자가 된다. 이 사용자는 데이터베이스 서버의 mysql.user (DB.Table)에 등록된 사용자인 경우이다. -p는 사용자의 개인 password이다. 이 과정에서 정상적인 접속이 이루어지면 MySQL 서버는 다은과 같은 프롬프트를 사용자에게 제공해 준다.

….. some message
>

2.2.2 mysql 클라이언트에 sql 명령어군 직접 실어 보내는 경우

# mysql -h host_name -u user_id -p -e ‘sql명령; sq명령; sql명령‘

이 경우는 -e 선택 사항을 사용하는 경우이다. 간단한 명령을 전달하여 그 결과를 보는데 편리하다고 하겠다. 간단히 예를 들면 다음과 같다.

# mysql -h host_name -u user_id -p -e ‘use mydb; show tables; select * from mystbl where myid=10’

앞서 설명한 것과 같이 OS shell에서 사용함으로써 제한된 sql 명령어만 제공할 수 있다. 많은 명령어는 기술하지 못하며, sql명령과 명령 사이에는 mysql 서버 엔진이 명령 구분기호로 사용하는 ;을 적어주면 되겠다.

2.2.3 mysql 클라이언트에 미리 작성해 둔 sql명령군의 파일을 전달하는 경우

이 방법은 OS가 제공하는 필터 기능을 활용하는 방법으로 리눅스 시스템에 설치된 mysql 서버엔진의 경우 명령 history, 방향키에 기존에 사용한 명령이 저장되어 있음, 를 유용하게 사용할 수 있지만, 윈도우즈 환경에서는 그렇지 못하다. 따라서 이 방법은 이러한 문제점을 쉽게 극복할 수 있으며, 초보자들에게 유영하리라 생각한다.

# mysql -h host_name -u user_id -p < path/file.sql

여기서 file.sql은 미리 sql 명령어를 만들어 준비한 것이라고 하겠다. 그럼 간단한 예제를 보다. 편의상 sql 명령어를 모아 두었으므로 파일 확장자를 sql로 했다. 사용자의 편의에 따라 변경하면 되겠다. path는 사용자가 만든 directory이다. 그럼 예제를 보자

$ mkdir -p ~/app/sql
$ vi test.sql

아래는 test.sql 파일의 내용이다. #은 mysql 서버가 remark로 인식한다. 따라서 remark를 붙이는 버릇이 습관화 되면 데이터베이스를 사용하는 모든 사용자에게 이해를 도울 수 있다고 하겠다.


# mysql test를 위한 명령어
# file : file.sql
# data :04.4.25
# auth : Lim.Y.K

use mydb;
show tables;
describe mytbl;
select * from mytbl where myid > 10;
# end of sql

이 파일의 저장위치는 앞서 만든 ~/app/sql이 되겠다. 그리고 다음과 같이 서버엔진에 접근가능하다.

# mysql -h host_name -u user_id -p < ~/app/sql/test.sql

2.2.4 웹 기반에서 OS shell을 통하여 명령을 전달하는 경우

오늘날 웹 기반 프로그래밍에 가장 많이 사용되는 PHP script 언어를 이용하여 mysql에 접근하는 방법에 대하여 알아보자. 이 방법은 PHP 자체 내장 함수를 이용하는 방법과 OS shell의 출력 결과를 backtick로 받아오는 방법으로 나누어 사용 가능하다. 함수를 이용하는 방법은 그 절차가 많이 복잡하다. 그러나 각 row,col 에 대한 유연한 처리가 가능하지만 backtick으로 처리하는 경우는 그렇지 못하다. 어떤 방법을 사용하는 것이 혀냊 적용하고자 하는 시스템에 적합한지는 사용자 몫이라고 하겠다.

본 과제에서는 backtick을 이용하여 mysql status를 모니터링 하고자 하는데 적용해 보았다. 우선 PHP의 backtick을 이용하기 위해서는 backtick 연산자 (·)에 대하여 알아야 할 것이다. ’(작은 따옴표)의 반대모양으로 키보드 판에 존재한다. 1번키 좌측. 이것은 PHP가 제공해 주는 system 함수와 비슷한 역할을 한다고 생각하면 아주 쉬울 것이다. 우선 mysql 접속을 위한 폼을 하나 만들어 이것을 post하고 서버접속 후 질의 결과를 받아와 출력해 주는 PHP 스크립트를 작성하면 되겠다.

스크립트 내부를 보면 알 수 있듯이 2.2.3, 2.2.3의 방법을 모두 적용 할 수 있는 장점이 있다. 여기에 간단한 스크립트를 제공한다. 아래 스크립트를 보고 사용하는데 많은 도움이 되기를 바란다.


<?
// form : mysql 접속 정보 입력
// file : out.php
// auth : 임영규 @ Gnome Institute Head
// date : '04. 04.23
//
$m_host=$HTTP_POST_VARS["f_host"];
$sqlcmd=$HTTP_POST_VARS["sql"];
$m_db=$HTTP_POST_VARS["db"];
$m_name=$HTTP_POST_VARS["f_name"];
$m_pw=$HTTP_POST_VARS["f_pw"];

// host 확인
if (empty($m_host))
   $m_host="localhost";

// DB 확인
if (!empty($m_db))
$cmd="mysql -h $m_host -e 'use $m_db; $sqlcmd;' -u $m_name --password=$m_pw". " | cat -b";
else
$cmd="mysql -h $m_host -e '$sqlcmd;' -u $m_name --password=$m_pw". " | cat -b";

// 특수문자 제거
$cmd=stripslashes($cmd);

// 조합 명령 출력
echo $cmd."<br>";

// 질의 전송 및 결과 획득
$result=`$cmd`;

// form 정보 출력
echo "DataBase : ".$m_db."<br> SQL = ".$sqlcmd."<br>";

// mysql 질의 결과 출력
echo "<textarea name=res rows=30 cols=100>";

if (empty($result))
   echo "access fail. Permition denied";
else
  echo $result;

echo "</textarea> ";
?>

3. 결론

여러 가지로 mysql 서버의 접근 방법을 알아보았다. 이번 과제를 하면서 상당히 많은 흥밋거리가 있음을 알게 되었다. 또한 함수 이용과 backtick 이용의 구분 방법 및 편리함에 대한 이해도 구할 수 있었다. 어떤 것이 효율적인지 잘 정리하고 여러분이 mysql 데이터베이스를 이용함에 있어 많은 편리를 보장 받았으면 한다. 또한 많은 좋은 결과가 있기를 바란다.

http://www.devshed.com/c/a/MySQL/Null-and-Empty-Strings/ 에서 참조

Before I actually start, here’s a little quiz. Is null equivalent to “” ? If you said yes, you definitely need to go through this article, and even if you know that “” is an empty string and not equivalent to NULL, you might want to run through the article.

Let’s start by talking about this concept in the context of PHP. The definition of null for PHP goes like this: “The special NULL value represents that a variable has no value. NULL is the only possible value of type NULL.”

Note: The null type was introduced in PHP 4.

A variable is considered to be NULL if

  • it has been assigned the constant NULL.

  • it has not been set to any value yet.

  • it has been unset().

Furthermore, there is only one value of type NULL, and that is the case-insensitive keyword NULL.

<?php

$var = NULL;

?>

Possible cases where you may misunderstand this and run into problems are:

  1. $var is an empty string

$var = “” ;

$z = is_null($var) ;

return $z ; // returns false

OR

$var = “ ;

$z = is_null($var) ;

return $z ; // returns false

  1. $var is an empty array

$var = array() ;

$z = is_null($var) ;

return $z ; // returns false

The cases where null behaves as it is intended to are: 

$var = null ; // case is not a problem – NULL, null, Null ..are all same

$z = is_null($var) ;

return $z ; // returns true

// $var is undefined or unset or uninitialized

$z = is_null($var) ;

return $z ; // returns true

In most cases we are used to displaying the contents of $var in "if” style i.e.,

if ( $var ) { // returns true when var has value and $var is not equal to zero

echo $var ; // or do some processing

}

This way of coding has a problem: whenever $var has zero value, it will not return true. To escape this predicament, sometimes one uses is_null(), which again causes the problems I already listed above.

http://networker.jinbo.net/zine/view.php?board=networker_4&id=1756 에서 참조

얼마 전에 시간을 가장 가치 없게 사용하는 방법 중 상위 3위안에 랭크될 것이 분명한 동원예비군 훈련을 갔다 왔습니다. 제가 갔던 부대는 의정부에 있는 통신대대인데, 여기서 하는 일은 말 그대로 부대 사이의 통신을 연결하고 관리하는 일입니다. 통신대는 크게 전송조, 교환조, 유무선조로 나뉘어 집니다. 저는 이 중 교환조에 속하는데, 교환조는 타 부대의 교환조와의 네트워크를 구성하여 이를 제어하고, 내부의 전화를 연결해 번호를 부여하는 등의 역할을 수행합니다. 옛날 전화에 있었다는 교환수와 비슷한 역할이라 할 수 있긴 하지만, 전화번호를 부여하고 음성 통신뿐만 아니라 데이터 통신까지 중계한다는 점에서 보다 하는 일의 범위가 넓다고 할 수 있겠죠. 여하튼 하려는 얘기는 이게 아니고-_-;;;

image

컴퓨터 사이의 네트워크도 전화와 비슷한 중계 구조를 가지고 있습니다. 집마다 있는 전화가 전화번호로서 고유하게 식별할 수 있듯이, 네트워크에 연결된 컴퓨터(정확히는 모뎀 또는 랜카드 등의 네트워크 장비)는 고유한 식별 번호를 가집니다. 예를 들면 공유 폴더로 접근할 때 사용하는 윈도의 인트라넷에서는 네트워크 아이디라는 것을 지정하도록 되어 있습니다.(“시스템 등록 정보"의 "네트워크 식별” 탭에서 지정 가능합니다.) 그리고 일반적인 인터넷 연결에 사용하는 TCP/IP 네트워크에서는 바로 IP 주소라는 것을 발급하도록 되어 있죠.

TCP/IP는 인터넷의 가장 기본적인 프로토콜입니다. 프로토콜은 (전에도 한 번 설명했지만) 통신을 위해 정의된 규약입니다. TCP(Transmission Control Protocol)라는 프로토콜과 IP(Internet Protocol)라는 프로토콜이 결합된 것이 TCP/IP로서, 웹(WWW)에 사용되는 HTTP, 원격 컴퓨터 제어를 위한 텔넷(Telnet), 파일 전송을 위한 FTP, 메일에 사용되는 SMTP등이 TCP/IP를 사용하는 프로토콜들입니다. 물론 인터넷 통신을 하기 위해서는 TCP/IP 프로그램이 반드시 필요합니다. 당연히 윈도나 리눅스 등의 거의 모든 OS에는 TCP/IP를 사용하기 위한 프로그램이 기본적으로 깔려 있죠. 보통 IP 주소를 잡기 위해 들어가는 설정 프로그램도 이런 제어 프로그램의 일부입니다.

IP 주소는 현재 IPv4라는 방식이 사용되고 있습니다. “211.255.23.35” 같이 0~255 사이의 수를 4개 붙여서 사용하는 것이 IPv4의 방식입니다. IP 주소는 앞에서 언급했듯이 전화번호같이 네트워크 상에서 특정 컴퓨터(호스트host)를 인식 가능하게 하는 고유한 번호입니다. 따라서 전 세계의 모든 컴퓨터에 IP 주소를 붙여주어야 한다고 하면, 이론상 각 자리마다 256개의 숫자가 들어갈 수 있으므로 256x256x256x256=4,294,967,296, 즉 대략 43억 대의 컴퓨터가 붙을 수 있게 되는 셈이죠.

하지만 복잡한 IP 주소 부여 법칙에 의해 실제로는 이보다 적은 수의 IP 주소만이 사용할 수 있으며, 특히 각 국가마다 할당된 IP 주소의 영역이 제한되어 있기 때문에 IP 주소는 지금도 매우 부족한 상황에 놓여 있죠. 그래서 방화벽으로 구성된 폐쇄적인 네트워크에서는 보안상의 이유와 더불어 사용 가능한 IP 주소를 늘이기 위해 내부에서만 사용할 수 있는 IP를 따로 정의해 쓰기도 하고, 하나의 IP를 여러 대의 컴퓨터가 공유하기도 합니다. 가장 흔한 예가 인터넷 공유기를 사용해 2대 이상의 컴퓨터를 붙여 쓰는 경우인데요, 이 경우에는 공유기가 라우터의 역할을 수행하여 별도의 IP를 각 컴퓨터에 붙여줍니다.

image

라우터는 독립된 네트워크의 시작점이 되는 장비로서 보통 게이트웨이의 역할을 수행합니다. IP주소를 직접 잡아 보신 분들은 아시겠지만 IP주소를 설정하기 위해 IP주소는 물론이고 게이트웨이 주소와 DNS 주소를 넣어줘야 하는데, 그 게이트웨이 주소가 일반적으로 이 라우터의 주소입니다. 게이트웨이와 DNS에 대한 설명은 조금 길어지므로 다음에 다루도록 하겠습니다.

만약 자신의 컴퓨터가 네트워크에 등록되어 있는데 IP 주소를 알아야 하는 경우가 있습니다. 물론 컴퓨터의 설정 정보를 뒤져보면 어딘가에 IP 주소를 정의해 놓은 곳이 있겠죠. 그러나 그 곳을 찾기 귀찮거나 IP를 자동 설정으로 해 놓으신 분들은 TCP/IP 프로그램을 이용하여 쉽게 IP 주소를 알아낼 수 있습니다. 윈도 2000이나 윈도 XP 이상을 사용하시는 분들은 윈도의 커맨드 창에서 “ipconfig"를 입력하시면 관련 정보가 쉽게 나타납니다.(커맨드 창을 띄우시려면 윈도의 시작 ->실행에서 "cmd"를 입력하시면 되죠.) 리눅스 등 유닉스 계열의 OS에서는 일반적으로 /sbin/ifconfig를 실행하시면 관련 정보가 나옵니다.

image

위에 보면 제 컴퓨터의 IP 주소는 10.0.1.3으로 나오는데요, 일반적인 IP 주소는 이런 식으로 발급되지 않습니다. 10.으로 시작하는 IP는 보통 내부에서만 사용 가능한 IP 주소인데요, 저희 집에서 공유기를 사용해 여러 컴퓨터를 연결해 사용하기 때문에 앞에서 얘기한 대로 제 공유기가 임의로 발급해 준 가짜 주소를 얻은 셈이죠. 제 컴퓨터의 진짜 IP 주소는 공유기가 가지고 있으며, 제 공유기에 연결된 컴퓨터는 외부로 접속할 경우 공유기가 갖고 있는 그 IP 주소를 모두 사용하게 됩니다.

IP 주소도 하나의 자원이다 보니 사용하는 사람이 많아질수록 점차 부족 현상이 심각해질 것임이 분명합니다. 만약 지구상의 60억 인구가 한 대씩 네트워크에 연결된 컴퓨터만 갖고 있어도 일찌감치 IP 주소는 동이 날 터이고, 특정한 주인이 없는 컴퓨터들(예컨대 서버들)도 수없이 많이 있을 테니까요. 그래서 IP 주소의 범위를 늘이기 위해 IPv6라는 방식을 도입하려 하고 있습니다. IPv6는 이론적으로 기존의 IPv4보다 65536배 많은 주소를 사용할 수 있기 때문에, 아마도 IP 주소 부족 현상은 해소 가능하다고 할 수 있겠죠. 하지만 집을 지어도지어도 내 집을 가지기가 어려운 것처럼, 왠지 IP 주소 부족 현상이 쉽게 해결될지 의심이 되는 것은…나쁜 성격 탓일까요? ;;;

When you issue a query, the MySQL Query Optimizer tries to devise an optimal plan for query execution. You can see information about the plan by prefixing the query with EXPLAIN. EXPLAIN is one of the most powerful tools at your disposal for understanding and optimizing troublesome MySQL queries, but it’s a sad fact that many developers rarely make use of it. In this article you’ll learn what the output of EXPLAIN can be and how to use it to optimize your schema and queries.

Understanding EXPLAIN’s Output

Using EXPLAIN is as simple as pre-pending it before the SELECT queries. Let’s analyze the output of a simple query to familiarize yourself with the columns returned by the command.

1 EXPLAIN SELECT * FROM categoriesG
********************** 1. row **********************
           id: 1
  select_type: SIMPLE
        table: categories
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4
        Extra: 
1 row in set (0.00 sec)

It may not seem like it, but there’s a lot of information packed into those 10 columns! The columns returned by the query are:

  • id – a sequential identifier for each SELECT within the query (for when you have nested subqueries)
  • select_type – the type of SELECTquery. Possible values are:
    • SIMPLE – the query is a simple SELECT query without any subqueries or UNIONs
    • PRIMARY – the SELECT is in the outermost query in a JOIN
    • DERIVED – the SELECT is part of a subquery within a FROM clause
    • SUBQUERY – the first SELECT in a subquery
    • DEPENDENT SUBQUERY – a subquery which is dependent upon on outer query
    • UNCACHEABLE SUBQUERY – a subquery which is not cacheable (there are certain conditions for a query to be cacheable)
    • UNION – the SELECT is the second or later statement of a UNION
    • DEPENDENT UNION – the second or later SELECT of a UNION is dependent on an outer query
    • UNION RESULT – the SELECT is a result of a UNION
  • table – the table referred to by the row
  • type– how MySQL joins the tables used. This is one of the most insightful fields in the output because it can indicate missing indexes or how the query is written should be reconsidered. Possible values are:
    • system – the table has only zero or one row
    • const – the table has only one matching row which is indexed. This is the fastest type of join because the table only has to be read once and the column’s value can be treated as a constant when joining other tables.
    • eq_ref – all parts of an index are used by the join and the index is PRIMARY KEY or UNIQUE NOT NULL. This is the next best possible join type.
    • ref – all of the matching rows of an indexed column are read for each combination of rows from the previous table. This type of join appears for indexed columns compared using = or <=> operators.
    • fulltext – the join uses the table’s FULLTEXT index.
    • ref_or_null – this is the same as ref but also contains rows with a null value for the column.
    • index_merge – the join uses a list of indexes to produce the result set. The key column of EXPLAIN‘s output will contain the keys used.
    • unique_subquery – an IN subquery returns only one result from the table and makes use of the primary key.
    • index_subquery – the same as unique_subquery but returns more than one result row.
    • range – an index is used to find matching rows in a specific range, typically when the key column is compared to a constant using operators like BETWEEN, IN, >, >=, etc.
    • index – the entire index tree is scanned to find matching rows.
    • all – the entire table is scanned to find matching rows for the join. This is the worst join type and usually indicates the lack of appropriate indexes on the table.
  • possible_keys – shows the keys that can be used by MySQL to find rows from the table, though they may or may not be used in practice. In fact, this column can often help in optimizing queries since if the column is NULL, it indicates no relevant indexes could be found.
  • key – indicates the actual index used by MySQL. This column may contain an index that is not listed in the possible_key column. MySQL optimizer always look for an optimal key that can be used for the query. While joining many tables, it may figure out some other keys which is not listed in possible_key but are more optimal.
  • key_len – indicates the length of the index the Query Optimizer chose to use. For example, a key_len value of 4 means it requires memory to store four characters. Check out MySQL’s data type storage requirements to know more about this.
  • ref – Shows the columns or constants that are compared to the index named in the key column. MySQL will either pick a constant value to be compared or a column itself based on the query execution plan. You can see this in the example given below.
  • rows – lists the number of records that were examined to produce the output. This Is another important column worth focusing on optimizing queries, especially for queries that use JOIN and subqueries.
  • Extra – contains additional information regarding the query execution plan. Values such as “Using temporary”, “Using filesort”, etc. in this column may indicate a troublesome query. For a complete list of possible values and their meaning, check out the MySQL documentation.

You can also add the keyword EXTENDED after EXPLAIN in your query and MySQL will show you additional information about the way it executes the query. To see the information, follow your EXPLAIN query with SHOW WARNINGS. This is mostly useful for seeing the query that is executed after any transformations have been made by the Query Optimizer.

1 EXPLAIN EXTENDED SELECT City.Name FROM City
2 JOIN Country ON (City.CountryCode = Country.Code)
3 WHERE City.CountryCode = 'IND' AND Country.Continent = 'Asia'G
********************** 1. row **********************
           id: 1
  select_type: SIMPLE
        table: Country
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 3
          ref: const
         rows: 1
     filtered: 100.00
        Extra: 
********************** 2. row **********************
           id: 1
  select_type: SIMPLE
        table: City
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4079
     filtered: 100.00
        Extra: Using where
2 rows in set, 1 warning (0.00 sec)
1 SHOW WARNINGSG
********************** 1. row **********************
  Level: Note
   Code: 1003
Message: select `World`.`City`.`Name` AS `Name` from `World`.`City` join `World`.`Country` where ((`World`.`City`.`CountryCode` = 'IND'))
1 row in set (0.00 sec)

Troubleshooting Performance with EXPLAIN

Now let’s take a look at how we can optimize a poorly performing query by analyzing the output of EXPLAIN. When dealing with a real-world application there’ll undoubtedly be a number of tables with many relations between them, but sometimes it’s hard to anticipate the most optimal way to write a query.

Here I’ve created a sample database for an e-commerce application which does not have any indexes or primary keys, and will demonstrate the impact of such a bad design by writing a pretty awful query. You can download the schema sample from GitHub.

1 EXPLAIN SELECT * FROM
2 orderdetails d
3 INNER JOIN orders o ON d.orderNumber = o.orderNumber
4 INNER JOIN products p ON p.productCode = d.productCode
5 INNER JOIN productlines l ON p.productLine = l.productLine
6 INNER JOIN customers c on c.customerNumber = o.customerNumber
7 WHERE o.orderNumber = 10101G
********************** 1. row **********************
           id: 1
  select_type: SIMPLE
        table: l
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 7
        Extra: 
********************** 2. row **********************
           id: 1
  select_type: SIMPLE
        table: p
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 110
        Extra: Using where; Using join buffer
********************** 3. row **********************
           id: 1
  select_type: SIMPLE
        table: c
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 122
        Extra: Using join buffer
********************** 4. row **********************
           id: 1
  select_type: SIMPLE
        table: o
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 326
        Extra: Using where; Using join buffer
********************** 5. row **********************
           id: 1
  select_type: SIMPLE
        table: d
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 2996
        Extra: Using where; Using join buffer
5 rows in set (0.00 sec)

If you look at the above result, you can see all of the symptoms of a bad query. But even if I wrote a better query, the results would still be the same since there are no indexes. The join type is shown as “ALL” (which is the worst), which means MySQL was unable to identify any keys that can be used in the join and hence the possible_keys and key columns are null. Most importantly, the rows column shows MySQL scans all of the records of each table for query. That means for executing the query, it will scans 7 × 110 × 122 × 326 × 2996 = 91,750,822,240 records to find the four matching results. That’s really horrible, and it will only increase exponentially as the database grows.

Now lets add some obvious indexes, such as primary keys for each table, and execute the query once again. As a general rule of thumb, you can look at the columns used in the JOIN clauses of the query as good candidates for keys because MySQL will always scan those columns to find matching records.

01 ALTER TABLE customers
02     ADD PRIMARY KEY (customerNumber);
03 ALTER TABLE employees
04     ADD PRIMARY KEY (employeeNumber);
05 ALTER TABLE offices
06     ADD PRIMARY KEY (officeCode);
07 ALTER TABLE orderdetails
08     ADD PRIMARY KEY (orderNumber, productCode);
09 ALTER TABLE orders
10     ADD PRIMARY KEY (orderNumber),
11     ADD KEY (customerNumber);
12 ALTER TABLE payments
13     ADD PRIMARY KEY (customerNumber, checkNumber);
14 ALTER TABLE productlines
15     ADD PRIMARY KEY (productLine);
16 ALTER TABLE products 
17     ADD PRIMARY KEY (productCode),
18     ADD KEY (buyPrice),
19     ADD KEY (productLine);
20 ALTER TABLE productvariants 
21     ADD PRIMARY KEY (variantId),
22     ADD KEY (buyPrice),
23     ADD KEY (productCode);

Let’s re-run the same query again after adding the indexes and the result should look like this:

********************** 1. row **********************
           id: 1
  select_type: SIMPLE
        table: o
         type: const
possible_keys: PRIMARY,customerNumber
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: 
********************** 2. row **********************
           id: 1
  select_type: SIMPLE
        table: c
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: 
********************** 3. row **********************
           id: 1
  select_type: SIMPLE
        table: d
         type: ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 4
        Extra: 
********************** 4. row **********************
           id: 1
  select_type: SIMPLE
        table: p
         type: eq_ref
possible_keys: PRIMARY,productLine
          key: PRIMARY
      key_len: 17
          ref: classicmodels.d.productCode
         rows: 1
        Extra: 
********************** 5. row **********************
           id: 1
  select_type: SIMPLE
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: 
5 rows in set (0.00 sec)

After adding indexes, the number of records scanned has been brought down to 1 × 1 × 4 × 1 × 1 = 4. That means for each record with orderNumber 10101 in the orderdetails table, MySQL was able to directly find the matching record in all other tables using the indexes and didn’t have to resort to scanning the entire table.

In the first row’s output you can see the join type used is “const,” which is the fastest join type for a table with more than one record. MySQL was able to use PRIMARY key as the index. The ref column shows “const,” which is nothing but the value 10101 used in the query’s WHERE clause.

Let’s take a look at one more example query. Here we’ll basically take the union of two tables, products and productvariants, each joined with productline. productvariants table consists of different product variants with productCode as reference keys and their prices.

01 EXPLAIN SELECT * FROM (
02 SELECT p.productName, p.productCode, p.buyPrice, l.productLine, p.status, l.status AS lineStatus FROM
03 products p
04 INNER JOIN productlines l ON p.productLine = l.productLine
05 UNION
06 SELECT v.variantName AS productName, v.productCode, p.buyPrice, l.productLine, p.status, l.status AS lineStatus FROM productvariants v
07 INNER JOIN products p ON p.productCode = v.productCode
08 INNER JOIN productlines l ON p.productLine = l.productLine
09 ) products
10 WHERE status = 'Active' AND lineStatus = 'Active' AND buyPrice BETWEEN 30 AND 50G
********************** 1. row **********************
           id: 1
  select_type: PRIMARY
        table: <derived2>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 219
        Extra: Using where
********************** 2. row **********************
           id: 2
  select_type: DERIVED
        table: p
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 110
        Extra: 
********************** 3. row **********************
           id: 2
  select_type: DERIVED
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: 
********************** 4. row **********************
           id: 3
  select_type: UNION
        table: v
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 109
        Extra: 
********************** 5. row **********************
           id: 3
  select_type: UNION
        table: p
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 17
          ref: classicmodels.v.productCode
         rows: 1
        Extra: 
********************** 6. row **********************
           id: 3
  select_type: UNION
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: 
********************** 7. row **********************
           id: NULL
  select_type: UNION RESULT
        table: <union2,3>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: 
7 rows in set (0.01 sec)

You can see a number of problem in this query. It scans all records in the products and productvariants tables. As there are no indexes on these tables for the productLine and buyPrice columns, the output’s possible_keys and key columns show null. The status of products and productlines is checked after the UNION, so moving them inside the UNION will reduce the number of records. Let’s add some additional indexes and rewrite the query.

1 CREATE INDEX idx_buyPrice ON products(buyPrice);
2 CREATE INDEX idx_buyPrice ON productvariants(buyPrice);
3 CREATE INDEX idx_productCode ON productvariants(productCode);
4 CREATE INDEX idx_productLine ON products(productLine);
01 EXPLAIN SELECT * FROM (
02 SELECT p.productName, p.productCode, p.buyPrice, l.productLine, p.status, l.status as lineStatus FROM products p
03 INNER JOIN productlines AS l ON (p.productLine = l.productLine AND p.status = 'Active' AND l.status = 'Active'
04 WHERE buyPrice BETWEEN 30 AND 50
05 UNION
06 SELECT v.variantName AS productName, v.productCode, p.buyPrice, l.productLine, p.status, l.status FROM productvariants v
07 INNER JOIN products p ON (p.productCode = v.productCode AND p.status = 'Active'
08 INNER JOIN productlines l ON (p.productLine = l.productLine AND l.status = 'Active')
09 WHERE
10 v.buyPrice BETWEEN 30 AND 50
11 ) productG
********************** 1. row **********************
          id: 1
  select_type: PRIMARY
        table: <derived2>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 12
        Extra: 
********************** 2. row **********************
           id: 2
  select_type: DERIVED
        table: p
         type: range
possible_keys: idx_buyPrice,idx_productLine
          key: idx_buyPrice
      key_len: 8
          ref: NULL
         rows: 23
        Extra: Using where
********************** 3. row **********************
           id: 2
  select_type: DERIVED
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: Using where
********************** 4. row **********************
           id: 3
  select_type: UNION
        table: v
         type: range
possible_keys: idx_buyPrice,idx_productCode
          key: idx_buyPrice
      key_len: 9
          ref: NULL
         rows: 1
        Extra: Using where
********************** 5. row **********************
           id: 3
  select_type: UNION
        table: p
         type: eq_ref
possible_keys: PRIMARY,idx_productLine
          key: PRIMARY
      key_len: 17
          ref: classicmodels.v.productCode
         rows: 1
        Extra: Using where
********************** 6. row **********************
           id: 3
  select_type: UNION
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: Using where
********************** 7. row **********************
           id: NULL
  select_type: UNION RESULT
        table: <union2,3>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: 
7 rows in set (0.01 sec)

As you can see in the result, now the number of approximate rows scanned is significantly reduced from 2,625,810 (219 × 110 × 109) to 276 (12 × 23), which is a huge performance gain. If you try the same query, without the previous re-arrangements, simply after adding the indexes, you wouldn’t see much of a reduction. MySQL isn’t able to make use of the indexes since it has the WHERE clause in the derived result. After moving those conditions inside the UNION, it is able to make use of the indexes. This means just adding an index isn’t always enough; MySQL won’t be able to use it unless you write your queries accordingly.

Summary

In this article I discussed the MySQL EXPLAIN keyword, what its output means, and how you can use its output to construct better queries. In the real world, it can be more useful than the scenarios demonstrated here. More often than not, you’ll be joining a number of tables together and using complex WHERE clauses. Simply added indexes on on a few columns may not always help, and then it’s time to take a closer look at your queries themselves.