2013년 12월 29일 일요일

3_3_글로벌_SEQ생성예제

REPLACE INTO 문 사용
*주의사항


*myIsam 으로 하고 많이 사용된다면 시퀀스당 하나씩 테이블을 설정해라
 innodb를 쓸경우 잠금때문에 성능 저하가 발생 할 수 있다.
  why
   하나의 클라이언트가 번호를 가져갈며 트랜 잭션을 걸면 다른 클라이언트가 그 트랜잭션이 끝날때까지 대기 하여야함
*복제를 사용할 경우 nextVal()의 펑션을 사용하면 안된다.
 why?
  복제에서 가져갈때
  nextVal()을 호출 함으로 시퀀스가 두번 증가

*select * from TEST.compare a where id not in (select id from TEST.test1 b);
 빈번호 찾는 쿼리



http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/

 * replace 문은 atomic 이 보장되는 문장이다.
 
 1.테이블 생성
  CREATE TABLE `Tickets64` (
    `id` bigint(20) unsigned NOT NULL auto_increment,
    `stub` char(1) NOT NULL default '',
    PRIMARY KEY  (`id`),
    UNIQUE KEY `stub` (`stub`)
  ) ENGINE=MyISAM

 2.
  SELECT * FROM Tickets64;

  REPLACE INTO Tickets64 (stub) VALUES ('a');

  SELECT LAST_INSERT_ID();

 3.
  -- --------------------------------------------------------------------------------
  -- Routine DDL
  -- Note: comments before and after the routine body will not be stored by the server
  -- --------------------------------------------------------------------------------
  DELIMITER $$

  CREATE FUNCTION `testNextVal` ()
  RETURNS INTEGER
  BEGIN
   REPLACE INTO Tickets64 (stub) VALUES ('a');
   RETURN LAST_INSERT_ID();
  END
 4.
  select testNextVal ();
 
 6.테스트 테이블
  CREATE TABLE test1 (
       id INT NOT NULL ,
       name CHAR(30) NOT NULL,
       PRIMARY KEY (id)
  ) ENGINE=INNODB;

 7.테스트 프로시저
   -- --------------------------------------------------------------------------------
  -- Routine DDL
  -- Note: comments before and after the routine body will not be stored by the server
  -- --------------------------------------------------------------------------------
  DELIMITER $$

  CREATE  PROCEDURE `insertsValAtTableWithNTimes`(
      IN  i_table_name      VARCHAR(255)
  ,   IN  i_n_times     INT
  )
  BEGIN
         DECLARE x  INT DEFAULT 1;
       SET @v_table_name :=  i_table_name;
       SET @v_sql := 'INSERT INTO @p_table_name(id, name) VALUES(? ,?);';
       SET @v_sql              :=  REPLACE(@v_sql,'@p_table_name',@v_table_name);
       

       PREPARE stmt FROM @v_sql;   
       
     WHILE x  <= i_n_times DO
       SET @v_id := 0;
       SELECT testNextVal () INTO @v_id;
       SET @v_name := CONCAT(x,'test');
       EXECUTE stmt USING @v_id, @v_name;
       SET x = x+1;
     END WHILE;
         
     DEALLOCATE PREPARE stmt;
        END

 8테스트
  SELECT * FROM Tickets64;

  TRUNCATE TABLE Tickets64;

  SELECT * FROM test1;

  TRUNCATE TABLE test1;


  call insertsValAtTableWithNTimes('test1',10);

 비교테이블
  delimiter $$

  CREATE TABLE T_COMPARE (
    `id`  bigint(20) unsigned NOT NULL auto_increment,
    `name` char(30) NOT NULL,
    PRIMARY KEY (`id`)
  ) ENGINE=MyISAM DEFAULT CHARSET=utf8$$
 
 비교 프로시저
   -- --------------------------------------------------------------------------------
  -- Routine DDL
  -- Note: comments before and after the routine body will not be stored by the server
  -- --------------------------------------------------------------------------------
  DELIMITER $$

  CREATE PROCEDURE `createTestData`(
      IN  i_table_name      VARCHAR(255)
  ,   IN  i_n_times     INT
  )
  BEGIN
         DECLARE x  INT DEFAULT 1;
       SET @v_table_name :=  i_table_name;
       SET @v_sql := 'INSERT INTO @p_table_name(name) VALUES(?);';
       SET @v_sql              :=  REPLACE(@v_sql,'@p_table_name',@v_table_name);
       

       PREPARE stmt FROM @v_sql;   
       
     WHILE x  <= i_n_times DO
       SET @v_name := CONCAT('test');
       EXECUTE stmt USING @v_name;
       SET x = x+1;
     END WHILE;
         
     DEALLOCATE PREPARE stmt;
  END
 
 테스트 데이터 만들기
  call createTestData('t_compare',1000000);
  
  확인

  select max(id) FROM t_compare;

  SELECT count(*) FROM t_compare;

 테스트
  창뛰우고

  call insertsValAtTableWithNTimes('test1',333333);


  show processlist; 애들이 경쟁하는게 보인다.

 잘못된 데이터 있나 체크 
  select count(*) from t_compare b where b.id not in (select id from test1);

 마스터-마스터 구조로 만들경우
  TicketServer1:
  auto-increment-increment = 2
  auto-increment-offset = 1

  TicketServer2:
  auto-increment-increment = 2
  auto-increment-offset = 2
 
 Flickr에서 현재 사용중



 


