[MySQL Patch] Binlog文件预分配


之前已经介绍过实现思路(见  ),binlog预分配在两年前被Yoshinori Matsunobu在5.1里实现,但其存在问题的是,即如果在非xfs系统上时,可能会在预分配文件时因为持有大锁,导致tps长时间为0。这里转换思路,使用一个daemon plugin来做文件分配,当binlog切换时,直接将预分配好的文件(命名为mysql-binlog.PA)  rename成binlog文件。

一组简单的测试数据,之前我在percona-discuess 上发过,直接拿过来了...

my test: (using mysqlslap, ext3 filesystem)
set sync_binlog       = 1


create a simple table:
     create table xxx (a int auto_increment, b int, c varchar(50), primary key(a))


mysqlslap --no-defaults -uxx --create-schema=test --number-of-queries=2000000 --concurrency=$i  -S $sock  --query="insert into xxx values  (NULL,2,     'sadasda')"

threads              with binlog-prealloc                             Original version
10                               152s                                               455s                         
30                               104s                                               216s
50                                97s                                                164s
70                                98s                                                143s
90                                98s                                                132s
110                              101s                                              127s
130                              102s                                              124s
150                              104s                                              123s

以下Patch基于Percona Server5.5.24,目前处于测试中,比较简陋...

cmake时需要增加选项  -DWITH_BINLOG_PREALLOC=ON

