Turning Hugepages for Oracle on linux server

http://vnhacker.blogspot.com/2007/07/khng-phi-c-hng-khng-l-tt-tt.html

Không phải cứ “hàng khủng” là tốt

Cách đây hơn 2 tháng, một khách hàng của tôi có một project rất quan trọng: nâng cấp máy chủ database từ Oracle 9i lên Oracle 10g cho hệ thống core-banking của họ.

Mọi việc diễn ra tốt đẹp cho đến ngày đưa hệ thống vào production. Cứ chạy chừng 30′ là con server “hàng khủng” 16 CPU Intel Itanium 2 + 32G RAM “sụm bà chè”. CPU idle 0%, ssh vào còn không được, huống hồ chi là chạy Oracle. Họ liên hệ với tôi để tìm cách cứu chữa (tôi vốn có làm dự án core-banking này từ trước).

Việc đầu tiên tôi làm là…nhất nút reboot máy chủ. Dẫu biết đối với hệ thống database, việc hard reset như thế là điều tối kỵ, nhưng lúc đó quả thật tôi chẳng còn lựa chọn nào khác để giải thoát máy chủ ra khỏi tình trạng treo cứng, đành phải gửi trọn niềm tin vào Larry Ellison, hi vọng “bé” Oracle sẽ tự phục hồi sau khi reboot (thật ra vẫn còn một giải pháp dự phòng nếu Oracle không thể tự phục hồi được nên tôi mới tự tin vậy).

Sau khi reboot, tôi bắt đầu chuẩn bị một mớ công cụ cần thiết như top, vmstat, iostat, sar, oprofile; mở vài cái “tail -f” trên alert log của Oracle, đăng nhập vào Oracle Metalink…Xong xuôi, tôi bật Oracle lên lại và thử cho người dùng kết nối vào lại.

Chạy được đúng 20′, server lại “hấp hối”. top báo rằng CPU idle là 0%, trong đó iowait (iowait = network i/o + memory i/o + disk i/o) chỉ dao động từ 3-4%, user (các thao tác thuộc user space, Oracle process nằm ở đây) cũng chỉ tầm 5%-10%, còn lại tất cả tập trung dồn vào sys (các thao tác thuộc kernel space). Trong danh sách process list của top, kswapd liên tục dẫn đầu. Đây là điều bất thường so với baseline của server này. Lẽ ra user phải chiếm đa số, kế tiếp là iowait, rồi mới đến sys.

Rồi server bắt đầu swapping đến chóng mặt. iowait tăng lên, sys vẫn không giảm, user và idle luôn ở mức xấp xỉ 0%. Mà bạn biết rồi đó, một khi server đã swapping thì chẳng còn cách nào khác ngoài việc reboot nó. May mắn là tôi đã chuẩn bị sẵn lệnh shutdown Oracle trước khi quá muộn, thành ra lần này không cần reboot nữa.

Có hai câu hỏi được đặt ra:

– Tại sao sys lại chiếm đa số? kswapd đang làm gì?

– Tại sao với lượng RAM nhiều như thế mà iowait vẫn tăng lên và server bắt đầu swapping dữ dội?

—-

Kinh nghiệm trong quá khứ làm việc với Oracle cho tôi biết rằng, mọi câu hỏi liên quan đến Oracle đều có thể được trả lời trong Oracle Metalink (nếu sếp của bạn bắt bạn sử dụng Oracle nhưng không mua account Metalink cho bạn, lời khuyên là hãy xin nghỉ việc càng sớm càng tốt :-d).

Tôi bắt đầu tìm kiếm với các keyword có thể nghĩ ra được. Mất gần 1h mò mẫm mà tôi vẫn chưa tìm được bất kì tài liệu nào đề cập đến hiện tượng giống với tình trạng hiện thời của tôi. Cũng may là lúc này đã qua giờ giao dịch rồi, nên thời gian cũng tương đối rộng rãi hơn. Tôi quyết định dành ra 30′ nữa để tìm, trước khi tạo một service request yêu cầu sự hỗ trợ trực tiếp từ Oracle.

Ngoài tôi ra, các anh chị DBA cũng ra sức tìm kiếm. Thường trong trường hợp như thế này, tất cả sẽ join vào một conference để tiện việc trao đổi thông tin. Đây là việc làm rất có lợi và cần thiết, bởi lẽ các anh chị DBA thường không rành lắm về Linux, còn bản thân tôi cũng không có nhiều kiến thức về Oracle.