insert update문

 1.테이블 생성
  CREATE TABLE `TEST`.`sequence_data` (

  `sequence_name` varchar(100) NOT NULL,

  `sequence_increment` int(11) unsigned NOT NULL DEFAULT 1,

  `sequence_min_value` int(11) unsigned NOT NULL DEFAULT 1,

  `sequence_max_value` bigint(20) unsigned NOT NULL DEFAULT 18446744073709551615,

  `sequence_cur_value` bigint(20) unsigned DEFAULT 1,

  `sequence_cycle` boolean NOT NULL DEFAULT FALSE,

  PRIMARY KEY (`sequence_name`)
  ) ENGINE=MyISAM;


 2.시퀀스 입력
  INSERT INTO TEST.sequence_data (sequence_name) VALUE ('sq_my_sequence');

  INSERT INTO TEST.sequence_data(sequence_name, sequence_increment, sequence_max_value)VALUE('sq_sequence_2', 10, 100)

 3.프로시저 생성
  -- --------------------------------------------------------------------------------
  -- Routine DDL
  -- Note: comments before and after the routine body will not be stored by the server
  -- --------------------------------------------------------------------------------
  DELIMITER $$

  CREATE FUNCTION TEST.`nextval` (`seq_name` varchar(100))
  RETURNS bigint(20) NOT DETERMINISTIC
  BEGIN
   DECLARE cur_val bigint(20);
   
   SELECT
    sequence_cur_value INTO cur_val
   FROM
    TEST.sequence_data
   WHERE
    sequence_name = seq_name;

   IF cur_val IS NOT NULL THEN
    UPDATE
     TEST.sequence_data
    SET
     sequence_cur_value = IF ((sequence_cur_value + sequence_increment) > sequence_max_value,
              IF (sequence_cycle = TRUE,sequence_min_value,NULL),
              sequence_cur_value + sequence_increment)
    WHERE
     sequence_name = seq_name;
   END IF;

  RETURN cur_val;

  END
 4.getnextVal()
  SELECT TEST.nextval('sq_my_sequence');

 5.널이 리턴 될수 있는 상황
  둘다 에러 상황임
   When the sequence doesn’t exist
   When current sequence value out of range

 http://www.microshell.com/database/mysql/emulating-nextval-function-to-get-sequence-in-mysql/

 6.테스트 테이블
  CREATE TABLE TEST.test1 (
       id INT NOT NULL ,
       name CHAR(30) NOT NULL,
       PRIMARY KEY (id)
  ) ENGINE=INNODB;

 7.테스트 프로시저
   -- --------------------------------------------------------------------------------
  -- Routine DDL
  -- Note: comments before and after the routine body will not be stored by the server
  -- --------------------------------------------------------------------------------
  DELIMITER $$

  CREATE  PROCEDURE TEST.`insertsValAtTableWithNTimes`(
      IN  i_table_name      VARCHAR(255)
  ,   IN  i_n_times     INT
  )
  BEGIN
         DECLARE x  INT DEFAULT 1;
       SET @v_table_name :=  i_table_name;
       SET @v_sql := 'INSERT INTO TEST.@p_table_name(id, name) VALUES(? ,?);';
       SET @v_sql              :=  REPLACE(@v_sql,'@p_table_name',@v_table_name);
       

       PREPARE stmt FROM @v_sql;   
       
     WHILE x  <= i_n_times DO
       SET @v_id := 0;
       SELECT TEST.nextval('sq_my_sequence') INTO @v_id;
       SET @v_name := CONCAT(x,'test');
       EXECUTE stmt USING @v_id, @v_name;
       SET x = x+1;
     END WHILE;
         
     DEALLOCATE PREPARE stmt;
        END

 8테스트
  SELECT * FROM TEST.test1;

  TRUNCATE TABLE `TEST`.`test1`;


  mysql -h  -u hoeuser -p 
  use test;

  call TEST.insertsValAtTableWithNTimes('test1',1000);


 비교테이블
  delimiter $$

  CREATE TABLE `compare` (
    `id` int(11) NOT NULL,
    `name` char(30) NOT NULL,
    PRIMARY KEY (`id`)
  ) ENGINE=MyISAM DEFAULT CHARSET=utf8$$


  UPDATE `test`.`sequence_data` SET `sequence_cur_value`='1' WHERE `sequence_name`='sq_my_sequence';

  call TEST.insertsValAtTableWithNTimes('compare',60000);



