buf0flu.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:18k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /******************************************************
  2. The database buffer buf_pool flush algorithm
  3. (c) 1995 Innobase Oy
  4. Created 11/11/1995 Heikki Tuuri
  5. *******************************************************/
  6. #include "buf0flu.h"
  7. #ifdef UNIV_NONINL
  8. #include "buf0flu.ic"
  9. #endif
  10. #include "ut0byte.h"
  11. #include "ut0lst.h"
  12. #include "fil0fil.h"
  13. #include "buf0buf.h"
  14. #include "buf0lru.h"
  15. #include "buf0rea.h"
  16. #include "ibuf0ibuf.h"
  17. #include "log0log.h"
  18. #include "os0file.h"
  19. /* When flushed, dirty blocks are searched in neigborhoods of this size, and
  20. flushed along with the original page. */
  21. #define BUF_FLUSH_AREA ut_min(BUF_READ_AHEAD_AREA,
  22. buf_pool->curr_size / 16)
  23. /**********************************************************************
  24. Validates the flush list. */
  25. static
  26. ibool
  27. buf_flush_validate_low(void);
  28. /*========================*/
  29. /* out: TRUE if ok */
  30. /************************************************************************
  31. Inserts a modified block into the flush list. */
  32. void
  33. buf_flush_insert_into_flush_list(
  34. /*=============================*/
  35. buf_block_t* block) /* in: block which is modified */
  36. {
  37. ut_ad(mutex_own(&(buf_pool->mutex)));
  38. ut_ad((UT_LIST_GET_FIRST(buf_pool->flush_list) == NULL)
  39.       || (ut_dulint_cmp(
  40. (UT_LIST_GET_FIRST(buf_pool->flush_list))
  41. ->oldest_modification,
  42. block->oldest_modification) <= 0));
  43. UT_LIST_ADD_FIRST(flush_list, buf_pool->flush_list, block);
  44. ut_ad(buf_flush_validate_low());
  45. }
  46. /************************************************************************
  47. Inserts a modified block into the flush list in the right sorted position.
  48. This function is used by recovery, because there the modifications do not
  49. necessarily come in the order of lsn's. */
  50. void
  51. buf_flush_insert_sorted_into_flush_list(
  52. /*====================================*/
  53. buf_block_t* block) /* in: block which is modified */
  54. {
  55. buf_block_t* prev_b;
  56. buf_block_t* b;
  57. ut_ad(mutex_own(&(buf_pool->mutex)));
  58. prev_b = NULL;
  59. b = UT_LIST_GET_FIRST(buf_pool->flush_list);
  60. while (b && (ut_dulint_cmp(b->oldest_modification,
  61. block->oldest_modification) > 0)) {
  62. prev_b = b;
  63. b = UT_LIST_GET_NEXT(flush_list, b);
  64. }
  65. if (prev_b == NULL) {
  66. UT_LIST_ADD_FIRST(flush_list, buf_pool->flush_list, block);
  67. } else {
  68. UT_LIST_INSERT_AFTER(flush_list, buf_pool->flush_list, prev_b,
  69. block);
  70. }
  71. ut_ad(buf_flush_validate_low());
  72. }
  73. /************************************************************************
  74. Returns TRUE if the file page block is immediately suitable for replacement,
  75. i.e., the transition FILE_PAGE => NOT_USED allowed. */
  76. ibool
  77. buf_flush_ready_for_replace(
  78. /*========================*/
  79. /* out: TRUE if can replace immediately */
  80. buf_block_t* block) /* in: buffer control block, must be in state
  81. BUF_BLOCK_FILE_PAGE and in the LRU list*/
  82. {
  83. ut_ad(mutex_own(&(buf_pool->mutex)));
  84. ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
  85. if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0)
  86.     || (block->buf_fix_count != 0)
  87.     || (block->io_fix != 0)) {
  88. return(FALSE);
  89. }
  90. return(TRUE);
  91. }
  92. /************************************************************************
  93. Returns TRUE if the block is modified and ready for flushing. */
  94. UNIV_INLINE
  95. ibool
  96. buf_flush_ready_for_flush(
  97. /*======================*/
  98. /* out: TRUE if can flush immediately */
  99. buf_block_t* block, /* in: buffer control block, must be in state
  100. BUF_BLOCK_FILE_PAGE */
  101. ulint flush_type)/* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */
  102. {
  103. ut_ad(mutex_own(&(buf_pool->mutex)));
  104. ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
  105. if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0)
  106.      && (block->io_fix == 0)) {
  107.      if (flush_type != BUF_FLUSH_LRU) {
  108. return(TRUE);
  109. } else if ((block->old || (UT_LIST_GET_LEN(buf_pool->LRU)
  110. < BUF_LRU_OLD_MIN_LEN))
  111.    && (block->buf_fix_count == 0)) {
  112.  
  113. /* If we are flushing the LRU list, to avoid deadlocks
  114. we require the block not to be bufferfixed, and hence
  115. not latched. Since LRU flushed blocks are soon moved
  116. to the free list, it is good to flush only old blocks
  117. from the end of the LRU list. */
  118. return(TRUE);
  119. }
  120. }
  121. return(FALSE);
  122. }
  123. /************************************************************************
  124. Updates the flush system data structures when a write is completed. */
  125. void
  126. buf_flush_write_complete(
  127. /*=====================*/
  128. buf_block_t* block) /* in: pointer to the block in question */
  129. {
  130. ut_ad(block);
  131. ut_ad(mutex_own(&(buf_pool->mutex)));
  132. block->oldest_modification = ut_dulint_zero;
  133. UT_LIST_REMOVE(flush_list, buf_pool->flush_list, block);
  134. ut_d(UT_LIST_VALIDATE(flush_list, buf_block_t, buf_pool->flush_list));
  135. (buf_pool->n_flush[block->flush_type])--;
  136. if (block->flush_type == BUF_FLUSH_LRU) {
  137. /* Put the block to the end of the LRU list to wait to be
  138. moved to the free list */
  139. buf_LRU_make_block_old(block);
  140. buf_pool->LRU_flush_ended++;
  141. }
  142. /*  printf("n pending flush %lun",
  143. buf_pool->n_flush[block->flush_type]); */
  144. if ((buf_pool->n_flush[block->flush_type] == 0)
  145.     && (buf_pool->init_flush[block->flush_type] == FALSE)) {
  146. /* The running flush batch has ended */
  147. os_event_set(buf_pool->no_flush[block->flush_type]);
  148. }
  149. }
  150. /************************************************************************
  151. Does an asynchronous write of a buffer page. NOTE: in simulated aio we must
  152. call os_aio_simulated_wake_handler_threads after we have posted a batch
  153. of writes! */
  154. static
  155. void
  156. buf_flush_write_block_low(
  157. /*======================*/
  158. buf_block_t* block) /* in: buffer block to write */
  159. {
  160. #ifdef UNIV_IBUF_DEBUG
  161. ut_a(ibuf_count_get(block->space, block->offset) == 0);
  162. #endif
  163. ut_ad(!ut_dulint_is_zero(block->newest_modification));
  164. #ifdef UNIV_LOG_DEBUG
  165. printf(
  166. "Warning: cannot force log to disk in the log debug version!n");
  167. #else
  168. /* Force the log to the disk before writing the modified block */
  169. log_flush_up_to(block->newest_modification, LOG_WAIT_ALL_GROUPS);
  170. #endif
  171. /* Write the newest modification lsn to the page */
  172. mach_write_to_8(block->frame + FIL_PAGE_LSN,
  173. block->newest_modification);
  174. mach_write_to_8(block->frame + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN,
  175. block->newest_modification);
  176. fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER,
  177. FALSE, block->space, block->offset, 0, UNIV_PAGE_SIZE,
  178.   (void*)block->frame, (void*)block);
  179. }
  180. /************************************************************************
  181. Writes a page asynchronously from the buffer buf_pool to a file, if it can be
  182. found in the buf_pool and it is in a flushable state. NOTE: in simulated aio
  183. we must call os_aio_simulated_wake_handler_threads after we have posted a batch
  184. of writes! */
  185. static
  186. ulint
  187. buf_flush_try_page(
  188. /*===============*/
  189. /* out: 1 if a page was flushed, 0 otherwise */
  190. ulint space, /* in: space id */
  191. ulint offset, /* in: page offset */
  192. ulint flush_type) /* in: BUF_FLUSH_LRU, BUF_FLUSH_LIST, or
  193. BUF_FLUSH_SINGLE_PAGE */
  194. {
  195. buf_block_t* block;
  196. ibool locked;
  197. ut_ad((flush_type == BUF_FLUSH_LRU) || (flush_type == BUF_FLUSH_LIST)
  198. || (flush_type == BUF_FLUSH_SINGLE_PAGE));
  199. mutex_enter(&(buf_pool->mutex));
  200. block = buf_page_hash_get(space, offset);
  201. if ((flush_type == BUF_FLUSH_LIST)
  202.     && block && buf_flush_ready_for_flush(block, flush_type)) {
  203. block->io_fix = BUF_IO_WRITE;
  204. block->flush_type = flush_type;
  205. if (buf_pool->n_flush[block->flush_type] == 0) {
  206. os_event_reset(buf_pool->no_flush[block->flush_type]);
  207. }
  208. (buf_pool->n_flush[flush_type])++;
  209. locked = FALSE;
  210. /* If the simulated aio thread is not running, we must
  211. not wait for any latch, as we may end up in a deadlock:
  212. if buf_fix_count == 0, then we know we need not wait */
  213. if (block->buf_fix_count == 0) {
  214. rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE);
  215. locked = TRUE;
  216. }
  217. mutex_exit(&(buf_pool->mutex));
  218. if (!locked) {
  219. os_aio_simulated_wake_handler_threads();
  220. rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE);
  221. }
  222. if (buf_debug_prints) {
  223. printf("Flushing page space %lu, page no %lu n",
  224. block->space, block->offset);
  225. }
  226. buf_flush_write_block_low(block);
  227. return(1);
  228. } else if ((flush_type == BUF_FLUSH_LRU) && block
  229. && buf_flush_ready_for_flush(block, flush_type)) {
  230. /* VERY IMPORTANT:
  231. Because any thread may call the LRU flush, even when owning
  232. locks on pages, to avoid deadlocks, we must make sure that the
  233. s-lock is acquired on the page without waiting: this is
  234. accomplished because in the if-condition above we require
  235. the page not to be bufferfixed (in function
  236. ..._ready_for_flush). */
  237. block->io_fix = BUF_IO_WRITE;
  238. block->flush_type = flush_type;
  239. (buf_pool->n_flush[flush_type])++;
  240. rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE);
  241. /* Note that the s-latch is acquired before releasing the
  242. buf_pool mutex: this ensures that the latch is acquired
  243. immediately. */
  244. mutex_exit(&(buf_pool->mutex));
  245. buf_flush_write_block_low(block);
  246. return(1);
  247. } else if ((flush_type == BUF_FLUSH_SINGLE_PAGE) && block
  248. && buf_flush_ready_for_flush(block, flush_type)) {
  249. block->io_fix = BUF_IO_WRITE;
  250. block->flush_type = flush_type;
  251. if (buf_pool->n_flush[block->flush_type] == 0) {
  252. os_event_reset(buf_pool->no_flush[block->flush_type]);
  253. }
  254. (buf_pool->n_flush[flush_type])++;
  255. mutex_exit(&(buf_pool->mutex));
  256. rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE);
  257. if (buf_debug_prints) {
  258. printf("Flushing single page space %lu, page no %lu n",
  259. block->space, block->offset);
  260. }
  261. buf_flush_write_block_low(block);
  262. return(1);
  263. } else {
  264. mutex_exit(&(buf_pool->mutex));
  265. return(0);
  266. }
  267. }
  268. /***************************************************************
  269. Flushes to disk all flushable pages within the flush area. */
  270. static
  271. ulint
  272. buf_flush_try_neighbors(
  273. /*====================*/
  274. /* out: number of pages flushed */
  275. ulint space, /* in: space id */
  276. ulint offset, /* in: page offset */
  277. ulint flush_type) /* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */
  278. {
  279. buf_block_t* block;
  280. ulint low, high;
  281. ulint count = 0;
  282. ulint i;
  283. ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST);
  284. low = (offset / BUF_FLUSH_AREA) * BUF_FLUSH_AREA;
  285. high = (offset / BUF_FLUSH_AREA + 1) * BUF_FLUSH_AREA;
  286. if (UT_LIST_GET_LEN(buf_pool->LRU) < BUF_LRU_OLD_MIN_LEN) {
  287. /* If there is little space, it is better not to flush any
  288. block except from the end of the LRU list */
  289. low = offset;
  290. high = offset + 1;
  291. }
  292. /* printf("Flush area: low %lu high %lun", low, high); */
  293. if (high > fil_space_get_size(space)) {
  294. high = fil_space_get_size(space);
  295. }
  296. mutex_enter(&(buf_pool->mutex));
  297. for (i = low; i < high; i++) {
  298. block = buf_page_hash_get(space, i);
  299. if (block && buf_flush_ready_for_flush(block, flush_type)) {
  300. mutex_exit(&(buf_pool->mutex));
  301. /* Note: as we release the buf_pool mutex above, in
  302. buf_flush_try_page we cannot be sure the page is still
  303. in a flushable state: therefore we check it again
  304. inside that function. */
  305. count += buf_flush_try_page(space, i, flush_type);
  306. mutex_enter(&(buf_pool->mutex));
  307. }
  308. }
  309. mutex_exit(&(buf_pool->mutex));
  310. /* In simulated aio we wake up the i/o-handler threads now that
  311. we have posted a batch of writes: */
  312. os_aio_simulated_wake_handler_threads();
  313. return(count);
  314. }
  315. /***********************************************************************
  316. This utility flushes dirty blocks from the end of the LRU list or flush_list.
  317. NOTE 1: in the case of an LRU flush the calling thread may own latches to
  318. pages: to avoid deadlocks, this function must be written so that it cannot
  319. end up waiting for these latches! NOTE 2: in the case of a flush list flush,
  320. the calling thread is not allowed to own any latches on pages! */
  321. ulint
  322. buf_flush_batch(
  323. /*============*/
  324. /* out: number of blocks for which the write
  325. request was queued; ULINT_UNDEFINED if there
  326. was a flush of the same type already running */
  327. ulint flush_type, /* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST; if
  328. BUF_FLUSH_LIST, then the caller must not own
  329. any latches on pages */
  330. ulint min_n, /* in: wished minimum mumber of blocks flushed
  331. (it is not guaranteed that the actual number
  332. is that big, though) */
  333. dulint lsn_limit) /* in the case BUF_FLUSH_LIST all blocks whose
  334. oldest_modification is smaller than this
  335. should be flushed (if their number does not
  336. exceed min_n), otherwise ignored */
  337. {
  338. buf_block_t* block;
  339. ulint page_count  = 0;
  340. ulint old_page_count;
  341. ulint space;
  342. ulint offset;
  343. ibool found;
  344. ut_ad((flush_type == BUF_FLUSH_LRU) || (flush_type == BUF_FLUSH_LIST)); 
  345. ut_ad((flush_type != BUF_FLUSH_LIST) ||
  346. sync_thread_levels_empty_gen(TRUE));
  347.       
  348. mutex_enter(&(buf_pool->mutex));
  349. if ((buf_pool->n_flush[flush_type] > 0)
  350.     || (buf_pool->init_flush[flush_type] == TRUE)) {
  351. /* There is already a flush batch of the same type running */
  352. mutex_exit(&(buf_pool->mutex));
  353. return(ULINT_UNDEFINED);
  354. }
  355. (buf_pool->init_flush)[flush_type] = TRUE;
  356. for (;;) {
  357. /* If we have flushed enough, leave the loop */
  358. if (page_count >= min_n) {
  359. break;
  360. }
  361. /* Start from the end of the list looking for a suitable
  362. block to be flushed. */
  363.      if (flush_type == BUF_FLUSH_LRU) {
  364. block = UT_LIST_GET_LAST(buf_pool->LRU);
  365.      } else {
  366. ut_ad(flush_type == BUF_FLUSH_LIST);
  367. block = UT_LIST_GET_LAST(buf_pool->flush_list);
  368. if (!block
  369.     || (ut_dulint_cmp(block->oldest_modification,
  370.      lsn_limit) >= 0)) {
  371. /* We have flushed enough */
  372. break;
  373. }
  374.      }
  375.     
  376.      found = FALSE;
  377. /* Note that after finding a single flushable page, we try to
  378. flush also all its neighbors, and after that start from the
  379. END of the LRU list or flush list again: the list may change
  380. during the flushing and we cannot safely preserve within this
  381. function a pointer to a block in the list! */
  382.      while ((block != NULL) && !found) {
  383. if (buf_flush_ready_for_flush(block, flush_type)) {
  384. found = TRUE;
  385. space = block->space;
  386. offset = block->offset;
  387.     
  388. mutex_exit(&(buf_pool->mutex));
  389. old_page_count = page_count;
  390. /* Try to flush also all the neighbors */
  391. page_count +=
  392. buf_flush_try_neighbors(space, offset,
  393. flush_type);
  394. /* printf(
  395. "Flush type %lu, page no %lu, neighb %lun",
  396. flush_type, offset,
  397. page_count - old_page_count); */
  398. mutex_enter(&(buf_pool->mutex));
  399. } else if (flush_type == BUF_FLUSH_LRU) {
  400. block = UT_LIST_GET_PREV(LRU, block);
  401. } else {
  402. ut_ad(flush_type == BUF_FLUSH_LIST);
  403. block = UT_LIST_GET_PREV(flush_list, block);
  404. }
  405.      }
  406.      /* If we could not find anything to flush, leave the loop */
  407.      if (!found) {
  408.      break;
  409.      }
  410. }
  411. (buf_pool->init_flush)[flush_type] = FALSE;
  412. if ((buf_pool->n_flush[flush_type] == 0)
  413.     && (buf_pool->init_flush[flush_type] == FALSE)) {
  414. /* The running flush batch has ended */
  415. os_event_set(buf_pool->no_flush[flush_type]);
  416. }
  417. mutex_exit(&(buf_pool->mutex));
  418. if (buf_debug_prints && (page_count > 0)) {
  419. if (flush_type == BUF_FLUSH_LRU) {
  420. printf("To flush %lu pages in LRU flushn",
  421. page_count);
  422. } else if (flush_type == BUF_FLUSH_LIST) {
  423. printf("To flush %lu pages in flush list flushn",
  424. page_count, flush_type);
  425. } else {
  426. ut_error;
  427. }
  428. }
  429. return(page_count);
  430. }
  431. /**********************************************************************
  432. Waits until a flush batch of the given type ends */
  433. void
  434. buf_flush_wait_batch_end(
  435. /*=====================*/
  436. ulint type) /* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */
  437. {
  438. ut_ad((type == BUF_FLUSH_LRU) || (type == BUF_FLUSH_LIST));
  439. os_event_wait(buf_pool->no_flush[type]);
  440. }
  441. /**********************************************************************
  442. Gives a recommendation of how many blocks should be flushed to establish
  443. a big enough margin of replaceable blocks near the end of the LRU list
  444. and in the free list. */
  445. static
  446. ulint
  447. buf_flush_LRU_recommendation(void)
  448. /*==============================*/
  449. /* out: number of blocks which should be flushed
  450. from the end of the LRU list */
  451. {
  452. buf_block_t* block;
  453. ulint n_replaceable;
  454. ulint distance = 0;
  455. mutex_enter(&(buf_pool->mutex));
  456. n_replaceable = UT_LIST_GET_LEN(buf_pool->free);
  457. block = UT_LIST_GET_LAST(buf_pool->LRU);
  458. while ((block != NULL)
  459.        && (n_replaceable < BUF_FLUSH_FREE_BLOCK_MARGIN
  460.         + BUF_FLUSH_EXTRA_MARGIN)
  461.        && (distance < BUF_LRU_FREE_SEARCH_LEN)) {
  462. if (buf_flush_ready_for_replace(block)) {
  463. n_replaceable++;
  464. }
  465. distance++;
  466. block = UT_LIST_GET_PREV(LRU, block);
  467. }
  468. mutex_exit(&(buf_pool->mutex));
  469. if (n_replaceable >= BUF_FLUSH_FREE_BLOCK_MARGIN) {
  470. return(0);
  471. }
  472. return(BUF_FLUSH_FREE_BLOCK_MARGIN + BUF_FLUSH_EXTRA_MARGIN
  473. - n_replaceable);
  474. }
  475. /*************************************************************************
  476. Flushes pages from the end of the LRU list if there is too small a margin
  477. of replaceable pages there or in the free list. VERY IMPORTANT: this function
  478. is called also by threads which have locks on pages. To avoid deadlocks, we
  479. flush only pages such that the s-lock required for flushing can be acquired
  480. immediately, without waiting. */ 
  481. void
  482. buf_flush_free_margin(void)
  483. /*=======================*/
  484. {
  485. ulint n_to_flush;
  486. n_to_flush = buf_flush_LRU_recommendation();
  487. if (n_to_flush > 0) {
  488. buf_flush_batch(BUF_FLUSH_LRU, n_to_flush, ut_dulint_zero);
  489. }
  490. }
  491. /**********************************************************************
  492. Validates the flush list. */
  493. static
  494. ibool
  495. buf_flush_validate_low(void)
  496. /*========================*/
  497. /* out: TRUE if ok */
  498. {
  499. buf_block_t* block;
  500. dulint om;
  501. UT_LIST_VALIDATE(flush_list, buf_block_t, buf_pool->flush_list);
  502. block = UT_LIST_GET_FIRST(buf_pool->flush_list);
  503. while (block != NULL) {
  504. om = block->oldest_modification;
  505. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  506. ut_a(ut_dulint_cmp(om, ut_dulint_zero) > 0);
  507. block = UT_LIST_GET_NEXT(flush_list, block);
  508. if (block) {
  509. ut_a(ut_dulint_cmp(om, block->oldest_modification)
  510. >= 0);
  511. }
  512. }
  513. return(TRUE);
  514. }
  515. /**********************************************************************
  516. Validates the flush list. */
  517. ibool
  518. buf_flush_validate(void)
  519. /*====================*/
  520. /* out: TRUE if ok */
  521. {
  522. ibool ret;
  523. mutex_enter(&(buf_pool->mutex));
  524. ret = buf_flush_validate_low();
  525. mutex_exit(&(buf_pool->mutex));
  526. return(ret);
  527. }