Trong số các tài liệu được các anh chị DBA gửi lên conference, tôi chú ý đến một tài liệu có tên như sau: How to Configure RHEL 4 32-bit for Very Large Memory with ramfs and HugePages Note:317141.1 Type: WHITE PAPER.
Chà, “very large memory”, sao tôi không nghĩ đến keyword này nhỉ?

Lúc đầu tôi vẫn không nghĩ tài liệu này sẽ giải quyết được vấn đề, bởi lẽ nó ghi rõ ràng là 32-bit, trong khi hệ thống của tôi là 64-bit. Hơn nữa nếu tôi nhớ không lầm, HugePages chỉ có tác dụng cho hệ thống 32-bit mà thôi. Tuy thế, tôi vẫn mở nó ra xem; dẫu gì nãy giờ cũng tiêu tốn nhiều thời gian rồi, tốn thêm một chút nữa cũng không sao :-p.

Điều đầu tiên tôi nhìn thấy trong tài liệu là dòng:

This document aims to guide Linux system administrators on Red Hat Enterprise Linux 4.0 to configure Very Large Memory and big SGA for Oracle RDBMS with ramfs and hugepages

The configuration provided in this document applies strictly to 32-bit Linux. For HugePages on 64-bit Linux, see Note 361468.1

Chà, nó ghi là nếu muốn tìm hiểu về HugePages trên Linux 64-bit thì coi cái Note:361468.1. Như vậy có nghĩa là vẫn có thể áp dụng được HugePages cho 64-bit Linux àh? Tôi nhanh chóng tải cái Note:361468.1 về và quả thật, nó chính là thứ tôi cần tìm!

Hóa ra tôi hiể nhầm về HugePages thật:

HugePages is a feature of the Linux kernel which allows larger pages to manage memory as the alternative to the small 4K pagesize. For a detailed introduction, see Note 361323.1.

There is a generalmisconception where the HugePages is a feature specific to 32-bit Linux. This document focuses on uses of HugePages on 64-bit Linux platforms.

Phần cuối của tài liệu này trông có vẻ rất promising:

Why HugePages Should Be Used on 64-bit Linux?

* Very Large Memory: The 64-bit systems do have VLM. So the general VLM issues apply.

* Not swappable: HugePages are not swappable. Therefore there is no page replacement mechanism overhead. HugePages are universally regarded as pinned.

* Eliminated page table lookup overhead: Since the pages are not subject to replacement, page table lookups are not required.

* Faster overall memory performance: On virtual memory systems each memory operation is actually two abstract memory operations. Since there are less number of pages to work on, the possible bottleneck on page table access is clearly avoided.

* kswapd: kswapd will get very busy if there is a very large area to be paged (i.e. 13 million page table entries for 50GB memory) and will use an incredible amount of CPU resource. When HugePages are used, kswapd is not involved in managing them.

Có vẻ như tôi đang đi đúng hướng rồi. Tôi quyết định đọc lại về HugePages để hiểu rõ hơn về nó. Một điều may mắn là trước đây tôi có đọc rất kĩ về phần memory management của Linux kernel trong cuốn Understanding The Linux Kernel, thành ra tôi không mất nhiều thời gian lắm để hiểu rõ HugePages.

Không phải cứ “hàng khủng” là tốt (tt)

Nhắc lại phần đầu, máy chủ Oracle của một khách hàng mặc dù có đến 32G RAM và 16CPU nhưng vẫn thường xuyên bị treo vì quá tải. Tôi được giao nhiệm vụ tìm hiểu nguyên nhân và nhờ sự hỗ trợ của Oracle Metalink, tạm thời tôi đã tìm ra đầu mối liên quan đến Hugepages.

HugePages là một tính năng được giới thiệu từ phiên bản Linux kernel 2.6. Linux sử dụng page như là đơn vị cơ bản của bộ nhớ – bộ nhớ vật lý được phân chia và truy cập theo từng page. Kích thước mặc định của một page trong kiến trúc x86 là 4KB, trong ia64 là 16KB, nghĩa là nếu tôi có 32G RAM, Linux sẽ tự động chia lượng RAM này thành 2^21 page, một con số khổng lồ.

Hugepages, như tên gọi của nó, giúp Linux tăng kích thước mỗi page lên, nhằm mục đích giảm số lượng page. Với kiến trúc x64, khi sử dụng Hugepages, mỗi page sẽ có kích thước lên đến 256MB, nghĩa là nếu tôi có 32RAM, Linux chỉ cần chia ra 128 hugepage là đủ.