Myisam 테이블을 사용해서 여러개가 그룹별 seq 만들기 
 Myisam table has a feature where , if a primary key contains 2 columns where one is an autoincrement and other is a varchar , then for each value of the text column the autoincrement column will create separate sequence of autoincrement numbers.

 CREATE TABLE IF NOT EXISTS `vt_sequences` (
   `type` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `name` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
   `created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   PRIMARY KEY (`type`,`id`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;


 In the above table type and id columns form the primary key. If the following insert is made :

 INSERT INTO `vt_sequences` (`type`) VALUES
 ('A'),
 ('A'),
 ('B'),
 ('B'),
 ('A');

 A 1 2011-10-22 15:29:04
 A 2 2011-10-22 15:29:04
 B 1 2011-10-22 15:29:04
 B 2 2011-10-22 15:29:04
 A 3 2011-10-22 15:29:04

 So the id column has a 1 for A and B separately. So now if we want autoincrement numbers resetting every month we could insert 2011-10 and 2011-11 in the type field and the autoincrement numbers would be independant.

 Another very useful thing about this technique is that if a particular entry is deleted , say A 3 is deleted , then next time when A is insert 3 would be reused , so autoincrement numbers get reused.

 and use get_last_inserted_id

  CREATE TABLE component_core ( 
      component_id INT AUTO_INCREMENT, 
      PRIMARY KEY(component_id) 
  ); 
   
  CREATE TABLE component_history ( 
      component_id INT NOT NULL, 
      version_id INT AUTO_INCREMENT, 
      DATA VARCHAR(20), 
      version_start_date DATETIME NOT NULL, 
      version_end_date DATETIME NOT NULL, 
      PRIMARY KEY(component_id,version_id) 
  ) ENGINE=MYISAM; 
   
  INSERT INTO component_core () VALUES ();
  INSERT INTO component_core () VALUES ();
   
  INSERT INTO component_history 
   (component_id, DATA, version_start_date, version_end_date)
   VALUES (1, 'data', NOW(), NOW());
  INSERT INTO component_history 
   (component_id, DATA, version_start_date, version_end_date)
   VALUES (1, 'data', NOW(), NOW());
  INSERT INTO component_history 
   (component_id, DATA, version_start_date, version_end_date)
   VALUES (1, 'data', NOW(), NOW());
   
  INSERT INTO component_history 
   (component_id, DATA, version_start_date, version_end_date)
   VALUES (2, 'data', NOW(), NOW());
  INSERT INTO component_history 
   (component_id, DATA, version_start_date, version_end_date)
   VALUES (2, 'data', NOW(), NOW());
  INSERT INTO component_history 
   (component_id, DATA, version_start_date, version_end_date)
   VALUES (2, 'data', NOW(), NOW());


앞에 키를 두어서 pk를 잡는거 어차피 샤딩 되니까 상관없다
 create table some_sharded_table(
  shard_key int,
  some_child_key bigint auto_increment not null,
  key(some_child_key),
  primary key (shard_key, some_child_key),
  data1 int,
  data2 char(10),
  …
 )

 You can safely move records between shards, because the primary key includes the directory provided key. The directory server maps keys to shards.

 You can then partition `some_sharded_table` nicely:
 alter table some_sharded_table partition by hash(shard_key) partitions 16;

 All the values related to one shard_key are now in one physical server, and the values are then partitioned again locally for improved performance through smaller indexes. This helps alleviate the overhead of the increased length of the secondary key used for the auto_increment value.

 You can use the primary key index to get the most recent N rows very efficiently for any shard_key, something that is very common in most sharded applications that I see.


last insert id 를 써서 사용하는 예제
 /* For this example, we'll put the sequences table in the test database. */
 USE test;
 /* Create a sequence table */
 CREATE TABLE IF NOT EXISTS sequences
 (name CHAR(20) PRIMARY KEY,
 val INT UNSIGNED);
 DROP FUNCTION IF EXISTS nextval;
 DELIMITER //
 /* The actual sequence function. Call nextval('seqname'), and it returns the next value. */
 /* If the named sequence does not yet exist, it is created with initial value 1. */
 CREATE FUNCTION nextval (seqname CHAR(20))
 RETURNS INT UNSIGNED
 BEGIN
 INSERT INTO sequences VALUES (seqname,LAST_INSERT_ID(1))
 ON DUPLICATE KEY UPDATE val=LAST_INSERT_ID(val+1);
 RETURN LAST_INSERT_ID();
 END
 //
 DELIMITER ;
 /* Let's now use a sequence in a test table... */
 CREATE TABLE IF NOT EXISTS data
 (id int UNSIGNED NOT NULL PRIMARY KEY DEFAULT 0,
 info VARCHAR(50));
 DROP TRIGGER nextval;
 /* The trigger only generates a new id if 0 is inserted. */
 /* The default value of id is also 0 (see the create table statement) so that makes it implicit. */
 CREATE TRIGGER nextval BEFORE INSERT ON data
 FOR EACH ROW SET new.id=IF(new.id=0,nextval('data'),new.id);
 TRUNCATE TABLE data;
 INSERT INTO data (info) VALUES ('bla');
 INSERT INTO data (info) VALUES ('foo'),('bar');
 SELECT * FROM data;