然后 install plugin:
set binlog_prealloc = 1;
install plugin binlog_prealloc soname 'libbinlog_prealloc.so';
flush logs;

  1. Index: a/CMakeLists.txt  
  2. ===================================================================  
  3. --- a.orig/CMakeLists.txt  
  4. +++ a/CMakeLists.txt  
  5. @@ -161,6 +161,11 @@ INCLUDE(install_layout)  
  6.  INCLUDE(mysql_add_executable)  
  7.    
  8.  # Handle options   
  9. +OPTION(WITH_BINLOG_PREALLOC "if allow binlog file prealloced" OFF)  
  10. +IF(WITH_BINLOG_PREALLOC)  
  11. +ADD_DEFINITIONS(-DWITH_BINLOG_PREALLOC)  
  12. +ENDIF()  
  13. +  
  14.  OPTION(DISABLE_SHARED   
  15.   "Don't build shared libraries, compile code as position-dependent" OFF)  
  16.  IF(DISABLE_SHARED)  
  17. Index: a/include/my_global.h  
  18. ===================================================================  
  19. --- a.orig/include/my_global.h  
  20. +++ a/include/my_global.h  
  21. @@ -1501,4 +1501,8 @@ static inline double rint(double x)  
  22.    
  23.  #endif /* EMBEDDED_LIBRARY */   
  24.    
  25. +#if defined (HAVE_POSIX_FALLOCATE) && defined(WITH_BINLOG_PREALLOC)  
  26. +#define BINLOG_PREALLOC  
  27. +#endif  
  28. +  
  29.  #endif /* my_global_h */   
  30. Index: a/plugin/daemon_example/CMakeLists.txt  
  31. ===================================================================  
  32. --- a.orig/plugin/daemon_example/CMakeLists.txt  
  33. +++ a/plugin/daemon_example/CMakeLists.txt  
  34. @@ -17,3 +17,6 @@ MYSQL_ADD_PLUGIN(daemon_example daemon_e  
  35.    MODULE_ONLY MODULE_OUTPUT_NAME "libdaemon_example")  
  36.    
  37.  INSTALL(FILES daemon_example.ini DESTINATION ${INSTALL_PLUGINDIR})  
  38. +  
  39. +MYSQL_ADD_PLUGIN(binlog_prealloc binlog_prealloc.cc  
  40. +  MODULE_ONLY MODULE_OUTPUT_NAME "libbinlog_prealloc")  
  41. Index: a/plugin/daemon_example/binlog_prealloc.cc  
  42. ===================================================================  
  43. --- /dev/null  
  44. +++ a/plugin/daemon_example/binlog_prealloc.cc  
  45. @@ -0,0 +1,111 @@  
  46. +#ifndef MYSQL_SERVER  
  47. +#define MYSQL_SERVER  
  48. +#endif  
  49. +  
  50. +#include <string.h>  
  51. +#include <mysql/plugin.h>  
  52. +#include <mysql_version.h>  
  53. +#include "my_global.h"  
  54. +#include <my_sys.h>  
  55. +#include <sys/time.h>  
  56. +#include "log.h"  
  57. +  
  58. +#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__)  || __GNUC__ == 2 && __GNUC_MINOR__ < 8)  
  59. +#define __attribute__(A)  
  60. +#endif  
  61. +  
  62. +/*defined in log.cc*/  
  63. +static pthread_t bin_prealloc_thread;  
  64. +extern unsigned long max_binlog_size;  
  65. +extern my_bool binlog_prealloc_inited;  
  66. +extern ulong     binlog_prealloc ;  
  67. +extern my_bool use_plugin_prealloc;  
  68. +extern my_bool has_prealloc_next;  
  69. +extern pthread_mutex_t binlog_prealloc_mutex;  
  70. +extern pthread_cond_t  binlog_prealloc_cond;  
  71. +extern char prealloc_file[FN_REFLEN];  
  72. +extern int create_prealloc_file(char *filename);  
  73. +  
  74. +pthread_handler_t bin_prealloc_func(void *p)  
  75. +{  
  76. +  
  77. +    int fd;  
  78. +    long i  = 0;  
  79. +    int ret = 0;  
  80. +    int len = 0;  
  81. +      
  82. +    while (1){  
  83. +        if ( binlog_prealloc == 0 ||   
  84. +                   !binlog_prealloc_inited ) {  
  85. +            sleep(2);  
  86. +            continue;  
  87. +        }   
  88. +          
  89. +        pthread_mutex_lock(&(binlog_prealloc_mutex));  
  90. +        if (has_prealloc_next)  
  91. +           pthread_cond_wait(&(binlog_prealloc_cond), &(binlog_prealloc_mutex));  
  92. +            
  93. +        int ret = create_prealloc_file(prealloc_file);  
  94. +        if (unlikely(use_plugin_prealloc) == FALSE)  
  95. +            use_plugin_prealloc = TRUE;  
  96. +  
  97. +        has_prealloc_next = TRUE;  
  98. +  
  99. +        pthread_mutex_unlock(&(binlog_prealloc_mutex));  
  100. +    }  
  101. +      
  102. +    return 0;  
  103. +}  
  104. +  
  105. +  
  106. +static int bin_prealloc_init(void *p)  
  107. +{  
  108. +    pthread_attr_t attr;  
  109. +  
  110. +    pthread_attr_init(&attr);  
  111. +    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);  
  112. +  
  113. +    use_plugin_prealloc = FALSE;   
  114. +      
  115. +    if (pthread_create(&bin_prealloc_thread, &attr,  
  116. +                bin_prealloc_func, NULL) != 0){  
  117. +  
  118. +        fprintf(stderr, "Plugin 'bin_prealloc': "  
  119. +                "Could not create bin_prealloc thread!\n");  
  120. +        return 1;  
  121. +    }  
  122. +  
  123. +    return 0;  
  124. +}  
  125. +  
  126. +  
  127. +static int bin_prealloc_deinit(void *p)  
  128. +{  
  129. +    pthread_cancel(bin_prealloc_thread);  
  130. +    pthread_join(bin_prealloc_thread, NULL);  
  131. +  
  132. +    has_prealloc_next = FALSE;   
  133. +    use_plugin_prealloc = FALSE;  
  134. +    return 0;  
  135. +}  
  136. +  
  137. +  
  138. +struct st_mysql_daemon bin_prealloc = { MYSQL_DAEMON_INTERFACE_VERSION };  
  139. +  
  140. +mysql_declare_plugin(bin_prealloc)  
  141. +{  
  142. +    MYSQL_DAEMON_PLUGIN,  
  143. +    &bin_prealloc,  
  144. +    "binlog_prealloc",  
  145. +    "yinfeng.zwx@taobao.com",  
  146. +    "a daemon plugin to prealloc binlog file",  
  147. +    PLUGIN_LICENSE_GPL,  
  148. +    bin_prealloc_init,  
  149. +    bin_prealloc_deinit,  
  150. +    0x0100,  
  151. +    NULL,  
  152. +    NULL,  
  153. +    NULL  
  154. +}  
  155. +mysql_declare_plugin_end;  
  156. +  
  157. Index: a/sql/log_event.cc  
  158. ===================================================================  
  159. --- a.orig/sql/log_event.cc  
  160. +++ a/sql/log_event.cc  
  161. @@ -65,6 +65,10 @@  
  162.  */  
  163.  #define FMT_G_BUFSIZE(PREC) (3 + (PREC) + 5 + 1)   
  164.    
  165. +#ifdef BINLOG_PREALLOC  
  166. +extern ulonglong fetch_active_size(void);  
  167. +extern ulonglong use_binlog_prealloc;  
  168. +#endif  
  169.    
  170.  #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)   
  171.  static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD* thd);  
  172. @@ -1018,7 +1022,14 @@ int Log_event::read_log_event(IO_CACHE*   
  173.    int result=0;  
  174.    char buf[LOG_EVENT_MINIMAL_HEADER_LEN];  
  175.    DBUG_ENTER("Log_event::read_log_event");  
  176. -  
  177. +#ifdef BINLOG_PREALLOC  
  178. +  if (use_binlog_prealloc && file->file_name && file->type == READ_CACHE) {  
  179. +     if (mysql_bin_log.is_active(file->file_name))  
  180. +         file->end_of_file= fetch_active_size();  
  181. +     else  
  182. +         file->end_of_file= ~(my_off_t) 0;  
  183. +  }  
  184. +#endif  
  185.    if (log_lock)  
  186.      mysql_mutex_lock(log_lock);  
  187.    if (my_b_read(file, (uchar*) buf, sizeof(buf)))  
  188. Index: a/sql/log.cc  
  189. ===================================================================  
  190. --- a.orig/sql/log.cc  
  191. +++ a/sql/log.cc  
  192. @@ -90,6 +90,112 @@ static SHOW_VAR binlog_status_vars_detai  
  193.    {NullS, NullS, SHOW_LONG}  
  194.  };  
  195.    
  196. +#ifdef BINLOG_PREALLOC  
  197. +  
  198. +ulonglong active_binlog_size  = 0;   
  199. +ulong     binlog_prealloc     = 0;   
  200. +  
  201. +my_bool use_binlog_prealloc    =  FALSE;  
  202. +my_bool binlog_prealloc_inited =  FALSE;  
  203. +my_bool use_plugin_prealloc    =  FALSE;  
  204. +my_bool has_prealloc_next      =  FALSE;  
  205. +  
  206. +pthread_mutex_t binlog_prealloc_mutex;  
  207. +pthread_cond_t  binlog_prealloc_cond;  
  208. +  
  209. +char prealloc_file[FN_REFLEN];  
  210. +  
  211. +static void init_binlog_prealloc(const char * name)  
  212. +{  
  213. +    use_plugin_prealloc = FALSE;  
  214. +    has_prealloc_next   = FALSE;  
  215. +  
  216. +    bzero(prealloc_file, FN_REFLEN);  
  217. +  
  218. +    /*hardcode the prealloc file name */  
  219. +    fn_format(prealloc_file, name, mysql_data_home, "", 4);  
  220. +    size_t length = strlen(prealloc_file);  
  221. +    prealloc_file[length]   = '.' ;  
  222. +    prealloc_file[length+1] = 'P' ;  
  223. +    prealloc_file[length+2] = 'A';   
  224. +    prealloc_file[length+3] = '\0';  
  225. +  
  226. +    pthread_mutex_init(&(binlog_prealloc_mutex), NULL);  
  227. +    pthread_cond_init(&(binlog_prealloc_cond), NULL);  
  228. +    binlog_prealloc_inited = TRUE;    
  229. +}  
  230. +  
  231. +ulonglong set_active_size(ulonglong new_val)  
  232. +{    
  233. +       return  __sync_val_compare_and_swap(&active_binlog_size,  
  234. +                           active_binlog_size, new_val);  
  235. +}  
  236. +  
  237. +  
  238. +ulonglong fetch_active_size(void)  
  239. +{    
  240. +    return __sync_add_and_fetch(&active_binlog_size,0);  
  241. +}  
  242. +  
  243. +int create_prealloc_file(char *file_name)  
  244. +{     
  245. +    int fd = 0;  
  246. +    int ret = 0;  
  247. +    fd = open(file_name, O_CREAT | O_RDWR, 0);  
  248. +    if (fd == -1)  
  249. +        return -1;  
  250. +  
  251. +    ret = posix_fallocate(fd, 0, max_binlog_size)  
  252. +        || my_sync(fd, MYF(MY_WME));  
  253. +  
  254. +    close(fd);  
  255. +    return ret;  
  256. +}  
  257. +  
  258. +  
  259. +int prealloc_binlog_with_newname(char *new_name)  
  260. +{    
  261. +    int ret = 0;  
  262. +  
  263. +    if (use_plugin_prealloc) {  
  264. +        ret = pthread_mutex_trylock(&(binlog_prealloc_mutex));  
  265. +  
  266. +        /*if can't get lock ,simply return to orignal mode(means no prealloc...) */  
  267. +        if (ret != 0)  
  268. +            return -1;  
  269. +  
  270. +        my_bool success = FALSE;  
  271. +  
  272. +        if (has_prealloc_next &&  
  273. +                access(prealloc_file, 0) == 0 &&  
  274. +                rename(prealloc_file, new_name) == 0)  
  275. +            success = TRUE;  
  276. +  
  277. +        has_prealloc_next = FALSE;  
  278. +  
  279. +        pthread_mutex_unlock(&(binlog_prealloc_mutex));  
  280. +  
  281. +        pthread_cond_broadcast(&(binlog_prealloc_cond));  
  282. +  
  283. +        if (!success)  
  284. +            return -1;  
  285. +  
  286. +    } else {  
  287. +  
  288. +        int ret = create_prealloc_file(new_name);  
  289. +  
  290. +        if (ret!= 0) {  
  291. +            fprintf(stderr, "Prealloc Binlog Failed:%s\n", new_name);  
  292. +            return -1;  
  293. +        }  
  294. +    }  
  295. +  
  296. +    chmod(new_name, 438);  
  297. +    use_binlog_prealloc = TRUE;  
  298. +    return 0;  
  299. +}  
  300. +  
  301. +#endif  
  302.  /** 
  303.     purge logs, master and slave sides both, related error code 
  304.     convertor. 
  305. @@ -2122,6 +2228,12 @@ File open_binlog(IO_CACHE *log, const ch 
  306.      *errmsg = "Could not open log file"; 
  307.      goto err; 
  308.    } 
  309. +#ifdef BINLOG_PREALLOC 
  310. +  if (use_binlog_prealloc) { 
  311. +    log->file_name=(char *)log_file_name; 
  312. +    log->end_of_file= fetch_active_size(); 
  313. +  } 
  314. +#endif 
  315.    if (check_binlog_magic(log,errmsg)) 
  316.      goto err; 
  317.    DBUG_RETURN(file); 
  318. @@ -3182,7 +3294,9 @@ bool MYSQL_BIN_LOG::open(const char *log 
  319.      if (flush_io_cache(&log_file) || 
  320.          mysql_file_sync(log_file.file, MYF(MY_WME))) 
  321.        goto err; 
  322. - 
  323. +#ifdef BINLOG_PREALLOC    
  324. +    set_active_size(log_file.pos_in_file);  
  325. +#endif 
  326.      if (write_file_name_to_index_file) 
  327.      { 
  328.  #ifdef HAVE_REPLICATION 
  329. @@ -4378,7 +4492,16 @@ int MYSQL_BIN_LOG::new_file_impl(bool ne 
  330.    old_name=name; 
  331.    name=0;              // Don't free name 
  332.    close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX); 
  333. - 
  334. +#ifdef BINLOG_PREALLOC 
  335. +  /*try to pre-alloc binlog file,we don't care if this will fail*/  
  336. +  if (!is_relay_log && binlog_prealloc) {  
  337. +     if (unlikely(!binlog_prealloc_inited)) {  
  338. +        init_binlog_prealloc(old_name);   
  339. +     }  
  340. +    prealloc_binlog_with_newname(new_name_ptr);   
  341. +  } else  
  342. +    use_binlog_prealloc = FALSE;  
  343. +#endif  
  344.    /* 
  345.       Note that at this point, log_state != LOG_CLOSED (important for is_open()). 
  346.    */  
  347. @@ -5156,6 +5279,9 @@ err:  
  348.          else  
  349.          {  
  350.            bool check_purge;  
  351. +#ifdef BINLOG_PREALLOC          
  352. +          set_active_size(event_info->log_pos);  
  353. +#endif  
  354.            signal_update();  
  355.            error= rotate(false, &check_purge);  
  356.            mysql_mutex_unlock(&LOCK_log);  
  357. @@ -5540,6 +5666,9 @@ bool MYSQL_BIN_LOG::write_incident(THD *  
  358.      if (!error && !(error= flush_and_sync(0)))  
  359.      {  
  360.        bool check_purge= false;  
  361. +#ifdef BINLOG_PREALLOC          
  362. +      set_active_size(ev.log_pos);  
  363. +#endif   
  364.        signal_update();  
  365.        error= rotate(false, &check_purge);  
  366.        mysql_mutex_unlock(&LOCK_log);  
  367. @@ -5695,6 +5824,9 @@ void  
  368.  MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)  
  369.  {  
  370.    DBUG_ENTER("MYSQL_BIN_LOG::trx_group_commit_leader");  
  371. +#ifdef BINLOG_PREALLOC  
  372. +  ulonglong last_actual_pos = 0;  
  373. +#endif  
  374.    uint xid_count= 0;  
  375.    uint write_count= 0;  
  376.    bool check_purge= false;  
  377. @@ -5753,6 +5885,9 @@ MYSQL_BIN_LOG::trx_group_commit_leader(g  
  378.        }  
  379.    
  380.        cache_data->commit_bin_log_file_pos= my_b_write_tell(&log_file);  
  381. +#ifdef BINLOG_PREALLOC  
  382. +      last_actual_pos = cache_data->commit_bin_log_file_pos;  
  383. +#endif  
  384.        if (cache_data->using_xa && cache_data->xa_xid)  
  385.          xid_count++;  
  386.      }  
  387. @@ -5773,6 +5908,9 @@ MYSQL_BIN_LOG::trx_group_commit_leader(g  
  388.        }  
  389.        else  
  390.        {  
  391. +#ifdef BINLOG_PREALLOC       
  392. +        set_active_size(last_actual_pos);  
  393. +#endif  
  394.          signal_update();  
  395.        }  
  396.    
  397. @@ -6005,6 +6143,18 @@ void MYSQL_BIN_LOG::close(uint exiting)  
  398.          original position on system that doesn't support pwrite().  
  399.        */  
  400.        mysql_file_seek(log_file.file, org_position, MY_SEEK_SET, MYF(0));  
  401. +#ifdef BINLOG_PREALLOC  
  402. +      end_io_cache(&log_file);  
  403. +      DBUG_ASSERT(is_active(log_file_name));  
  404. +      mysql_mutex_assert_owner(&LOCK_log);  
  405. +      set_active_size(log_file.pos_in_file);  
  406. +      if (use_binlog_prealloc && my_chsize(log_file.file,  
  407. +                  log_file.pos_in_file, 0, MYF(MY_WME)))  
  408. +      {  
  409. +          write_error= 1;  
  410. +          sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);  
  411. +      }  
  412. +#endif  
  413.      }  
  414.    
  415.      /* this will cleanup IO_CACHE, sync and close the file */  
  416. Index: a/sql/sys_vars.cc  
  417. ===================================================================  
  418. --- a.orig/sql/sys_vars.cc  
  419. +++ a/sql/sys_vars.cc  
  420. @@ -3330,6 +3330,12 @@ static Sys_var_uint Sys_slave_net_timeou  
  421.         VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(SLAVE_NET_TIMEOUT), BLOCK_SIZE(1),  
  422.         NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),  
  423.         ON_UPDATE(fix_slave_net_timeout));  
  424. +#ifdef BINLOG_PREALLOC  
  425. +static Sys_var_ulong Sys_binlog_prealloc(  
  426. +       "binlog_prealloc""default 0 , if binlog_prealloc >0, means prealloc binlog file",  
  427. +       GLOBAL_VAR(binlog_prealloc), CMD_LINE(REQUIRED_ARG),  
  428. +       VALID_RANGE(0,100), DEFAULT(0), BLOCK_SIZE(1));  
  429. +#endif  
  430.    
  431.  static bool check_slave_skip_counter(sys_var *self, THD *thd, set_var *var)  
  432.  {  
  433. Index: a/sql/mysqld.h  
  434. ===================================================================  
  435. --- a.orig/sql/mysqld.h  
  436. +++ a/sql/mysqld.h  
  437. @@ -238,7 +238,9 @@ extern I_List<THD> threads;  
  438.  extern char err_shared_dir[];  
  439.  extern TYPELIB thread_handling_typelib;  
  440.  extern my_decimal decimal_zero;  
  441. -  
  442. +#ifdef BINLOG_PREALLOC   
  443. +extern ulong binlog_prealloc;  
  444. +#endif  
  445.  extern ulonglong opt_log_warnings_suppress;  
  446.    
  447.  extern char* enforce_storage_engine;  

相关内容