Memcache
Summary
First, you start up the memcached daemon on as many spare machines as you have. The daemon has no configuration file, just a few command line options, only 3 or 4 of which you'll likely use:
./memcached -d -m 2048 -l 10.0.0.40 -p 11211
This starts memcached up as a daemon, using 2GB of memory, and listening on IP 10.0.0.40, port 11211. Because a 32-bit process can only address 4GB of virtual memory (usually significantly less, depending on your operating system), if you have a 32-bit server with 4-64GB of memory using PAE you can just run multiple processes on the machine, each using 2 or 3GB of memory.
What are some limits in memcached I might hit?: [1]
The simple limits you will probably see with memcache are the key and item size limits. Keys are restricted to 250 characters. Stored data cannot exceed 1 megabyte in size, since that is the largest typical slab size.
Installation
Yum Method
RPMForge Method: (version 1.4.5-1.el5.rf as of 2010.10.25)
- See RPMforge for RPMforge installation
- Yum method info provided here: http://serverfault.com/questions/342826/problems-installing-memcached
yum install memcached --enablerepo=rpmforge --enablerepo=rpmforge-extras # x64 only - to fix x64 issue with SSLeay: # wget http://pkgs.repoforge.org/perl-Net-SSLeay/perl-Net-SSLeay-1.36-1.el5.rfx.x86_64.rpm # rpm -Uvh perl-Net-SSLeay-1.36-1.el5.rfx.x86_64.rpm
# to fix: Starting memcached: chown: cannot access `/var/run/memcached': No such file or directory # touch /var/run/memcached
/etc/sysconfig/memcached (original):
PORT="11211" USER="nobody" MAXCONN="1024" CACHESIZE="64" OPTIONS=""
/etc/sysconfig/memcached (modified, with cachesize in megabytes):
PORT="11211" USER="nobody" MAXCONN="1024" # in megabytes: CACHESIZE="128" # additional options, specify address to listen on OPTIONS="-l 127.0.0.1"
Start service:
chkconfig memcached on service memcached start
- ?eh? yum install php-eaccelerator
Manual Method
Download Libevent:
mkdir -p ~/.src ; cd ~/.src wget http://www.monkey.org/~provos/libevent-1.3e.tar.gz tar -zvxf libevent-1.3e.tar.gz cd libevent-1.3e ./configure; make; make install;
#32bit: ln -s /usr/local/lib/libevent-1.3e.so.1 /lib/libevent-1.3e.so.1 #64bit: ln -s /usr/local/lib/libevent-1.3e.so.1 /lib64/libevent-1.3e.so.1
Download memcached:
mkdir -p ~/.src ; cd ~/.src wget http://www.danga.com/memcached/dist/memcached-1.2.2.tar.gz tar -zxvf memcached-1.2.2.tar.gz cd memcached-1.2.2 ./configure; make; make install;
Start memcached:
memcached -d -u nobody -m 512 127.0.0.1 -p 11211
---
Often libevent.so cannot be found when executing memcache. A useful command LD_DEBUG, is very helpful to determine where libraries are being loaded from.
# help LD_DEBUG=help memcached -v LD_DEBUG=libs memcached -v 2>&1 > /dev/null | less 18990: find library=libevent-1.3b.so.1 [0]; searching ... 18990: trying file=/usr/lib/libevent-1.3b.so.1 18990: memcached: error while loading shared libraries: libevent-1.3b.so.1: cannot open shared object file: No such file or directory
Simply place the library where memcached will find it and execute memcached:
ln -s /usr/local/lib/libevent-1.3e.so.1 /lib/libevent-1.3e.so.1 #64bit: ln -s /usr/local/lib/libevent-1.3e.so.1 /lib64/libevent-1.3e.so.1
Testing memcache
How to install memcache on Debian Etch
Quick stats check:
echo -e "stats\nquit" | nc 127.0.0.1 11211
Testing the memcached daemon with telnet:
telnet 1.2.3.4 11211
The commands look like this:
set <key> <flag> <exptime> <bytes>\r\n
Example:
set test2 1 0 2 ab STORED
get test2 VALUE test2 1 2 ab END
set test4 1 0 2 abcde CLIENT_ERROR bad data chunk ERROR
To see statistics:
stats
To exit:
quit
The flag is an arbitrary number that you can use in your client logic. It is intended to be metadata that you can assign to each cached object.
I set the exptime to zero (never expire)
the bytes to the number of characters I want to store. If the bytes and the number of characters don't match, an error occurs.
For other commands see protocol.txt
An example of how to get the stats using a bash script
Stats Check
Dump all stats:
echo -e "stats\nquit" | nc 127.0.0.1 11211
Get important items:
echo -e "stats\nquit" | nc 127.0.0.1 11211 | grep -e "\(uptime\|get_hits\|get_misses\|bytes \|limit_maxbytes\)" # uptime - Number of seconds this server has been running # get_hits - request that are in cache # get_misses - requests that weren't in cache # limit_maxbytes - total max bytes can store # bytes - current bytes in cache
Stats Protocol
Name Type Meaning ---------------------------------- pid 32u Process id of this server process uptime 32u Number of seconds this server has been running time 32u current UNIX time according to the server version string Version string of this server pointer_size 32 Default size of pointers on the host OS (generally 32 or 64) rusage_user 32u:32u Accumulated user time for this process (seconds:microseconds) rusage_system 32u:32u Accumulated system time for this process (seconds:microseconds) curr_items 32u Current number of items stored by the server total_items 32u Total number of items stored by this server ever since it started bytes 64u Current number of bytes used by this server to store items curr_connections 32u Number of open connections total_connections 32u Total number of connections opened since the server started running connection_structures 32u Number of connection structures allocated by the server cmd_get 64u Cumulative number of retrieval requests cmd_set 64u Cumulative number of storage requests get_hits 64u Number of keys that have been requested and found present get_misses 64u Number of items that have been requested and not found evictions 64u Number of valid items removed from cache to free memory for new items bytes_read 64u Total number of bytes read by this server from network bytes_written 64u Total number of bytes sent by this server to network limit_maxbytes 32u Number of bytes this server is allowed to use for storage. threads 32u Number of worker threads requested. (see doc/threads.txt)
Source: Protocol - http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
Slab Size
What are some limits in memcached I might hit?:
- "The simple limits you will probably see with memcache are the key and item size limits. Keys are restricted to 250 characters. Stored data cannot exceed 1 megabyte in size, since that is the largest typical slab size."
- "Actually, it's a compile time option. The default is to use the internal slab allocator. You really really want to use the built-in slab allocator. At first memcached did just use malloc/free for everything. However this does not play very well with OS memory managers. You get fragmentation, and your OS ends up spending more time trying to find contiguous blocks of memory to feed malloc() than it does running the memcached process. If you disagree, of course you're free to try malloc! just don't complain on the lists ;)
- The slab allocator was built to work around this. Memory is allocated in chunks internally and constantly reused. Since memory is broken into different size slabs, you do waste memory if your items do not fit perfectly into the slab the server chooses to put it in. This has enjoyed considerable efficiency improvements by Steven Grimm.
- Some older posts about the slab changes (power of n vs power of 2), and some tradeoffs are on the mailing list:
- http://lists.danga.com/pipermail/memcached/2006-May/002163.html
- http://lists.danga.com/pipermail/memcached/2007-March/003753.html
- And if you'd like to attempt to use malloc/free and see how it works, you may define 'USE_SYSTEM_MALLOC' in build process. It might not be tested very well, so getting developer support for it is unlikely."
References:
- "The maximum object size is a few bytes under 1MB. You're trying to set about 250k more than it can take."
- "You can recompile memcached with a higher POWER_LARGEST if you need more than 1MB. It's in slabs.c at the top. Make it 21 to get 2MB max, or 22 for 4MB max, etc."
- "The maximum size is 1 megabyte. It can be increased; I believe there was a patch (against the old 1.1.12 version) to do it. The trick is that memcached's memory manager allocates memory in 1MB chunks, so increasing the maximum is not just a matter of changing a "maximum object size" setting somewhere. But it is certainly doable."
- "I propose all objects are stored as a linked list of 64 byte chunks."
- "We've talked internally about eventually moving away from the slab-class-based system. It should be possible to run a job in the background (especially in the multithreaded version, but even in the single-threaded one) to shuffle objects around in memory to pack them optimally. If you separate the fixed-size headers from the item data, you can even do away with byte-alignment-based waste. That would give you 100% memory efficiency for any arbitrary mix of item sizes, and true LRU behavior across the whole cache, at the cost of greater CPU consumption."
- Patch - Configurable slab size and large object support
- memcached overrides memory limit
- Patch - Increased memory efficiency
Startup Scripts
touch /etc/memcached.conf cat > /etc/memcached.conf
#Memory a usar -m 16 # default port -p 11211 # user to run daemon nobody/apache/www-data -u nobody # only listen locally -l 127.0.0.1
touch /etc/init.d/memcached chmod +x /etc/init.d/memcached cat > /etc/init.d/memcached
#!/bin/bash # # memcached This shell script takes care of starting and stopping # standalone memcached. # # chkconfig: - 80 12 # description: memcached is a high-performance, distributed memory # object caching system, generic in nature, but # intended for use in speeding up dynamic web # applications by alleviating database load. # processname: memcached # config: /etc/memcached.conf # Source function library. . /etc/rc.d/init.d/functions PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/local/bin/memcached DAEMONBOOTSTRAP=/usr/local/bin/start-memcached DAEMONCONF=/etc/memcached.conf NAME=memcached DESC=memcached PIDFILE=/var/run/$NAME.pid [ -x $DAEMON ] || exit 0 [ -x $DAEMONBOOTSTRAP ] || exit 0 RETVAL=0 start() { echo -n $"Starting $DESC: " daemon $DAEMONBOOTSTRAP $DAEMONCONF RETVAL=$? [ $RETVAL -eq 0 ] && touch $PIDFILE echo return $RETVAL } stop() { echo -n $"Shutting down $DESC: " killproc $NAME RETVAL=$? echo [ $RETVAL -eq 0 ] && rm -f $PIDFILE return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; restart|reload) stop start RETVAL=$? ;; status) status $prog RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|restart|status}" exit 1 esac exit $RETVAL
touch /usr/local/bin/start-memcached chmod +x /usr/local/bin/start-memcached cat > /usr/local/bin/start-memcached
#!/usr/bin/perl -w # start-memcached # 2003/2004 - Jay Bonci <jaybonci@debian.org> # This script handles the parsing of the /etc/memcached.conf file # and was originally created for the Debian distribution. # Anyone may use this little script under the same terms as # memcached itself. use strict; if ($> != 0 and $< != 0) { print STDERR "Only root wants to run start-memcached.\n"; exit; } my $etcfile = shift || "/etc/memcached.conf"; my $params = []; my $etchandle; # This script assumes that memcached is located at /usr/bin/memcached, and # that the pidfile is writable at /var/run/memcached.pid my $memcached = "/usr/local/bin/memcached"; my $pidfile = "/var/run/memcached.pid"; # If we don't get a valid logfile parameter in the /etc/memcached.conf file, # we'll just throw away all of our in-daemon output. We need to re-tie it so # that non-bash shells will not hang on logout. Thanks to Michael Renner for # the tip my $fd_reopened = "/dev/null"; sub handle_logfile { my ($logfile) = @_; $fd_reopened = $logfile; } sub reopen_logfile { my ($logfile) = @_; open *STDERR, ">>$logfile"; open *STDOUT, ">>$logfile"; open *STDIN, ">>/dev/null"; $fd_reopened = $logfile; } # This is set up in place here to support other non -[a-z] directives my $conf_directives = { "logfile" => \&handle_logfile }; if (open $etchandle, $etcfile) { foreach my $line (<$etchandle>) { $line =~ s/\#.*//go; $line = join ' ', split ' ', $line; next unless $line; next if $line =~ /^\-[dh]/o; if ($line =~ /^[^\-]/o) { my ($directive, $arg) = $line =~ /^(.*?)\s+(.*)/; $conf_directives->{$directive}->($arg); next; } push @$params, $line; } } unshift @$params, "-u root" unless (grep $_ eq '-u', @$params); $params = join " ", @$params; if (-e $pidfile) { open PIDHANDLE, "$pidfile"; my $localpid = <PIDHANDLE>; close PIDHANDLE; chomp $localpid; if (-d "/proc/$localpid") { print STDERR "memcached is already running.\n"; exit; } else { `rm -f $localpid`; } } my $pid = fork(); if ($pid == 0) { reopen_logfile($fd_reopened); exec "$memcached $params"; exit(0); } elsif (open PIDHANDLE,">$pidfile") { print PIDHANDLE $pid; close PIDHANDLE; } else { print STDERR "Can't write pidfile to $pidfile.\n"; }
service memcached restart chkconfig memcached on
An alternative startup script:
Bash Stat Command
#!/bin/bash # # Make a 'stat' command on a memcache daemon server # Outputs to a pipe for later using with syslog-ng # # alexandre.abreu at gmail.com PATH=/bin:/usr/bin:/sbin:/usr/sbin SERVER=localhost PORT=11111 FIFO=/export/logs/memcache.fifo BUFF="memcache: " [ -p $FIFO ] || mkfifo $FIFO || exit 1 exec 666<>/dev/tcp/$SERVER/$PORT || exit 1 append() { BUFF="$BUFF $@" [ "$1" == "uptime" ] && RUNTIME=`echo "scale=3; $2/60" | bc` [ "$1" == "get_hits" ] && { rate=`echo "scale=3; $2/$RUNTIME" | bc` rate=`printf "%.3f" $rate` append hitrate $rate } [ "$1" == "get_misses" ] && { rate=`echo "scale=3; $2/$RUNTIME" | bc` rate=`printf "%.3f" $rate` append missrate $rate } } echo "stats" >&666 while read line; do line=`echo $line | sed 's|[[:cntrl:]]||g'` set - $line append $2 $3 [ "$2" == "limit_maxbytes" ] && break done <&666 echo "$BUFF" > $FIFO echo "quit" >&666 exit 0
My Version:
#!/bin/bash # # Make a 'stat' command on a memcache daemon server # Outputs to a pipe for later using with syslog-ng # PATH=/bin:/usr/bin:/sbin:/usr/sbin SERVER=10.20.30.40 PORT=11211 FIFO=/tmp/memcache.fifo [ -p $FIFO ] || mkfifo $FIFO || exit 1 exec 666<>/dev/tcp/$SERVER/$PORT || exit 1 echo "stats" >&666 while read line; do echo $line; FLAG=`echo $line | cut -b 1-3` [ "$FLAG" == "END" ] && break done <&666 echo "quit" >&666 exit 0