Trước khi đi vào chi tiết những lợi ích của Hugepage, ta hãy cùng điểm qua hai thành phần rất quan trọng trong cách Linux quản lý bộ nhớ:

  • Page Table: Một page table là một cấu trúc dữ liệu ánh xạ giữa địa chỉ bộ nhớ ảo (virtual memory address) và địa chỉ bộ nhớ vật lý (physical memory address). Chúng ta đều biết, mỗi process trong Linux đều được kernel cấp cho một không gian bộ nhớ ảo tách biệt lẫn nhau. Các bộ nhớ ảo này sẽ được ánh xạ qua bộ nhớ vật lý thông qua các page table. Khi lượng RAM tăng quá lớn, sử dụng page với kích thước mặc định sẽ làm cho page table trở nên cực lớn. Khi đó kernel sẽ mất nhiều thời gian để dò tìm ánh xạ giữa địa chỉ bộ nhớ ảo và bộ nhớ vật lý.
  • TLB: TLB là một bộ nhớ đệm (còn gọi là buffer hay cache) nằm trong CPU; TLB chứa một phần của page table. TLB có kích thước cố định, được tạo ra nhằm mục đích tăng tốc việc chuyển đổi từ địa chỉ bộ nhớ ảo sang bộ nhớ vật lý (tốc độ truy xuất TLB cao hơn tốc độ truy xuất RAM nhiều lần). TLB là một tài nguyên có hạn, và đương nhiên nó cũng bị ảnh hưởng tương tự như page table nếu lượng RAM quá lớn.

Rõ ràng, việc sử dụng Hugepages sẽ đem lại nhiều lợi ích đáng kể trong trường hợp bạn có quá nhiều RAM, mà thiết thực nhất là:

  • Giảm thời gian truy xuất page table: Do số lượng page giảm xuống, kích thước của page table cũng giảm theo, do đó thời gian để kernel tìm kiếm trong page table sẽ giảm xuống đáng kể. Ngoài ra việc tận dụng TLB cũng sẽ trở nên hiệu quả hơn
  • Kernel không bao giờ swap out Hugepages: những vùng bộ nhớ đã được đánh dấu là hugepages sẽ nằm vĩnh viễn trong RAM (cho đến khi reboot hoặc sysadmin muốn thay đổi), kernel không bao giờ swap đám hugepage này ra ngoài ổ cứng. Đây chính là điểm hay nhất của Hugepages, nó giúp chúng ta có thể dedicate một vùng bộ nhớ dành riêng cho một dịch vụ quan trong nào đó trên hệ thống. Trong case này, tôi dùng cho SGA của Oracle.

Sau khi tìm hiểu kỹ về Hugepages, tôi quyết định trao đổi với các anh chị bên DBA cũng như phía lãnh đạo của khách hàng. Họ tỏ ra khá nghi ngờ, bởi lẽ cũng như tôi, họ nghĩ chỉ có thể áp dụng Hugepages được cho x86 mà thôi. Dẫu vậy, họ không còn cách nào khác cả, tôi lúc đó là niềm hi vọng duy nhất của họ, nên họ cũng quyết định cho phép tôi thực hiện.

Việc đầu tiên cần phải làm là tính số lượng Hugepages cần phải có để chứa đủ SGA hiện tại của thằng Oracle. Oracle có cung cấp sẵn một script để làm chuyện đó:

#!/bin/bash
#
# hugepages_settings.sh
#
# Linux bash script to compute values for the
# recommended HugePages/HugeTLB configuration
#
# Note: This script does calculation for all shared memory
# segments available when the script is run, no matter it
# is an Oracle RDBMS shared memory segment or not.

# Check for the kernel version
KERN=`uname -r | awk -F. ‘{ printf(“%d.%d\n”,$1,$2); }’`

# Find out the HugePage size
HPG_SZ=`grep Hugepagesize /proc/meminfo | awk {‘print $2’}`

# Start from 1 pages to be on the safe side and guarantee 1 free HugePage
NUM_PG=1

# Cumulative number of pages required to handle the running shared memory segments
for SEG_BYTES in `ipcs -m | awk {‘print $5’} | grep “[0-9][0-9]*”`
do
MIN_PG=`echo “$SEG_BYTES/($HPG_SZ*1024)” | bc -q`
if [ $MIN_PG -gt 0 ]; then
NUM_PG=`echo “$NUM_PG+$MIN_PG+1” | bc -q`
fi
done

# Finish with results
case $KERN in
‘2.4’) HUGETLB_POOL=`echo “$NUM_PG*$HPG_SZ/1024” | bc -q`;
echo “Recommended setting: vm.hugetlb_pool = $HUGETLB_POOL” ;;
‘2.6’) echo “Recommended setting: vm.nr_hugepages = $NUM_PG” ;;
*) echo “Unrecognized kernel version $KERN. Exiting.” ;;
esac

