Ver3.0 ๐ PHP ์บ์ฑ ์ ๋ต ์๋ฒฝ ๊ฐ์ด๋: ๋ฉ๋ชจ๋ฆฌ ์บ์์ ํ์ผ ์บ์๋ก ์ฑ๋ฅ ํญ๋ฐ์ํค๊ธฐ

๐ PHP ์บ์ฑ ์ ๋ต ์๋ฒฝ ๊ฐ์ด๋: ๋ฉ๋ชจ๋ฆฌ ์บ์์ ํ์ผ ์บ์๋ก ์ฑ๋ฅ ํญ๋ฐ์ํค๊ธฐ
์น ์ ํ๋ฆฌ์ผ์ด์ ์๋๋ฅผ 10๋ฐฐ ๋น ๋ฅด๊ฒ ๋ง๋๋ ์ค์ ์บ์ฑ ํ ํฌ๋
๐ฌ ์น๊ตฌ์ผ, ํน์ ๋์ PHP ์น์ฌ์ดํธ๊ฐ ๊ฑฐ๋ถ์ด์ฒ๋ผ ๋๋ฆฌ๊ฒ ์์ง์ด๋ ๊ฑฐ ๊ฐ์ง ์์? ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ํด๋ฆญํ๋๋ฐ 3์ด, 5์ด์ฉ ๊ธฐ๋ค๋ ค์ผ ํ๋ค๋ฉด ๊ทธ๊ฑด ์ ๋ง ํฐ ๋ฌธ์ ์ผ! ์ค๋์ ๋์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ก์ผ์ฒ๋ผ ๋น ๋ฅด๊ฒ ๋ง๋ค์ด์ค ์บ์ฑ ์ ๋ต์ ๋ํด ๊น์ด ์๊ฒ ํํค์ณ๋ณผ ๊ฑฐ์ผ. ๐ฏ
์บ์ฑ์ ๋จ์ํ "๋น ๋ฅด๊ฒ ๋ง๋๋ ๊ธฐ์ "์ด ์๋๋ผ, ์๋ฒ ์์์ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ๋ ํต์ฌ ์ ๋ต์ด์ผ. ๋ฉ๋ชจ๋ฆฌ ์บ์์ ํ์ผ ์บ์, ์ด ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ ๋๋ก ์ดํดํ๊ณ ํ์ฉํ๋ฉด ๋์ ๊ฐ๋ฐ ์ค๋ ฅ๋ ํ ๋จ๊ณ ์ ๊ทธ๋ ์ด๋๋ ๊ฑฐ์ผ!
๐ฏ ์บ์ฑ์ด ๋ญ๊ธธ๋ ์ด๋ ๊ฒ ์ค์ํ ๊น?
์, ๋จผ์ ๊ธฐ๋ณธ๋ถํฐ ํ์คํ๊ฒ ์ง๊ณ ๋์ด๊ฐ์! ์บ์ฑ(Caching)์ ์์ฃผ ์ฌ์ฉ๋๋ ๋ฐ์ดํฐ๋ฅผ ๋น ๋ฅด๊ฒ ์ ๊ทผํ ์ ์๋ ์์ ์ ์ฅ์์ ๋ณด๊ดํ๋ ๊ธฐ์ ์ด์ผ. ๋ง์น ๋๊ฐ ์์ฃผ ์ฐ๋ ๋ฌผ๊ฑด์ ์ฑ ์ ์์ ์ฌ๋ ค๋๋ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ. ์๋ ๊น์์ด ๋ฃ์ด๋๋ฉด ๊บผ๋ด๋ ๋ฐ ์๊ฐ์ด ๊ฑธ๋ฆฌ์ง๋ง, ์ฑ ์ ์์ ์์ผ๋ฉด ๋ฐ๋ก ์ง์ ์ ์์์? ๐
PHP ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์บ์ฑ์ด ํ์ํ ์ด์ ๋ ๋ช ํํด:
๐ฅ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ๋ถํ ๊ฐ์
๋งค๋ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ๋ ๊ฑด ์๋ฒ์๊ฒ ์์ฒญ๋ ์คํธ๋ ์ค์ผ. ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ณตํด์ ์กฐํํ๋ค๋ฉด? ๊ทธ๊ฑด ์์ ๋ญ๋น์ง!
โก ์๋ต ์๋ ํฅ์
์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ์์ฒญํ์ ๋ 0.1์ด ๋ง์ ๋ณด์ฌ์ฃผ๋ ๊ฒ๊ณผ 3์ด ํ์ ๋ณด์ฌ์ฃผ๋ ๊ฒ, ์ด๋ ์ชฝ์ด ์ข์๊น? ๋น์ฐํ ๋น ๋ฅผ์๋ก ์ข์ง!
๐ฐ ์๋ฒ ๋น์ฉ ์ ๊ฐ
ํจ์จ์ ์ธ ์บ์ฑ์ ์๋ฒ ์์์ ์๊ปด์ฃผ๊ณ , ๊ฒฐ๊ณผ์ ์ผ๋ก ์ธํ๋ผ ๋น์ฉ๋ ์ค์ฌ์ค.
๐จ ์ฌ์ฉ์ ๊ฒฝํ ๊ฐ์
๋น ๋ฅธ ์น์ฌ์ดํธ๋ ์ฌ์ฉ์๋ฅผ ํ๋ณตํ๊ฒ ๋ง๋ค๊ณ , ์ดํ๋ฅ ์ ๋ฎ์ถฐ์ค. ๊ตฌ๊ธ ๊ฒ์ ์์์๋ ๊ธ์ ์ ์ธ ์ํฅ์ ๋ฏธ์น์ง!
๐ง ๋ฉ๋ชจ๋ฆฌ ์บ์: ๋ฒ๊ฐ์ฒ๋ผ ๋น ๋ฅธ ์๋์ ๋น๋ฐ
๋ฉ๋ชจ๋ฆฌ ์บ์๋ RAM(Random Access Memory)์ ํ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฐฉ์์ด์ผ. RAM์ ์ปดํจํฐ์์ ๊ฐ์ฅ ๋น ๋ฅธ ์ ์ฅ ๋งค์ฒด ์ค ํ๋๊ฑฐ๋ ! ํ๋๋์คํฌ๋ SSD๋ณด๋ค ์์ญ ๋ฐฐ์์ ์๋ฐฑ ๋ฐฐ ๋น ๋ฅด๋ค๊ณ ์๊ฐํ๋ฉด ๋ผ. ๐
๐ ๋ฉ๋ชจ๋ฆฌ ์บ์์ ์ฃผ์ ํน์ง
โ ์ฅ์ ๋ค
1. ์๋์ ์ธ ์๋
๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ์๋๋ ๋๋
ธ์ด(ns) ๋จ์์ผ. ๋์คํฌ I/O๊ฐ ๋ฐ๋ฆฌ์ด(ms) ๋จ์์ธ ๊ฒ๊ณผ ๋น๊ตํ๋ฉด ์์ฒญ๋ ์ฐจ์ด์ง!
2. ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ ์ง์
๋จ์ ๋ฌธ์์ด๋ฟ๋ง ์๋๋ผ ๋ฐฐ์ด, ๊ฐ์ฒด, ๋ฆฌ์คํธ, ํด์ ๋ฑ ๋ค์ํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ ์ฅํ ์ ์์ด.
3. ์์์ ์ฐ์ฐ ์ง์
์ฆ๊ฐ(increment), ๊ฐ์(decrement) ๊ฐ์ ์ฐ์ฐ์ ๋์์ฑ ๋ฌธ์ ์์ด ์์ ํ๊ฒ ์ํํ ์ ์์ด.
4. ๋ถ์ฐ ํ๊ฒฝ ์ง์
์ฌ๋ฌ ์๋ฒ๊ฐ ํ๋์ ์บ์ ์๋ฒ๋ฅผ ๊ณต์ ํ ์ ์์ด์ ํ์ฅ์ฑ์ด ๋ฐ์ด๋.
โ ๏ธ ๋จ์ ๊ณผ ์ฃผ์์ฌํญ
1. ํ๋ฐ์ฑ
์๋ฒ๊ฐ ์ฌ์์๋๊ฑฐ๋ ์ ์์ด ๊บผ์ง๋ฉด ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ์ฌ๋ผ์ ธ. ์๊ตฌ ๋ณด๊ด์ด ํ์ํ ๋ฐ์ดํฐ๋ ์ ํฉํ์ง ์์.
2. ๋ฉ๋ชจ๋ฆฌ ์ฉ๋ ์ ํ
RAM์ ๋น์ธ๊ณ ์ฉ๋์ด ์ ํ์ ์ด์ผ. ๋ฌดํ์ ์ ์ฅํ ์ ์๋ค๋ ์ ์ ๋ช
์ฌํด์ผ ํด.
3. ์ถ๊ฐ ์ธํ๋ผ ํ์
Redis๋ Memcached ๊ฐ์ ๋ณ๋์ ์บ์ ์๋ฒ๋ฅผ ์ค์นํ๊ณ ๊ด๋ฆฌํด์ผ ํด.
๐ ๏ธ PHP์์ ์ฌ์ฉํ๋ ์ฃผ์ ๋ฉ๋ชจ๋ฆฌ ์บ์ ์๋ฃจ์
1๏ธโฃ Redis (Remote Dictionary Server)
Redis๋ ํ์ฌ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ ์ ์ฅ์์ผ. ๋จ์ํ ์บ์๋ฅผ ๋์ด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค, ๋ฉ์์ง ๋ธ๋ก์ปค๋ก๋ ํ์ฉํ ์ ์์ง. ๐ฏ
Key-Value ์ ์ฅ๋ฐ์ดํฐ ๊ตฌ์กฐ ์๋ฒPub/Sub ์ง์์์์ฑ ์ต์
Redis์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค:
โข ๋ค์ํ ๋ฐ์ดํฐ ํ์
: String, List, Set, Sorted Set, Hash, Bitmap, HyperLogLog ๋ฑ
โข TTL(Time To Live): ์๋์ผ๋ก ๋ง๋ฃ๋๋ ํค ์ค์ ๊ฐ๋ฅ
โข ํธ๋์ญ์
: ์ฌ๋ฌ ๋ช
๋ น์ ์์์ ์ผ๋ก ์คํ
โข Lua ์คํฌ๋ฆฝํ
: ๋ณต์กํ ๋ก์ง์ ์๋ฒ ์ฌ์ด๋์์ ์คํ
โข ๋ณต์ ์ ํด๋ฌ์คํฐ๋ง: ๊ณ ๊ฐ์ฉ์ฑ๊ณผ ํ์ฅ์ฑ ์ ๊ณต
<?php
// Redis ์ฐ๊ฒฐ
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// ๊ฐ๋จํ ๋ฌธ์์ด ์ ์ฅ (10๋ถ ๋์ ์ ํจ)
$redis->setex('user:1000:name', 600, 'John Doe');
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
$userName = $redis->get('user:1000:name');
echo $userName; // "John Doe"
// ํด์ ๋ฐ์ดํฐ ์ ์ฅ
$redis->hMSet('user:1000', [
'name' => 'John Doe',
'email' => 'john@example.com',
'age' => 30
]);
// ํด์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
$userInfo = $redis->hGetAll('user:1000');
print_r($userInfo);
// ๋ฆฌ์คํธ์ ๋ฐ์ดํฐ ์ถ๊ฐ
$redis->rPush('recent_posts', 'post_id_1');
$redis->rPush('recent_posts', 'post_id_2');
$redis->rPush('recent_posts', 'post_id_3');
// ๋ฆฌ์คํธ์์ ์ต๊ทผ 5๊ฐ ๊ฐ์ ธ์ค๊ธฐ
$recentPosts = $redis->lRange('recent_posts', 0, 4);
// ์นด์ดํฐ ์ฆ๊ฐ (์กฐํ์ ๋ฑ)
$redis->incr('post:1000:views');
$views = $redis->get('post:1000:views');
// ํค ์ญ์
$redis->del('user:1000:name');
?>
2๏ธโฃ Memcached
Memcached๋ Redis๋ณด๋ค ๋จผ์ ๋์จ ๋ฉ๋ชจ๋ฆฌ ์บ์ฑ ์์คํ ์ด์ผ. ๋จ์ํ๊ณ ๊ฐ๋ณ์ง๋ง ๊ทธ๋งํผ ๊ฐ๋ ฅํด! ํนํ ๋๊ท๋ชจ ๋ถ์ฐ ํ๊ฒฝ์์ ๋น์ ๋ฐํ์ง. ๐ช
์ด๊ณ ์๋จ์ํจ๋ฉํฐ์ค๋ ๋๋ถ์ฐ ์บ์ฑ
Memcached์ ํน์ง:
โข ๋จ์ํ Key-Value ์ ์ฅ: ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ ์ง์ํ์ง ์์ง๋ง ๊ทธ๋งํผ ๋น ๋ฆ
โข ๋ฉํฐ์ค๋ ๋ ์ํคํ
์ฒ: ๋ฉํฐ์ฝ์ด CPU๋ฅผ ํจ์จ์ ์ผ๋ก ํ์ฉ
โข LRU(Least Recently Used) ์๊ณ ๋ฆฌ์ฆ: ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ถ์กฑํ๋ฉด ๊ฐ์ฅ ์ค๋๋ ๋ฐ์ดํฐ ์๋ ์ญ์
โข ๋ถ์ฐ ํด์ฑ: ์ฌ๋ฌ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ๋ถ์ฐ ์ ์ฅ
<?php
// Memcached ์ฐ๊ฒฐ
$memcached = new Memcached();
$memcached->addServer('127.0.0.1', 11211);
// ๋ฐ์ดํฐ ์ ์ฅ (600์ด = 10๋ถ)
$memcached->set('user_1000', [
'name' => 'Jane Smith',
'email' => 'jane@example.com'
], 600);
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
$user = $memcached->get('user_1000');
if ($user !== false) {
echo "User found: " . $user['name'];
} else {
echo "User not in cache";
}
// ์ฌ๋ฌ ๋ฐ์ดํฐ ํ ๋ฒ์ ์ ์ฅ
$memcached->setMulti([
'product_100' => ['name' => 'Laptop', 'price' => 1200],
'product_101' => ['name' => 'Mouse', 'price' => 25],
'product_102' => ['name' => 'Keyboard', 'price' => 80]
], 600);
// ์ฌ๋ฌ ๋ฐ์ดํฐ ํ ๋ฒ์ ๊ฐ์ ธ์ค๊ธฐ
$products = $memcached->getMulti(['product_100', 'product_101', 'product_102']);
// ์นด์ดํฐ ์ฆ๊ฐ
$memcached->increment('page_views', 1);
// ๋ฐ์ดํฐ ์ญ์
$memcached->delete('user_1000');
// ๋ชจ๋ ์บ์ ์ญ์ (์ฃผ์ํด์ ์ฌ์ฉ!)
$memcached->flush();
?>
3๏ธโฃ APCu (Alternative PHP Cache - User Cache)
APCu๋ PHP ํ๋ก์ธ์ค ๋ด๋ถ์์ ๋์ํ๋ ๋ฉ๋ชจ๋ฆฌ ์บ์์ผ. ๋ณ๋์ ์๋ฒ ์ค์น ์์ด PHP ํ์ฅ ๋ชจ๋๋ง ์ค์นํ๋ฉด ๋ฐ๋ก ์ฌ์ฉํ ์ ์์ด์ ๊ฐํธํ์ง! ๐
์ค์น ๊ฐํธ์ถ๊ฐ ์๋ฒ ๋ถํ์๋น ๋ฅธ ๋ก์ปฌ ์บ์
APCu์ ํน์ง:
โข ํ๋ก์ธ์ค ๋ ๋ฒจ ์บ์: ๊ฐ PHP ํ๋ก์ธ์ค๊ฐ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ
โข ์ค์ ๊ฐ๋จ: php.ini์์ ๋ช ๊ฐ์ง ์ค์ ๋ง ํ๋ฉด ๋
โข ๋จ์ผ ์๋ฒ ํ๊ฒฝ์ ์ต์ : ๋ถ์ฐ ํ๊ฒฝ์์๋ ๊ฐ ์๋ฒ๊ฐ ๋
๋ฆฝ์ ์ธ ์บ์ ๋ณด์
โข OPcache์ ๋ณ๊ฐ: OPcache๋ ์ฝ๋ ์บ์ฑ, APCu๋ ๋ฐ์ดํฐ ์บ์ฑ
<?php
// APCu ์ฌ์ฉ ๊ฐ๋ฅ ์ฌ๋ถ ํ์ธ
if (!function_exists('apcu_fetch')) {
die('APCu is not available');
}
// ๋ฐ์ดํฐ ์ ์ฅ (600์ด ๋์ ์ ํจ)
apcu_store('config_settings', [
'site_name' => 'My Awesome Site',
'theme' => 'dark',
'language' => 'ko'
], 600);
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
$config = apcu_fetch('config_settings', $success);
if ($success) {
echo "Site name: " . $config['site_name'];
} else {
echo "Config not in cache";
}
// ์ฌ๋ฌ ๋ฐ์ดํฐ ํ ๋ฒ์ ์ ์ฅ
apcu_store([
'cache_key_1' => 'value_1',
'cache_key_2' => 'value_2',
'cache_key_3' => 'value_3'
]);
// ์นด์ดํฐ ์ฆ๊ฐ
apcu_inc('visitor_count', 1);
$visitors = apcu_fetch('visitor_count');
// ๋ฐ์ดํฐ ์ญ์
apcu_delete('config_settings');
// ์ ์ฒด ์บ์ ์ญ์
apcu_clear_cache();
// ์บ์ ์ ๋ณด ํ์ธ
$cacheInfo = apcu_cache_info();
echo "Cached entries: " . $cacheInfo['num_entries'];
?>
๐พ ํ์ผ ์บ์: ์์ ์ ์ด๊ณ ์๊ตฌ์ ์ธ ์ ์ฅ์
ํ์ผ ์บ์๋ ๋ง ๊ทธ๋๋ก ํ์ผ ์์คํ ์ ํ์ฉํด์ ์บ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฐฉ์์ด์ผ. ๋ฉ๋ชจ๋ฆฌ ์บ์๋ณด๋ค๋ ๋๋ฆฌ์ง๋ง, ์๋ฒ๊ฐ ์ฌ์์๋์ด๋ ๋ฐ์ดํฐ๊ฐ ์ ์ง๋๊ณ ๋ณ๋์ ์ธํ๋ผ๊ฐ ํ์ ์๋ค๋ ํฐ ์ฅ์ ์ด ์์ง! ๐
๐ ํ์ผ ์บ์์ ์ฃผ์ ํน์ง
โ ์ฅ์ ๋ค
1. ์์์ฑ
์๋ฒ๊ฐ ์ฌ์์๋์ด๋ ์บ์ ๋ฐ์ดํฐ๊ฐ ๊ทธ๋๋ก ์ ์ง๋ผ. ์ค์ํ ์ค์ ์ด๋ ์์ฃผ ๋ณํ์ง ์๋ ๋ฐ์ดํฐ์ ์ ํฉํด.
2. ์ถ๊ฐ ์ธํ๋ผ ๋ถํ์
Redis๋ Memcached ๊ฐ์ ๋ณ๋์ ์๋ฒ๋ฅผ ์ค์นํ๊ณ ๊ด๋ฆฌํ ํ์๊ฐ ์์ด. ํ์ผ ์์คํ
๋ง ์์ผ๋ฉด ๋ผ!
3. ์ฉ๋ ์ ํ ์ํ
๋์คํฌ ๊ณต๊ฐ์ด ํ์ฉํ๋ ํ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์ ์์ด. RAM๋ณด๋ค ํจ์ฌ ์ ๋ ดํ๊ณ ์ฉ๋๋ ํฌ์ง.
4. ๋๋ฒ๊น
์ฉ์ด
ํ์ผ๋ก ์ ์ฅ๋๊ธฐ ๋๋ฌธ์ ์ง์ ์ด์ด์ ๋ด์ฉ์ ํ์ธํ๊ณ ์์ ํ ์ ์์ด.
5. ๊ฐ๋จํ ๊ตฌํ
๋ณต์กํ ์ค์ ์์ด PHP์ ๊ธฐ๋ณธ ํ์ผ ํจ์๋ง์ผ๋ก๋ ๊ตฌํ ๊ฐ๋ฅํด.
โ ๏ธ ๋จ์ ๊ณผ ์ฃผ์์ฌํญ
1. ์๋์ ์ผ๋ก ๋๋ฆฐ ์๋
๋์คํฌ I/O๋ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ๋ณด๋ค ํจ์ฌ ๋๋ ค. ํนํ HDD๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๋์ฑ ๊ทธ๋.
2. ๋์์ฑ ๋ฌธ์
์ฌ๋ฌ ํ๋ก์ธ์ค๊ฐ ๋์์ ๊ฐ์ ํ์ผ์ ์ ๊ทผํ๋ฉด ์ถฉ๋์ด ๋ฐ์ํ ์ ์์ด. ํ์ผ ์ ๊ธ(locking) ๋ฉ์ปค๋์ฆ์ด ํ์ํด.
3. ํ์ผ ์์คํ
์ค๋ฒํค๋
์๋ง์ ์์ ํ์ผ๋ค์ด ์์ฑ๋๋ฉด ํ์ผ ์์คํ
์ฑ๋ฅ์ด ์ ํ๋ ์ ์์ด.
4. ์บ์ ๋ฌดํจํ ๊ด๋ฆฌ
์ค๋๋ ์บ์ ํ์ผ์ ์ ๋ฆฌํ๋ ๋ก์ง์ ์ง์ ๊ตฌํํด์ผ ํด.
5. ๋ถ์ฐ ํ๊ฒฝ ์ ์ฝ
์ฌ๋ฌ ์๋ฒ ํ๊ฒฝ์์๋ ๊ฐ ์๋ฒ๊ฐ ๋
๋ฆฝ์ ์ธ ์บ์๋ฅผ ๊ฐ์ง๊ฒ ๋ผ. ๊ณต์ ํ์ผ ์์คํ
์ ์ฌ์ฉํ์ง ์๋ ํ ๋๊ธฐํ๊ฐ ์ด๋ ค์.
๐ ๏ธ PHP์์ ํ์ผ ์บ์ ๊ตฌํํ๊ธฐ
ํ์ผ ์บ์๋ ์ง์ ๊ตฌํํ ์๋ ์๊ณ , ํ๋ ์์ํฌ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ ์๋ ์์ด. ๋จผ์ ์ง์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ๋ถํฐ ์ดํด๋ณด์! ๐ป
<?php
class SimpleFileCache {
private $cacheDir;
private $defaultTTL;
public function __construct($cacheDir = './cache', $defaultTTL = 3600) {
$this->cacheDir = rtrim($cacheDir, '/');
$this->defaultTTL = $defaultTTL;
// ์บ์ ๋๋ ํ ๋ฆฌ๊ฐ ์์ผ๋ฉด ์์ฑ
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0755, true);
}
}
/**
* ์บ์ ํค๋ฅผ ํ์ผ ๊ฒฝ๋ก๋ก ๋ณํ
*/
private function getFilePath($key) {
$hash = md5($key);
// ๋๋ ํ ๋ฆฌ ๋ถ์ฐ (์ฒซ 2์๋ฆฌ๋ก ์๋ธ ๋๋ ํ ๋ฆฌ ์์ฑ)
$subDir = substr($hash, 0, 2);
$dir = $this->cacheDir . '/' . $subDir;
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
return $dir . '/' . $hash . '.cache';
}
/**
* ์บ์์ ๋ฐ์ดํฐ ์ ์ฅ
*/
public function set($key, $value, $ttl = null) {
$ttl = $ttl ?? $this->defaultTTL;
$filePath = $this->getFilePath($key);
$data = [
'expires' => time() + $ttl,
'value' => $value
];
// ์ง๋ ฌํํ์ฌ ์ ์ฅ
$serialized = serialize($data);
// ํ์ผ ์ ๊ธ์ ์ฌ์ฉํ์ฌ ์์ ํ๊ฒ ์ฐ๊ธฐ
$fp = fopen($filePath, 'w');
if (flock($fp, LOCK_EX)) {
fwrite($fp, $serialized);
flock($fp, LOCK_UN);
}
fclose($fp);
return true;
}
/**
* ์บ์์์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
*/
public function get($key, $default = null) {
$filePath = $this->getFilePath($key);
if (!file_exists($filePath)) {
return $default;
}
// ํ์ผ ์ ๊ธ์ ์ฌ์ฉํ์ฌ ์์ ํ๊ฒ ์ฝ๊ธฐ
$fp = fopen($filePath, 'r');
if (flock($fp, LOCK_SH)) {
$content = fread($fp, filesize($filePath));
flock($fp, LOCK_UN);
}
fclose($fp);
$data = unserialize($content);
// ๋ง๋ฃ ํ์ธ
if ($data['expires'] < time()) {
$this->delete($key);
return $default;
}
return $data['value'];
}
/**
* ์บ์ ๋ฐ์ดํฐ ์ญ์
*/
public function delete($key) {
$filePath = $this->getFilePath($key);
if (file_exists($filePath)) {
return unlink($filePath);
}
return false;
}
/**
* ๋ชจ๋ ์บ์ ์ญ์
*/
public function clear() {
$this->deleteDirectory($this->cacheDir);
mkdir($this->cacheDir, 0755, true);
return true;
}
/**
* ๋ง๋ฃ๋ ์บ์ ์ ๋ฆฌ
*/
public function cleanup() {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($this->cacheDir),
RecursiveIteratorIterator::SELF_FIRST
);
$deletedCount = 0;
foreach ($iterator as $file) {
if ($file->isFile() && $file->getExtension() === 'cache') {
$content = file_get_contents($file->getPathname());
$data = unserialize($content);
if ($data['expires'] < time()) {
unlink($file->getPathname());
$deletedCount++;
}
}
}
return $deletedCount;
}
/**
* ๋๋ ํ ๋ฆฌ ์ฌ๊ท์ ์ญ์
*/
private function deleteDirectory($dir) {
if (!is_dir($dir)) {
return false;
}
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $dir . '/' . $item;
if (is_dir($path)) {
$this->deleteDirectory($path);
} else {
unlink($path);
}
}
return rmdir($dir);
}
}
// ์ฌ์ฉ ์์
$cache = new SimpleFileCache('./cache', 600); // 10๋ถ TTL
// ๋ฐ์ดํฐ ์ ์ฅ
$cache->set('user_profile_1000', [
'name' => 'Alice',
'email' => 'alice@example.com',
'role' => 'admin'
]);
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
$profile = $cache->get('user_profile_1000');
if ($profile) {
echo "User: " . $profile['name'];
} else {
echo "Cache miss!";
}
// ๋ง๋ฃ๋ ์บ์ ์ ๋ฆฌ
$deleted = $cache->cleanup();
echo "Deleted {$deleted} expired cache files";
?>
๐จ ํ๋ ์์ํฌ์ ํ์ผ ์บ์ ํ์ฉ
๋๋ถ๋ถ์ PHP ํ๋ ์์ํฌ๋ ์ด๋ฏธ ์ ๋ง๋ค์ด์ง ์บ์ ์์คํ ์ ์ ๊ณตํด. ์ง์ ๊ตฌํํ๋ ๊ฒ๋ณด๋ค ํ๋ ์์ํฌ์ ์บ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ํจ์ฌ ์์ ํ๊ณ ํจ์จ์ ์ด์ผ! ๐
<?php
use Illuminate\Support\Facades\Cache;
// ๋ฐ์ดํฐ ์ ์ฅ (10๋ถ)
Cache::put('products', $products, 600);
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
$products = Cache::get('products');
// ์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ ๋ฐํ
$products = Cache::get('products', []);
// ์๊ตฌ ์ ์ฅ
Cache::forever('site_settings', $settings);
// ์กด์ฌ ์ฌ๋ถ ํ์ธ
if (Cache::has('products')) {
echo "Cache exists!";
}
// ์์ผ๋ฉด ๊ฐ์ ธ์์ ์ ์ฅ (ํด๋ก์ ์ฌ์ฉ)
$products = Cache::remember('products', 600, function() {
return DB::table('products')->get();
});
// ์ญ์
Cache::forget('products');
// ์ ์ฒด ์ญ์
Cache::flush();
// ํ๊ทธ๋ฅผ ์ฌ์ฉํ ๊ทธ๋ฃน ๊ด๋ฆฌ (Redis/Memcached๋ง ์ง์)
Cache::tags(['products', 'featured'])->put('featured_products', $data, 600);
Cache::tags(['products'])->flush(); // products ํ๊ทธ์ ๋ชจ๋ ์บ์ ์ญ์
?>
<?php
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
// ํ์ผ ์บ์ ์ด๋ํฐ ์์ฑ
$cache = new FilesystemAdapter(
'app_cache', // ๋ค์์คํ์ด์ค
3600, // ๊ธฐ๋ณธ TTL
__DIR__ . '/cache' // ์บ์ ๋๋ ํ ๋ฆฌ
);
// ์บ์ ์์ดํ
๊ฐ์ ธ์ค๊ธฐ
$productsCache = $cache->getItem('products');
if (!$productsCache->isHit()) {
// ์บ์ ๋ฏธ์ค - ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ธ์ค๊ธฐ
$products = fetchProductsFromDatabase();
// ์บ์์ ์ ์ฅ
$productsCache->set($products);
$productsCache->expiresAfter(600); // 10๋ถ
$cache->save($productsCache);
} else {
// ์บ์ ํํธ
$products = $productsCache->get();
}
// ์ฌ๋ฌ ์์ดํ
ํ ๋ฒ์ ์ฒ๋ฆฌ
$items = $cache->getItems(['product_1', 'product_2', 'product_3']);
foreach ($items as $key => $item) {
if (!$item->isHit()) {
$item->set(fetchProduct($key));
$cache->save($item);
}
}
// ์บ์ ์ญ์
$cache->deleteItem('products');
// ์ ์ฒด ์บ์ ์ญ์
$cache->clear();
?>
โ๏ธ ๋ฉ๋ชจ๋ฆฌ ์บ์ vs ํ์ผ ์บ์: ์ธ์ ๋ฌด์์ ์ ํํ ๊น?
์, ์ด์ ๊ฐ์ฅ ์ค์ํ ์ง๋ฌธ์ด์ผ! ๋ฉ๋ชจ๋ฆฌ ์บ์์ ํ์ผ ์บ์ ์ค์์ ์ด๋ค ๊ฑธ ์ ํํด์ผ ํ ๊น? ์ ๋ต์ "์ํฉ์ ๋ฐ๋ผ ๋ค๋ฅด๋ค"์ผ. ๊ฐ๊ฐ์ ์ฅ๋จ์ ์ ์ดํดํ๊ณ ํ๋ก์ ํธ์ ์๊ตฌ์ฌํญ์ ๋ง๊ฒ ์ ํํ๋ ๊ฒ ์ค์ํด! ๐ค
๐ง ๋ฉ๋ชจ๋ฆฌ ์บ์๋ฅผ ์ ํํด์ผ ํ ๋
โ
์ด๊ณ ์ ์๋ต์ด ํ์ํ ๊ฒฝ์ฐ
์ค์๊ฐ ์ฑํ
, ๊ฒ์ ์๋ฒ, ๊ธ์ต ๊ฑฐ๋ ๋ฑ ๋ฐ๋ฆฌ์ด ๋จ์์ ์๋ต ์๊ฐ์ด ์ค์ํ ์ ํ๋ฆฌ์ผ์ด์
โ
์์ฃผ ๋ณ๊ฒฝ๋๋ ๋ฐ์ดํฐ
์ธ์
์ ๋ณด, ์ค์๊ฐ ํต๊ณ, ์์ ํ ํฐ ๋ฑ ํ๋ฐ์ฑ์ด ๋ฌธ์ ๋์ง ์๋ ๋ฐ์ดํฐ
โ
๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๊ฐ ํ์ํ ๊ฒฝ์ฐ
๋ฆฌ์คํธ, ์
, ํด์ ๋ฑ์ ์๋ฃ๊ตฌ์กฐ๋ฅผ ํ์ฉํ ๊ณ ๊ธ ๊ธฐ๋ฅ์ด ํ์ํ ๋
โ
๋ถ์ฐ ํ๊ฒฝ
์ฌ๋ฌ ์น ์๋ฒ๊ฐ ํ๋์ ์บ์๋ฅผ ๊ณต์ ํด์ผ ํ๋ ๋ก๋๋ฐธ๋ฐ์ฑ ํ๊ฒฝ
โ
๋์์ฑ ์ ์ด๊ฐ ์ค์ํ ๊ฒฝ์ฐ
์์์ ์ฐ์ฐ(atomic operations)์ด ํ์ํ ์นด์ดํฐ, ์ฌ๊ณ ๊ด๋ฆฌ ๋ฑ
๐พ ํ์ผ ์บ์๋ฅผ ์ ํํด์ผ ํ ๋
โ
์์์ฑ์ด ์ค์ํ ๊ฒฝ์ฐ
์ค์ ํ์ผ, ์ปดํ์ผ๋ ํ
ํ๋ฆฟ, ์ ์ ์ฝํ
์ธ ๋ฑ ์๋ฒ ์ฌ์์ ํ์๋ ์ ์ง๋์ด์ผ ํ๋ ๋ฐ์ดํฐ
โ
์ธํ๋ผ ์ ์ฝ์ด ์๋ ๊ฒฝ์ฐ
๋ณ๋์ ์บ์ ์๋ฒ๋ฅผ ์ด์ํ ์ ์๊ฑฐ๋, ์์ฐ์ด ์ ํ์ ์ธ ์๊ท๋ชจ ํ๋ก์ ํธ
โ
๋์ฉ๋ ๋ฐ์ดํฐ
์ด๋ฏธ์ง ์ธ๋ค์ผ, ๋์ฉ๋ ๋ณด๊ณ ์ ๋ฑ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ๊ธฐ์ ํฐ ๋ฐ์ดํฐ
โ
๋ณ๊ฒฝ ๋น๋๊ฐ ๋ฎ์ ๋ฐ์ดํฐ
์ํ ์นดํ๋ก๊ทธ, ์นดํ
๊ณ ๋ฆฌ ์ ๋ณด, ์ ์ ํ์ด์ง ๋ฑ ์์ฃผ ๋ฐ๋์ง ์๋ ๋ฐ์ดํฐ
โ
๋จ์ผ ์๋ฒ ํ๊ฒฝ
ํธ๋ํฝ์ด ์ ๊ณ ํ ๋์ ์๋ฒ๋ก ์ถฉ๋ถํ ์๊ท๋ชจ ์น์ฌ์ดํธ
๐ฏ ํ์ด๋ธ๋ฆฌ๋ ์ ๋ต: ๋ ๊ฐ์ง๋ฅผ ํจ๊ป ์ฌ์ฉํ๊ธฐ
์ค์ ํ๋ก๋์ ํ๊ฒฝ์์๋ ๋ฉ๋ชจ๋ฆฌ ์บ์์ ํ์ผ ์บ์๋ฅผ ๊ณ์ธต์ ์ผ๋ก ํจ๊ป ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์! ์ด๋ฅผ ๋ค๋จ๊ณ ์บ์ฑ(Multi-tier Caching)์ด๋ผ๊ณ ํด. ๐จ
์ ํ์ ์ธ 3๋จ๊ณ ์บ์ฑ ๊ตฌ์กฐ:
1๏ธโฃ L1 ์บ์ (APCu): ํ๋ก์ธ์ค ๋ ๋ฒจ์ ์ด๊ณ ์ ์บ์
2๏ธโฃ L2 ์บ์ (Redis/Memcached): ์๋ฒ ๊ฐ ๊ณต์ ๋๋ ๋ฉ๋ชจ๋ฆฌ ์บ์
3๏ธโฃ L3 ์บ์ (ํ์ผ ์บ์): ์์์ ์ธ ๋ฐฑ์
์บ์
๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ๋๋ L1 โ L2 โ L3 โ ์๋ณธ ์์๋ก ํ์ธํ๊ณ , ์ ์ฅํ ๋๋ ๋ชจ๋ ๋ ๋ฒจ์ ๋์์ ์ ์ฅํ๋ ๋ฐฉ์์ด์ผ!
<?php
class MultiTierCache {
private $l1Cache; // APCu
private $l2Cache; // Redis
private $l3Cache; // File Cache
public function __construct($redis, $fileCache) {
$this->l2Cache = $redis;
$this->l3Cache = $fileCache;
}
public function get($key) {
// L1 ์บ์ ํ์ธ (APCu)
if (function_exists('apcu_fetch')) {
$value = apcu_fetch($key, $success);
if ($success) {
return $value;
}
}
// L2 ์บ์ ํ์ธ (Redis)
$value = $this->l2Cache->get($key);
if ($value !== false) {
// L1์ ์ ์ฅ
if (function_exists('apcu_store')) {
apcu_store($key, $value, 300); // 5๋ถ
}
return $value;
}
// L3 ์บ์ ํ์ธ (File)
$value = $this->l3Cache->get($key);
if ($value !== null) {
// L1, L2์ ์ ์ฅ
if (function_exists('apcu_store')) {
apcu_store($key, $value, 300);
}
$this->l2Cache->setex($key, 600, $value); // 10๋ถ
return $value;
}
// ์บ์ ๋ฏธ์ค
return null;
}
public function set($key, $value, $ttl = 3600) {
// ๋ชจ๋ ๋ ๋ฒจ์ ์ ์ฅ
if (function_exists('apcu_store')) {
apcu_store($key, $value, min($ttl, 300)); // L1์ ์ต๋ 5๋ถ
}
$this->l2Cache->setex($key, min($ttl, 3600), $value); // L2๋ ์ต๋ 1์๊ฐ
$this->l3Cache->set($key, $value, $ttl); // L3๋ ์ ์ฒด TTL
return true;
}
public function delete($key) {
// ๋ชจ๋ ๋ ๋ฒจ์์ ์ญ์
if (function_exists('apcu_delete')) {
apcu_delete($key);
}
$this->l2Cache->del($key);
$this->l3Cache->delete($key);
return true;
}
}
// ์ฌ์ฉ ์์
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$fileCache = new SimpleFileCache('./cache');
$cache = new MultiTierCache($redis, $fileCache);
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ (์๋์ผ๋ก L1 โ L2 โ L3 ์์๋ก ํ์ธ)
$userData = $cache->get('user_1000');
if ($userData === null) {
// ์บ์ ๋ฏธ์ค - ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ธ์ค๊ธฐ
$userData = fetchUserFromDatabase(1000);
// ๋ชจ๋ ์บ์ ๋ ๋ฒจ์ ์ ์ฅ
$cache->set('user_1000', $userData, 3600);
}
echo "User: " . $userData['name'];
?>
๐ ์ค์ ์บ์ฑ ์ ๋ต๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋, ์ด์ ์ค์ ์์ ๋ฐ๋ก ์ ์ฉํ ์ ์๋ ๊ตฌ์ฒด์ ์ธ ์ ๋ต๋ค์ ์์๋ณด์! ์ด ํ๋ค์ ๋ด๊ฐ ์ค์ ํ๋ก์ ํธ์์ ๊ฒฝํํ๊ณ ๊ฒ์ฆํ ๊ฒ๋ค์ด์ผ. ๐ก
1๏ธโฃ ์บ์ ํค ๋ค์ด๋ฐ ์ ๋ต
์บ์ ํค๋ฅผ ์ ์ค๊ณํ๋ ๊ฒ์ ์ ๋ง ์ค์ํด! ๋์ค์ ๊ด๋ฆฌํ๊ธฐ ์ฝ๊ณ , ์ถฉ๋์ ๋ฐฉ์งํ ์ ์๊ฑฐ๋ . ๐
์ถ์ฒํ๋ ๋ค์ด๋ฐ ์ปจ๋ฒค์ :
โข ๋ค์์คํ์ด์ค ์ฌ์ฉ: app:module:entity:id ํ์
โข ๋ฒ์ ํฌํจ: ์คํค๋ง ๋ณ๊ฒฝ ์ ์๋ ๋ฌดํจํ๋ฅผ ์ํด v1:user:1000
โข ๋ช
ํํ ์๋ฏธ: user_profile_1000๋ณด๋ค๋ user:profile:1000
โข ์ผ๊ด์ฑ ์ ์ง: ํ๋ก์ ํธ ์ ์ฒด์์ ๋์ผํ ๊ท์น ์ ์ฉ
<?php
class CacheKeyBuilder {
private $namespace;
private $version;
public function __construct($namespace = 'app', $version = 'v1') {
$this->namespace = $namespace;
$this->version = $version;
}
public function build($entity, $id = null, $suffix = null) {
$parts = [$this->namespace, $this->version, $entity];
if ($id !== null) {
$parts[] = $id;
}
if ($suffix !== null) {
$parts[] = $suffix;
}
return implode(':', $parts);
}
public function buildQuery($entity, array $params) {
$queryString = http_build_query($params);
$hash = md5($queryString);
return $this->build($entity, null, 'query:' . $hash);
}
}
// ์ฌ์ฉ ์์
$keyBuilder = new CacheKeyBuilder('myapp', 'v2');
$userKey = $keyBuilder->build('user', 1000);
// ๊ฒฐ๊ณผ: "myapp:v2:user:1000"
$userProfileKey = $keyBuilder->build('user', 1000, 'profile');
// ๊ฒฐ๊ณผ: "myapp:v2:user:1000:profile"
$searchKey = $keyBuilder->buildQuery('products', [
'category' => 'electronics',
'price_min' => 100,
'price_max' => 500
]);
// ๊ฒฐ๊ณผ: "myapp:v2:products:query:a1b2c3d4..."
?>
2๏ธโฃ ์บ์ ์๋ฐ(Cache Warming)
์๋ฒ๋ฅผ ์ฌ์์ํ๊ฑฐ๋ ์บ์๋ฅผ ์ด๊ธฐํํ ์งํ์๋ ๋ชจ๋ ์์ฒญ์ด ์บ์ ๋ฏธ์ค๊ฐ ๋์ด ์ฑ๋ฅ์ด ๊ธ๊ฒฉํ ๋จ์ด์ ธ. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ๋ฏธ๋ฆฌ ์บ์๋ฅผ ์ฑ์๋๋ ๊ฒ์ ์บ์ ์๋ฐ์ด๋ผ๊ณ ํด! ๐ฅ
<?php
class CacheWarmer {
private $cache;
private $db;
public function __construct($cache, $db) {
$this->cache = $cache;
$this->db = $db;
}
public function warmPopularProducts() {
echo "Warming popular products cache...\n";
// ์ธ๊ธฐ ์ํ 100๊ฐ ๊ฐ์ ธ์ค๊ธฐ
$products = $this->db->query(
"SELECT * FROM products ORDER BY views DESC LIMIT 100"
)->fetchAll();
foreach ($products as $product) {
$key = "product:{$product['id']}";
$this->cache->set($key, $product, 3600);
echo "Cached: {$key}\n";
}
echo "Done!\n";
}
public function warmCategories() {
echo "Warming categories cache...\n";
$categories = $this->db->query(
"SELECT * FROM categories WHERE active = 1"
)->fetchAll();
$this->cache->set('categories:all', $categories, 7200);
foreach ($categories as $category) {
$key = "category:{$category['id']}";
$this->cache->set($key, $category, 7200);
}
echo "Done!\n";
}
public function warmAll() {
$this->warmPopularProducts();
$this->warmCategories();
// ํ์ํ ๋ค๋ฅธ ์บ์๋ค๋ ์ถ๊ฐ...
}
}
// CLI์์ ์คํ
if (php_sapi_name() === 'cli') {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$pdo = new PDO('mysql:host=localhost;dbname=myapp', 'user', 'pass');
$warmer = new CacheWarmer($redis, $pdo);
$warmer->warmAll();
}
?>
3๏ธโฃ ์บ์ ์คํฌํผ๋(Cache Stampede) ๋ฐฉ์ง
์ธ๊ธฐ ์๋ ์บ์๊ฐ ๋ง๋ฃ๋๋ ์๊ฐ, ์๋ง์ ์์ฒญ์ด ๋์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ๋ชฐ๋ ค๊ฐ๋ ํ์์ ์บ์ ์คํฌํผ๋๋ผ๊ณ ํด. ์ด๋ ์๋ฒ๋ฅผ ๋ค์ด์ํฌ ์ ์๋ ์ฌ๊ฐํ ๋ฌธ์ ์ผ! ๐ฑ
ํด๊ฒฐ ๋ฐฉ๋ฒ:
โข ํ๋ฅ ์ ์กฐ๊ธฐ ๋ง๋ฃ: TTL์ด ๋๋๊ธฐ ์ ์ ํ๋ฅ ์ ์ผ๋ก ๊ฐฑ์
โข ๋ฝ(Lock) ๋ฉ์ปค๋์ฆ: ์ฒซ ๋ฒ์งธ ์์ฒญ๋ง ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๋๋จธ์ง๋ ๋๊ธฐ
โข ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ฐฑ์ : ๋ง๋ฃ ์ ์ ๋น๋๊ธฐ๋ก ๋ฏธ๋ฆฌ ๊ฐฑ์
<?php
class StampedeProofCache {
private $cache;
private $lockTimeout = 10; // ๋ฝ ํ์์์ (์ด)
public function __construct($cache) {
$this->cache = $cache;
}
/**
* ๋ฝ์ ์ฌ์ฉํ ์์ ํ ์บ์ ๊ฐ์ ธ์ค๊ธฐ
*/
public function remember($key, $ttl, callable $callback) {
// ์บ์ ํ์ธ
$value = $this->cache->get($key);
if ($value !== null) {
return $value;
}
// ๋ฝ ํค
$lockKey = $key . ':lock';
// ๋ฝ ํ๋ ์๋
$lockAcquired = $this->acquireLock($lockKey);
if ($lockAcquired) {
try {
// ๋ค์ ํ ๋ฒ ์บ์ ํ์ธ (๋ค๋ฅธ ํ๋ก์ธ์ค๊ฐ ์ด๋ฏธ ์ ์ฅํ์ ์ ์์)
$value = $this->cache->get($key);
if ($value !== null) {
return $value;
}
// ์ฝ๋ฐฑ ์คํํ์ฌ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
$value = $callback();
// ์บ์์ ์ ์ฅ
$this->cache->set($key, $value, $ttl);
return $value;
} finally {
// ๋ฝ ํด์
$this->releaseLock($lockKey);
}
} else {
// ๋ฝ ํ๋ ์คํจ - ๋ค๋ฅธ ํ๋ก์ธ์ค๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์ค
// ์งง์ ์๊ฐ ๋๊ธฐ ํ ์ฌ์๋
$maxRetries = 20;
$retryDelay = 100000; // 0.1์ด (๋ง์ดํฌ๋ก์ด)
for ($i = 0; $i < $maxRetries; $i++) {
usleep($retryDelay);
$value = $this->cache->get($key);
if ($value !== null) {
return $value;
}
}
// ํ์์์ - ์ด์ฉ ์ ์์ด ์ง์ ๊ฐ์ ธ์ค๊ธฐ
return $callback();
}
}
/**
* ๋ฝ ํ๋
*/
private function acquireLock($lockKey) {
// Redis์ SETNX๋ฅผ ์ฌ์ฉํ ๋ฝ (SET if Not eXists)
return $this->cache->set(
$lockKey,
1,
['nx', 'ex' => $this->lockTimeout]
);
}
/**
* ๋ฝ ํด์
*/
private function releaseLock($lockKey) {
$this->cache->del($lockKey);
}
/**
* ํ๋ฅ ์ ์กฐ๊ธฐ ๋ง๋ฃ๋ฅผ ์ฌ์ฉํ ๋ฐฉ๋ฒ
*/
public function rememberWithProbabilisticExpiry($key, $ttl, callable $callback) {
$data = $this->cache->get($key . ':data');
if ($data !== null) {
$stored = unserialize($data);
$value = $stored['value'];
$createdAt = $stored['created_at'];
$age = time() - $createdAt;
// ํ๋ฅ ์ ์กฐ๊ธฐ ๋ง๋ฃ ๊ณ์ฐ
// TTL์ 80%๊ฐ ์ง๋๋ฉด ๊ฐฑ์ ํ๋ฅ ์ด ์ฆ๊ฐ
if ($age > $ttl * 0.8) {
$probability = ($age - $ttl * 0.8) / ($ttl * 0.2);
if (mt_rand() / mt_getrandmax() < $probability) {
// ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๊ฐฑ์ (๋น๋๊ธฐ)
$this->refreshInBackground($key, $ttl, $callback);
}
}
return $value;
}
// ์บ์ ๋ฏธ์ค - ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
$value = $callback();
$data = serialize([
'value' => $value,
'created_at' => time()
]);
$this->cache->set($key . ':data', $data, $ttl);
return $value;
}
/**
* ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ฐฑ์ (๊ฐ๋จํ ๊ตฌํ)
*/
private function refreshInBackground($key, $ttl, callable $callback) {
// ์ค์ ๋ก๋ ํ ์์คํ
์ด๋ ๋น๋๊ธฐ ์์
์๋ฅผ ์ฌ์ฉํด์ผ ํจ
// ์ฌ๊ธฐ์๋ ๊ฐ๋จํ ์ฆ์ ๊ฐฑ์
$value = $callback();
$data = serialize([
'value' => $value,
'created_at' => time()
]);
$this->cache->set($key . ':data', $data, $ttl);
}
}
// ์ฌ์ฉ ์์
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cache = new StampedeProofCache($redis);
// ์์ ํ๊ฒ ์บ์ ์ฌ์ฉ
$products = $cache->remember('popular_products', 600, function() {
// ๋ฌด๊ฑฐ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ
return fetchPopularProductsFromDatabase();
});
?>
4๏ธโฃ ์บ์ ๋ฌดํจํ ์ ๋ต
์บ์๋ฅผ ์ ์ฅํ๋ ๊ฒ๋งํผ ์ค์ํ ๊ฒ ์ ์ ํ ์์ ์ ์บ์๋ฅผ ๋ฌดํจํํ๋ ๊ฑฐ์ผ. ์ค๋๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ๋๋น ์ง๊ฑฐ๋ ! ๐
์ฃผ์ ๋ฌดํจํ ์ ๋ต:
โข TTL ๊ธฐ๋ฐ: ์๊ฐ์ด ์ง๋๋ฉด ์๋์ผ๋ก ๋ง๋ฃ
โข ์ด๋ฒคํธ ๊ธฐ๋ฐ: ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์ฆ์ ์บ์ ์ญ์
โข ํ๊ทธ ๊ธฐ๋ฐ: ๊ด๋ จ๋ ์บ์๋ค์ ๊ทธ๋ฃน์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ผ๊ด ์ญ์
โข ๋ฒ์ ๊ธฐ๋ฐ: ์บ์ ํค์ ๋ฒ์ ์ ํฌํจ์์ผ ์คํค๋ง ๋ณ๊ฒฝ ์ ์๋ ๋ฌดํจํ
<?php
class CacheInvalidator {
private $cache;
public function __construct($cache) {
$this->cache = $cache;
}
/**
* ์ฌ์ฉ์ ์ ๋ณด ์
๋ฐ์ดํธ ์ ๊ด๋ จ ์บ์ ๋ฌดํจํ
*/
public function onUserUpdated($userId) {
$patterns = [
"user:{$userId}",
"user:{$userId}:profile",
"user:{$userId}:posts",
"user:{$userId}:comments",
"user:{$userId}:followers",
"user:{$userId}:following"
];
foreach ($patterns as $key) {
$this->cache->del($key);
}
// ์ฌ์ฉ์ ๋ชฉ๋ก ์บ์๋ ๋ฌดํจํ
$this->cache->del('users:list');
$this->cache->del('users:active');
}
/**
* ์ํ ์ ๋ณด ์
๋ฐ์ดํธ ์ ๊ด๋ จ ์บ์ ๋ฌดํจํ
*/
public function onProductUpdated($productId) {
$product = $this->getProduct($productId);
$patterns = [
"product:{$productId}",
"product:{$productId}:details",
"category:{$product['category_id']}:products",
"products:featured",
"products:new"
];
foreach ($patterns as $key) {
$this->cache->del($key);
}
}
/**
* ์นดํ
๊ณ ๋ฆฌ ๋ณ๊ฒฝ ์ ๊ด๋ จ ์บ์ ๋ฌดํจํ
*/
public function onCategoryUpdated($categoryId) {
// ํด๋น ์นดํ
๊ณ ๋ฆฌ์ ๋ชจ๋ ์ํ ์บ์ ๋ฌดํจํ
$keys = $this->cache->keys("category:{$categoryId}:*");
if (!empty($keys)) {
$this->cache->del(...$keys);
}
// ์นดํ
๊ณ ๋ฆฌ ๋ชฉ๋ก ์บ์ ๋ฌดํจํ
$this->cache->del('categories:all');
$this->cache->del('categories:tree');
}
/**
* ์์ผ๋์นด๋ ํจํด์ผ๋ก ์บ์ ์ญ์
*/
public function deleteByPattern($pattern) {
$keys = $this->cache->keys($pattern);
if (!empty($keys)) {
return $this->cache->del(...$keys);
}
return 0;
}
private function getProduct($productId) {
// ์ค์ ๋ก๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ธ์ด
return ['category_id' => 10];
}
}
// ์ฌ์ฉ ์์ - ๋ชจ๋ธ์ ํตํฉ
class UserModel {
private $db;
private $cache;
private $invalidator;
public function __construct($db, $cache) {
$this->db = $db;
$this->cache = $cache;
$this->invalidator = new CacheInvalidator($cache);
}
public function updateUser($userId, array $data) {
// ๋ฐ์ดํฐ๋ฒ ์ด์ค ์
๋ฐ์ดํธ
$stmt = $this->db->prepare(
"UPDATE users SET name = ?, email = ? WHERE id = ?"
);
$stmt->execute([$data['name'], $data['email'], $userId]);
// ์บ์ ๋ฌดํจํ
$this->invalidator->onUserUpdated($userId);
return true;
}
}
?>
๐ก ํ๋ก ํ: ์บ์ ๋ฌดํจํ๋ ๋ณด์์ ์ผ๋ก!
ํ์คํ์ง ์์ผ๋ฉด ๊ด๋ จ๋ ์บ์๋ฅผ ๋ชจ๋ ์ญ์ ํ๋ ๊ฒ ์์ ํด. ์๋ชป๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ฒ๋ณด๋ค ์บ์ ๋ฏธ์ค๊ฐ ๋์ ๊ฒฝ์ฐ๊ฐ ๋ง๊ฑฐ๋ . ํ์ง๋ง ๋๋ฌด ๋ง์ ์บ์๋ฅผ ์ญ์ ํ๋ฉด ์ฑ๋ฅ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ผ๋ ๊ท ํ์ ์ ๋ง์ถฐ์ผ ํด!
๐ ์บ์ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง๊ณผ ์ต์ ํ
์บ์๋ฅผ ๊ตฌ์ถํ๋ค๋ฉด ์ด์ ์ ๋๋ก ์๋ํ๋์ง ๋ชจ๋ํฐ๋งํ๋ ๊ฒ ์ค์ํด! ์บ์ ํํธ์จ, ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋, ์๋ต ์๊ฐ ๋ฑ์ ์ถ์ ํด์ผ ํด. ๐
๐ฏ ์ฃผ์ ๋ชจ๋ํฐ๋ง ์งํ
1. ์บ์ ํํธ์จ (Cache Hit Ratio)
์ ์ฒด ์์ฒญ ์ค ์บ์์์ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ๋น์จ. ์ผ๋ฐ์ ์ผ๋ก 80% ์ด์์ด๋ฉด ์ข์!
๊ณ์ฐ ๊ณต์: (์บ์ ํํธ ์ / ์ ์ฒด ์์ฒญ ์) ร 100
2. ํ๊ท ์๋ต ์๊ฐ
์บ์ ํํธ ์์ ์บ์ ๋ฏธ์ค ์์ ์๋ต ์๊ฐ ์ฐจ์ด๋ฅผ ์ธก์ ํด.
3. ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ
์บ์ ์๋ฒ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ๋ชจ๋ํฐ๋งํด์ ์ ์ ํ ์ฉ๋์ ์ ์งํด.
4. ๋ง๋ฃ/์ญ์ ๋น์จ
TTL๋ก ๋ง๋ฃ๋๋ ํค์ ๋ช
์์ ์ผ๋ก ์ญ์ ๋๋ ํค์ ๋น์จ์ ํ์ธํด.
<?php
class CacheStatistics {
private $cache;
private $statsKey = 'cache:stats';
public function __construct($cache) {
$this->cache = $cache;
}
/**
* ์บ์ ํํธ ๊ธฐ๋ก
*/
public function recordHit($key) {
$this->cache->hIncrBy($this->statsKey, 'hits', 1);
$this->cache->hIncrBy($this->statsKey, 'total', 1);
// ์ต๊ทผ ํํธ ํค ๊ธฐ๋ก
$this->cache->lPush('cache:recent_hits', $key);
$this->cache->lTrim('cache:recent_hits', 0, 99); // ์ต๊ทผ 100๊ฐ๋ง ์ ์ง
}
/**
* ์บ์ ๋ฏธ์ค ๊ธฐ๋ก
*/
public function recordMiss($key) {
$this->cache->hIncrBy($this->statsKey, 'misses', 1);
$this->cache->hIncrBy($this->statsKey, 'total', 1);
// ์ต๊ทผ ๋ฏธ์ค ํค ๊ธฐ๋ก
$this->cache->lPush('cache:recent_misses', $key);
$this->cache->lTrim('cache:recent_misses', 0, 99);
}
/**
* ์๋ต ์๊ฐ ๊ธฐ๋ก
*/
public function recordResponseTime($key, $milliseconds, $isHit) {
$type = $isHit ? 'hit' : 'miss';
$this->cache->lPush("cache:response_times:{$type}", $milliseconds);
$this->cache->lTrim("cache:response_times:{$type}", 0, 999); // ์ต๊ทผ 1000๊ฐ
}
/**
* ํต๊ณ ๊ฐ์ ธ์ค๊ธฐ
*/
public function getStats() {
$stats = $this->cache->hGetAll($this->statsKey);
$hits = (int)($stats['hits'] ?? 0);
$misses = (int)($stats['misses'] ?? 0);
$total = (int)($stats['total'] ?? 0);
$hitRatio = $total > 0 ? ($hits / $total) * 100 : 0;
// ํ๊ท ์๋ต ์๊ฐ ๊ณ์ฐ
$hitTimes = $this->cache->lRange('cache:response_times:hit', 0, -1);
$missTimes = $this->cache->lRange('cache:response_times:miss', 0, -1);
$avgHitTime = !empty($hitTimes) ? array_sum($hitTimes) / count($hitTimes) : 0;
$avgMissTime = !empty($missTimes) ? array_sum($missTimes) / count($missTimes) : 0;
return [
'hits' => $hits,
'misses' => $misses,
'total' => $total,
'hit_ratio' => round($hitRatio, 2),
'avg_hit_time_ms' => round($avgHitTime, 2),
'avg_miss_time_ms' => round($avgMissTime, 2),
'performance_gain' => $avgMissTime > 0 ?
round(($avgMissTime - $avgHitTime) / $avgMissTime * 100, 2) : 0
];
}
/**
* ํต๊ณ ์ด๊ธฐํ
*/
public function reset() {
$this->cache->del($this->statsKey);
$this->cache->del('cache:recent_hits');
$this->cache->del('cache:recent_misses');
$this->cache->del('cache:response_times:hit');
$this->cache->del('cache:response_times:miss');
}
/**
* ๊ฐ์ฅ ๋ง์ด ๋ฏธ์ค๋ ํค ๋ถ์
*/
public function getTopMissedKeys($limit = 10) {
$recentMisses = $this->cache->lRange('cache:recent_misses', 0, -1);
$counts = array_count_values($recentMisses);
arsort($counts);
return array_slice($counts, 0, $limit, true);
}
}
// ์บ์ ๋ํผ ํด๋์ค (ํต๊ณ ์์ง ํฌํจ)
class MonitoredCache {
private $cache;
private $stats;
public function __construct($cache) {
$this->cache = $cache;
$this->stats = new CacheStatistics($cache);
}
public function get($key) {
$startTime = microtime(true);
$value = $this->cache->get($key);
$endTime = microtime(true);
$responseTime = ($endTime - $startTime) * 1000; // ๋ฐ๋ฆฌ์ด
if ($value !== false && $value !== null) {
$this->stats->recordHit($key);
$this->stats->recordResponseTime($key, $responseTime, true);
} else {
$this->stats->recordMiss($key);
$this->stats->recordResponseTime($key, $responseTime, false);
}
return $value;
}
public function set($key, $value, $ttl = 3600) {
return $this->cache->setex($key, $ttl, $value);
}
public function getStatistics() {
return $this->stats->getStats();
}
}
// ์ฌ์ฉ ์์
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cache = new MonitoredCache($redis);
// ์ผ๋ฐ์ ์ธ ์ฌ์ฉ
$userData = $cache->get('user:1000');
// ํต๊ณ ํ์ธ
$stats = $cache->getStatistics();
echo "Cache Hit Ratio: {$stats['hit_ratio']}%\n";
echo "Average Hit Time: {$stats['avg_hit_time_ms']}ms\n";
echo "Average Miss Time: {$stats['avg_miss_time_ms']}ms\n";
echo "Performance Gain: {$stats['performance_gain']}%\n";
?>
๐ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๊ธฐ
์ด๋ก ๊ณผ ์์ ๋ฅผ ์ถฉ๋ถํ ๋ดค์ผ๋, ์ด์ ์ค์ ํ๋ก์ ํธ์ ์ด๋ป๊ฒ ์ ์ฉํ๋์ง ๊ตฌ์ฒด์ ์ธ ์๋๋ฆฌ์ค๋ฅผ ์ดํด๋ณด์! ๐ฏ
์๋๋ฆฌ์ค 1: ์ ์์๊ฑฐ๋ ์ฌ์ดํธ ๐
์๊ตฌ์ฌํญ:
โข ์์ฒ ๊ฐ์ ์ํ ์ ๋ณด
โข ์ค์๊ฐ ์ฌ๊ณ ๊ด๋ฆฌ
โข ๊ฐ์ธํ๋ ์ถ์ฒ
โข ๋์ ํธ๋ํฝ
์บ์ฑ ์ ๋ต:
1๏ธโฃ ์ํ ์ ๋ณด (Redis): ์์ฃผ ์กฐํ๋๋ ์ํ์ ๋ฉ๋ชจ๋ฆฌ ์บ์์ ์ ์ฅ (TTL: 1์๊ฐ)
2๏ธโฃ ์นดํ
๊ณ ๋ฆฌ ํธ๋ฆฌ (ํ์ผ ์บ์): ๊ฑฐ์ ๋ณํ์ง ์์ผ๋ฏ๋ก ํ์ผ๋ก ์ ์ฅ (TTL: 24์๊ฐ)
3๏ธโฃ ์ฌ๊ณ ์ ๋ณด (Redis): ์ค์๊ฐ ์
๋ฐ์ดํธ๊ฐ ํ์ํ๋ฏ๋ก ์งง์ TTL (TTL: 1๋ถ)
4๏ธโฃ ์ฌ์ฉ์ ์ฅ๋ฐ๊ตฌ๋ (Redis): ์ธ์
๊ธฐ๋ฐ์ผ๋ก ๋น ๋ฅธ ์ ๊ทผ ํ์ (TTL: ์ธ์
์ ์ง ์๊ฐ)
5๏ธโฃ ๊ฒ์ ๊ฒฐ๊ณผ (Redis): ๋์ผํ ๊ฒ์์ด๋ ์บ์ฑ (TTL: 10๋ถ)
<?php
class EcommerceCache {
private $redis;
private $fileCache;
public function __construct($redis, $fileCache) {
$this->redis = $redis;
$this->fileCache = $fileCache;
}
/**
* ์ํ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
*/
public function getProduct($productId) {
$key = "product:{$productId}";
// Redis์์ ํ์ธ
$product = $this->redis->get($key);
if ($product !== false) {
return json_decode($product, true);
}
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ธ์ค๊ธฐ
$product = $this->fetchProductFromDB($productId);
// Redis์ ์ ์ฅ (1์๊ฐ)
$this->redis->setex($key, 3600, json_encode($product));
return $product;
}
/**
* ์นดํ
๊ณ ๋ฆฌ ํธ๋ฆฌ ๊ฐ์ ธ์ค๊ธฐ
*/
public function getCategoryTree() {
$key = 'categories:tree';
// ํ์ผ ์บ์์์ ํ์ธ
$tree = $this->fileCache->get($key);
if ($tree !== null) {
return $tree;
}
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ธ์ค๊ธฐ
$tree = $this->buildCategoryTree();
// ํ์ผ ์บ์์ ์ ์ฅ (24์๊ฐ)
$this->fileCache->set($key, $tree, 86400);
return $tree;
}
/**
* ์ฌ๊ณ ํ์ธ
*/
public function getStock($productId) {
$key = "stock:{$productId}";
// Redis์์ ํ์ธ (์งง์ TTL)
$stock = $this->redis->get($key);
if ($stock !== false) {
return (int)$stock;
}
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ธ์ค๊ธฐ
$stock = $this->fetchStockFromDB($productId);
// Redis์ ์ ์ฅ (1๋ถ)
$this->redis->setex($key, 60, $stock);
return $stock;
}
/**
* ์ฌ๊ณ ๊ฐ์ (์์์ ์ฐ์ฐ)
*/
public function decrementStock($productId, $quantity = 1) {
$key = "stock:{$productId}";
// Redis์์ ์์์ ์ผ๋ก ๊ฐ์
$newStock = $this->redis->decrBy($key, $quantity);
// ์์๊ฐ ๋๋ฉด ๋กค๋ฐฑ
if ($newStock < 0) {
$this->redis->incrBy($key, $quantity);
return false;
}
// ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์
๋ฐ์ดํธ (๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ ์ข์)
$this->updateStockInDB($productId, $newStock);
return true;
}
/**
* ์ฅ๋ฐ๊ตฌ๋ ๊ฐ์ ธ์ค๊ธฐ
*/
public function getCart($userId) {
$key = "cart:{$userId}";
$cart = $this->redis->get($key);
if ($cart !== false) {
return json_decode($cart, true);
}
return [];
}
/**
* ์ฅ๋ฐ๊ตฌ๋์ ์ํ ์ถ๊ฐ
*/
public function addToCart($userId, $productId, $quantity) {
$key = "cart:{$userId}";
$cart = $this->getCart($userId);
if (isset($cart[$productId])) {
$cart[$productId]['quantity'] += $quantity;
} else {
$cart[$productId] = [
'product_id' => $productId,
'quantity' => $quantity,
'added_at' => time()
];
}
// Redis์ ์ ์ฅ (2์๊ฐ)
$this->redis->setex($key, 7200, json_encode($cart));
return $cart;
}
/**
* ๊ฒ์ ๊ฒฐ๊ณผ ์บ์ฑ
*/
public function searchProducts($query, $filters = []) {
$cacheKey = 'search:' . md5($query . serialize($filters));
// Redis์์ ํ์ธ
$results = $this->redis->get($cacheKey);
if ($results !== false) {
return json_decode($results, true);
}
// ๊ฒ์ ์ํ
$results = $this->performSearch($query, $filters);
// Redis์ ์ ์ฅ (10๋ถ)
$this->redis->setex($cacheKey, 600, json_encode($results));
return $results;
}
// ํฌํผ ๋ฉ์๋๋ค (์ค์ ๋ก๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ)
private function fetchProductFromDB($productId) {
// ์ค์ ๊ตฌํ...
return ['id' => $productId, 'name' => 'Sample Product'];
}
private function buildCategoryTree() {
// ์ค์ ๊ตฌํ...
return [];
}
private function fetchStockFromDB($productId) {
// ์ค์ ๊ตฌํ...
return 100;
}
private function updateStockInDB($productId, $stock) {
// ์ค์ ๊ตฌํ...
}
private function performSearch($query, $filters) {
// ์ค์ ๊ตฌํ...
return [];
}
}
?>
์๋๋ฆฌ์ค 2: ์์ ๋ฏธ๋์ด ํ๋ซํผ ๐ฑ
์๊ตฌ์ฌํญ:
โข ์ค์๊ฐ ํผ๋
โข ์ข์์/๋๊ธ ์นด์ดํฐ
โข ์ฌ์ฉ์ ํ๋กํ
โข ์๋ฆผ ์์คํ
์บ์ฑ ์ ๋ต:
1๏ธโฃ ์ฌ์ฉ์ ํผ๋ (Redis List): ์ต๊ทผ ๊ฒ์๋ฌผ ID ๋ชฉ๋ก ์ ์ฅ (TTL: 5๋ถ)
2๏ธโฃ ์ข์์ ์นด์ดํฐ (Redis): ์์์ ์ฆ๊ฐ/๊ฐ์ ์ฐ์ฐ ํ์ฉ
3๏ธโฃ ์ฌ์ฉ์ ํ๋กํ (Redis Hash): ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ ์ ์ฅ (TTL: 30๋ถ)
4๏ธโฃ ์๋ฆผ (Redis Sorted Set): ํ์์คํฌํ ๊ธฐ๋ฐ ์ ๋ ฌ
5๏ธโฃ ์ธ๊ธฐ ๊ฒ์๋ฌผ (Redis Sorted Set): ์ ์ ๊ธฐ๋ฐ ๋ญํน
<?php
class SocialMediaCache {
private $redis;
public function __construct($redis) {
$this->redis = $redis;
}
/**
* ์ฌ์ฉ์ ํผ๋ ๊ฐ์ ธ์ค๊ธฐ
*/
public function getUserFeed($userId, $limit = 20) {
$key = "feed:{$userId}";
// Redis List์์ ์ต๊ทผ ๊ฒ์๋ฌผ ID ๊ฐ์ ธ์ค๊ธฐ
$postIds = $this->redis->lRange($key, 0, $limit - 1);
if (empty($postIds)) {
// ํผ๋ ์์ฑ
$postIds = $this->generateFeed($userId);
// Redis์ ์ ์ฅ
foreach ($postIds as $postId) {
$this->redis->rPush($key, $postId);
}
$this->redis->expire($key, 300); // 5๋ถ
}
// ๊ฒ์๋ฌผ ์์ธ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
$posts = [];
foreach ($postIds as $postId) {
$posts[] = $this->getPost($postId);
}
return $posts;
}
/**
* ์ ๊ฒ์๋ฌผ์ ํ๋ก์๋ค์ ํผ๋์ ์ถ๊ฐ
*/
public function fanoutPost($authorId, $postId) {
// ํ๋ก์ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ
$followers = $this->getFollowers($authorId);
// ๊ฐ ํ๋ก์์ ํผ๋์ ์ถ๊ฐ
foreach ($followers as $followerId) {
$key = "feed:{$followerId}";
$this->redis->lPush($key, $postId);
$this->redis->lTrim($key, 0, 99); // ์ต๊ทผ 100๊ฐ๋ง ์ ์ง
$this->redis->expire($key, 300);
}
}
/**
* ์ข์์ ์ถ๊ฐ
*/
public function likePost($postId, $userId) {
$likesKey = "post:{$postId}:likes";
$userLikesKey = "post:{$postId}:liked_users";
// ์ด๋ฏธ ์ข์์๋ฅผ ๋๋ ๋์ง ํ์ธ
if ($this->redis->sIsMember($userLikesKey, $userId)) {
return false;
}
// ์ข์์ ์นด์ดํฐ ์ฆ๊ฐ
$this->redis->incr($likesKey);
// ์ข์์ ๋๋ฅธ ์ฌ์ฉ์ ์ถ๊ฐ
$this->redis->sAdd($userLikesKey, $userId);
// ์ธ๊ธฐ ๊ฒ์๋ฌผ ์ ์ ์
๋ฐ์ดํธ
$this->redis->zIncrBy('trending_posts', 1, $postId);
return true;
}
/**
* ์ข์์ ์ทจ์
*/
public function unlikePost($postId, $userId) {
$likesKey = "post:{$postId}:likes";
$userLikesKey = "post:{$postId}:liked_users";
// ์ข์์๋ฅผ ๋๋ฅด์ง ์์์ผ๋ฉด ๋ฌด์
if (!$this->redis->sIsMember($userLikesKey, $userId)) {
return false;
}
// ์ข์์ ์นด์ดํฐ ๊ฐ์
$this->redis->decr($likesKey);
// ์ข์์ ๋๋ฅธ ์ฌ์ฉ์ ์ ๊ฑฐ
$this->redis->sRem($userLikesKey, $userId);
// ์ธ๊ธฐ ๊ฒ์๋ฌผ ์ ์ ์
๋ฐ์ดํธ
$this->redis->zIncrBy('trending_posts', -1, $postId);
return true;
}
/**
* ๊ฒ์๋ฌผ ์ข์์ ์ ๊ฐ์ ธ์ค๊ธฐ
*/
public function getLikesCount($postId) {
$key = "post:{$postId}:likes";
$count = $this->redis->get($key);
return $count !== false ? (int)$count : 0;
}
/**
* ์ธ๊ธฐ ๊ฒ์๋ฌผ ๊ฐ์ ธ์ค๊ธฐ
*/
public function getTrendingPosts($limit = 10) {
// Sorted Set์์ ์ ์๊ฐ ๋์ ์์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ
$postIds = $this->redis->zRevRange('trending_posts', 0, $limit - 1);
$posts = [];
foreach ($postIds as $postId) {
$post = $this->getPost($postId);
$post['score'] = $this->redis->zScore('trending_posts', $postId);
$posts[] = $post;
}
return $posts;
}
/**
* ์๋ฆผ ์ถ๊ฐ
*/
public function addNotification($userId, $notification) {
$key = "notifications:{$userId}";
$timestamp = microtime(true);
// Sorted Set์ ์ถ๊ฐ (ํ์์คํฌํ๋ฅผ ์ ์๋ก ์ฌ์ฉ)
$this->redis->zAdd($key, $timestamp, json_encode($notification));
// ์ต๊ทผ 100๊ฐ๋ง ์ ์ง
$this->redis->zRemRangeByRank($key, 0, -101);
// ์ฝ์ง ์์ ์๋ฆผ ์นด์ดํฐ ์ฆ๊ฐ
$this->redis->incr("notifications:{$userId}:unread");
}
/**
* ์๋ฆผ ๊ฐ์ ธ์ค๊ธฐ
*/
public function getNotifications($userId, $limit = 20) {
$key = "notifications:{$userId}";
// ์ต๊ทผ ์๋ฆผ๋ถํฐ ๊ฐ์ ธ์ค๊ธฐ
$notifications = $this->redis->zRevRange($key, 0, $limit - 1);
return array_map(function($item) {
return json_decode($item, true);
}, $notifications);
}
/**
* ์๋ฆผ ์ฝ์ ์ฒ๋ฆฌ
*/
public function markNotificationsAsRead($userId) {
$this->redis->set("notifications:{$userId}:unread", 0);
}
/**
* ์ฌ์ฉ์ ํ๋กํ ๊ฐ์ ธ์ค๊ธฐ
*/
public function getUserProfile($userId) {
$key = "user:{$userId}:profile";
// Redis Hash์์ ๊ฐ์ ธ์ค๊ธฐ
$profile = $this->redis->hGetAll($key);
if (empty($profile)) {
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ธ์ค๊ธฐ
$profile = $this->fetchUserProfileFromDB($userId);
// Redis์ ์ ์ฅ (30๋ถ)
$this->redis->hMSet($key, $profile);
$this->redis->expire($key, 1800);
}
return $profile;
}
// ํฌํผ ๋ฉ์๋๋ค
private function generateFeed($userId) {
// ์ค์ ๊ตฌํ...
return [];
}
private function getFollowers($userId) {
// ์ค์ ๊ตฌํ...
return [];
}
private function getPost($postId) {
// ์ค์ ๊ตฌํ...
return ['id' => $postId];
}
private function fetchUserProfileFromDB($userId) {
// ์ค์ ๊ตฌํ...
return [];
}
}
?>
๐ ์ฌ๋ฅ๋ท์์ PHP ๊ฐ๋ฐ ์ค๋ ฅ ํค์ฐ๊ธฐ
์ฌ๊ธฐ๊น์ง PHP ์บ์ฑ ์ ๋ต์ ๋ํด ๊น์ด ์๊ฒ ์์๋ดค์ด! ๋ฉ๋ชจ๋ฆฌ ์บ์์ ํ์ผ ์บ์์ ์ฐจ์ด์ , ๊ฐ๊ฐ์ ์ฅ๋จ์ , ๊ทธ๋ฆฌ๊ณ ์ค์ ์์ ์ด๋ป๊ฒ ํ์ฉํ๋์ง๊น์ง ๋ฐฐ์ ์ง. ๐
ํ์ง๋ง ์ด๋ก ๋ง์ผ๋ก๋ ๋ถ์กฑํด! ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๋ฉด์ ๊ฒฝํ์ ์๋ ๊ฒ ๊ฐ์ฅ ์ค์ํ๊ฑฐ๋ . ํน์ PHP ๊ฐ๋ฐ์ด๋ ์บ์ฑ ์ ๋ต์ ๋ํด ๋ ๊น์ด ๋ฐฐ์ฐ๊ณ ์ถ๋ค๋ฉด, ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ๋ค์ ๋์์ ๋ฐ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ๐ก
์ฌ๋ฅ๋ท์๋ PHP ๊ฐ๋ฐ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ต์ ํ, ์๋ฒ ์ํคํ ์ฒ ์ค๊ณ ๋ฑ ๋ค์ํ ๋ถ์ผ์ ์ ๋ฌธ๊ฐ๋ค์ด ์์ด. 1:1 ๋ฉํ ๋ง์ ๋ฐ๊ฑฐ๋, ํ๋ก์ ํธ๋ฅผ ์๋ขฐํ ์๋ ์์ง. ๋ํ ๋์ ๊ฐ๋ฐ ์ค๋ ฅ์ ๋ค๋ฅธ ์ฌ๋๋ค๊ณผ ๊ณต์ ํ๋ฉด์ ์์ต๋ ์ฐฝ์ถํ ์ ์์ด!
๐ ๋ค์ ๋จ๊ณ๋ก ๋์๊ฐ๊ธฐ
์บ์ฑ์ ๋ง์คํฐํ๋ค๋ฉด ์ด์ ๋ค์ ์ฃผ์ ๋ค๋ ๊ณต๋ถํด๋ณด์:
โข ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ์ต์ ํ: ์ธ๋ฑ์ค, ์ฟผ๋ฆฌ ํ๋, EXPLAIN ๋ถ์
โข ๋ก๋ ๋ฐธ๋ฐ์ฑ: ์ฌ๋ฌ ์๋ฒ์ ํธ๋ํฝ ๋ถ์ฐ
โข CDN ํ์ฉ: ์ ์ ํ์ผ ๋ฐฐํฌ ์ต์ ํ
โข ๋น๋๊ธฐ ์ฒ๋ฆฌ: ํ ์์คํ
, ๋ฐฑ๊ทธ๋ผ์ด๋ ์์
โข ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ: ์๋น์ค ๋ถ๋ฆฌ์ ํ์ฅ์ฑ
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์ค๋ ์ฐ๋ฆฌ๋ PHP ์บ์ฑ ์ ๋ต์ ๋ํด ์ ๋ง ๋ง์ ๊ฒ์ ๋ฐฐ์ ์ด! ๋ฉ๋ชจ๋ฆฌ ์บ์(Redis, Memcached, APCu)์ ํ์ผ ์บ์์ ํน์ง, ์ฅ๋จ์ , ๊ทธ๋ฆฌ๊ณ ์ค์ ํ์ฉ ๋ฐฉ๋ฒ๊น์ง ์ดํด๋ดค์ง. ๐ฏ
ํต์ฌ ์์ฝ:
โ
๋ฉ๋ชจ๋ฆฌ ์บ์๋ ์ด๊ณ ์์ด์ง๋ง ํ๋ฐ์ฑ์ด๊ณ ์ธํ๋ผ ๋น์ฉ์ด ๋ ๋ค
โ
ํ์ผ ์บ์๋ ์์์ ์ด๊ณ ๊ฐํธํ์ง๋ง ์๋์ ์ผ๋ก ๋๋ฆฌ๋ค
โ
ํ์ด๋ธ๋ฆฌ๋ ์ ๋ต์ผ๋ก ๋ ๊ฐ์ง๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ต๊ณ ์ ํจ๊ณผ
โ
์บ์ ๋ฌดํจํ์ ๋ชจ๋ํฐ๋ง์ ํ์!
โ
์ค์ ํ๋ก์ ํธ์ ๋ง๋ ์ ๋ต์ ์ ํํ๋ ๊ฒ ์ค์
์บ์ฑ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ ์ต์ ํ์ ํต์ฌ์ด์ผ. ์ ๋๋ก ๊ตฌํํ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ๊ทน์ ์ผ๋ก ๊ฐ์ ๋๊ณ , ์๋ฒ ๋น์ฉ๋ ์ ๊ฐํ ์ ์์ด. ํ์ง๋ง ์๋ชป ์ฌ์ฉํ๋ฉด ์คํ๋ ค ๋ณต์ก๋๋ง ์ฆ๊ฐํ๊ณ ๋ฒ๊ทธ์ ์์ธ์ด ๋ ์ ์์ผ๋ ํญ์ ์ ์คํ๊ฒ ์ ๊ทผํด์ผ ํด! ๐จ
์ด ๊ธ์ด ๋์ PHP ๊ฐ๋ฐ ์ฌ์ ์ ๋์์ด ๋์๊ธฐ๋ฅผ ๋ฐ๋ผ! ๊ถ๊ธํ ์ ์ด ์๊ฑฐ๋ ๋ ๊น์ด ๋ฐฐ์ฐ๊ณ ์ถ๋ค๋ฉด ์ฌ๋ฅ๋ท์ ๋ฐฉ๋ฌธํด๋ด. ํจ๊ป ์ฑ์ฅํ๋ ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ๊ฐ ๊ธฐ๋ค๋ฆฌ๊ณ ์์ด! ๐ช
๊ทธ๋ผ ์ฆ๊ฑฐ์ด ์ฝ๋ฉ ๋๊ธธ ๋ฐ๋ผ! Happy Caching! ๐โจ
๊ด๋ จ ํค์๋
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.

๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.