# End

Như comment ở phần đầu cho thấy, script này tính tổng số lượng shared memory mà hệ thống đang có tại thời điểm chạy script. Tôi chạy script này ngay lúc thằng Oracle đang giãy chết, và kết quả là: Recommended setting: vm.nr_hugepages = 82.

Như vậy, tổng cộng lượng RAM mà tôi phải dành riêng cho thằng Hugepages là 82 x 256M = 20,5G. Do Oracle sẽ sử dụng đám hugepage này như là shared memory, vì thế, tôi phải điều chỉnh cấu hình Linux để nâng tổng dung lượng shared memory lên 25G, trong đó kích thước lớn nhất của một segment là 21G.

Tôi thêm ba dòng như sau vào /etc/sysctl.conf:

# single segment = 21G, Oracle SGA = 20G
kernel.shmmax = 22548578304
# total shared memory 25G
kernel.shmall = 1638400
# total hugepages = 20,5G
vm.nr_hugepages= 82

Nếu bạn không hiểu các dòng trên, hãy thử tự tìm trên Google tài liệu về kernel.shmmax và kernel.shmall để xem các thông số này nghĩa là gì. Do số lượng non-swappable RAM mà Oracle chiếm dụng là rất lớn, nên tôi phải thêm hai dòng sau đây vào /etc/security/limit.conf:

oracle soft memlock 22020096
oracle hard memlock 22020096

Hai dòng này cho phép user oracle được phép chiếm dụng tối đa 21G RAM. Nhắc lại, nếu bạn không hiểu những thông số này, thử tìm trên Google xem sao.

Chuẩn bị xong xuôi. Tôi yêu cầu được phép khởi động lại máy chủ. Lý do phải khởi động là sau một thời gian chạy, RAM đã bị phân chia thành nhiều mảnh nhỏ, cách duy nhất để gom RAM lại là khởi động lại máy chủ, rồi mới có thể chia RAM thành hugepage và gán 20G hugepage cho Oracle. Oracle rất thông minh, khi nó thấy có hugepage, nó sẽ tự động sử dụng hugepage, mà không cần phải cấu hình gì thêm.

Sau 20′ chờ cho thằng Oracle nó dừng và đóng database lại, khách hàng cho phép tôi khởi động lại máy chủ. Máy chủ này nó khởi động cũng tương đối nhanh, chỉ mất chưa đầy 10′ là đã xong rồi.

Việc đầu tiên phải làm là kiểm tra xem Linux đã chia RAM thành Hugepages chưa:

# cat /proc/meminfo | grep Huge
HugePages_Total: 82
HugePages_Free: 82
Hugepagesize: 262144 kB

Rồi đã có 82 hugepage đang nằm chờ thằng Oracle “sực”. Tôi khởi động Oracle, và kiểm tra lại số lượng hugepage:

# cat /proc/meminfo | grep Huge
HugePages_Total: 82
HugePages_Free: 2
Hugepagesize: 262144 kB

Yahoooo! Oracle đã “ăn” mất 80 miếng hugepage x 256M = 20G, vừa bằng với cấu hình SGA của nó. Tôi báo cho khách hàng biết, kêu nhân viên của họ làm việc lại và tôi bắt đầu theo dõi xem hệ thống vận hành thế nào.

Thật là hồi hộp, không biết những điều chỉnh của tôi có hiệu quả hay không nữa. 10′, 20′, 30′, 1 tiếng trôi qua, vmstat không thấy swap nữa rồi, top cũng không thấy kswapd nữa, toàn là Oracle, CPU idle 50%-60%, mọi thứ êm ru đến tận hôm nay!

Tôi đã từng làm rất nhiều case về performance tuning trên Linux nhưng chưa có lần nào thành công mỹ mãn như lần này, khi mà chỉ với vài dòng cấu hình đã đưa một máy chủ từ chỗ sống dở chết dở thành chạy trơn tru đến tận bây giờ, sau hơn 2 tháng mà vẫn chưa gặp sự cố nào khác. Tôi còn vui sướng hơn cả khách hàng của mình bởi lẽ nó giúp tôi lấy lại niềm tin sau nhiều lần làm performance tuning mà không thu được gì đáng kể.

Bài học rút ra từ case này: phải hiểu tận tường những công cụ mà bạn sử dụng nếu không có ngày nó sẽ gây hại khôn lường!