JavaからMemcachedを読み書きする。キーと値の一覧を取得する。

検索したら、Memcachedに、telnetで接続して、コマンドラインで、キー一覧取得というのをやっているのを見かけた。ほかにも、PHPRubyPerlで同じようなことをやっているのも見かけた。

自分もまねして、JavaのクライアントからMemcachedに登録したデータのキーと値の一覧の取得をやってみる。

ちなみに、本家のWebサイトを見ると、すべてのキー取得はできないとある
Google Code Archive - Long-term storage for Google Code Project Hosting.

With memcached, you can't list all keys

ここで、やる方法も問題あるのかな。。。。まあ、いいや。練習と開発用の小さいツールがほしいだけだから。

環境は構築済み
memcachedをUbuntu上で動かす - kaishitaeiichiの日記
Javamemcachedクライアントのライブラリもここでためしてある。

Eclipseに小さなJavaプロジェクトを作成

F:\Data\Eclipse\workspaces\Java\032-01 Memcached-Java-Client>tree /F
フォルダ パスの一覧
ボリューム シリアル番号は 00680063 781F:C3ED です
F:.
│  .classpath
│  .project
│
├─.settings
│      org.eclipse.jdt.core.prefs
│
├─classes
│      log4j.properties
│      MyClass.class
│      MyMemcachedClient.class
│      TestBean.class
│
├─lib
|      commons-lang3-3.0.1.jar
│      commons-pool-1.5.6.jar
│      java_memcached-release_2.6.3.jar
│      slf4j-api-1.6.1.jar
│      slf4j-log4j12-1.6.1.jar
│      slf4j-simple-1.6.1.jar
│
└─src
        log4j.properties
        MyClass.java
        MyMemcachedClient.java
        TestBean.java

Memcached-Java-Client/HOWTO.txt at master · gwhalin/Memcached-Java-Client · GitHubこのソースコードをもとにした。

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;

public class MyMemcachedClient {

    private Logger logger = LoggerFactory.getLogger(MyMemcachedClient.class);

    private MemCachedClient mcc = new MemCachedClient();

    private Pattern pattern = Pattern.compile("items:(.+):number");

    public MyMemcachedClient() {

        String[] servers = { "192.168.2.50:11211" };

        Integer[] weights = { 1 };

        SockIOPool pool = SockIOPool.getInstance();

        pool.setServers(servers);
        pool.setWeights(weights);

        pool.setInitConn(5);
        pool.setMinConn(5);
        pool.setMaxConn(250);
        pool.setMaxIdle(1000 * 60 * 60 * 6);

        pool.setMaintSleep(30);

        pool.setNagle(false);
        pool.setSocketTO(3000);
        pool.setSocketConnectTO(0);

        pool.initialize();
    }

    public void setAndGetExampleData() {
        mcc.set("bar", "This is a test String");
        String bar = (String) mcc.get("bar");
        System.out.println("bar=" + bar);

        mcc.set("testBean", new TestBean());
        TestBean testBean = (TestBean) mcc.get("testBean");
        System.out.println("testBean=" + testBean.toString());

    }

    public void showKeyAndValue() {
        List<String> keyList = getKeyList();
        for (String key : keyList) {
            Object value = this.mcc.get(key);
            String valueAsString = null;
            if (StringUtils.contains(value.getClass().getSimpleName(), "Dto")
                    || StringUtils.contains(value.getClass().getSimpleName(),
                            "Bean")) {
                valueAsString = ToStringBuilder.reflectionToString(value);
            }
            else {
                valueAsString = ObjectUtils.toString(value);
            }
            String keyAndValue = MessageFormat.format("key={0} value={1}", key,
                    valueAsString);
            System.out.println(keyAndValue);
        }
    }

    public List<String> getKeyList() {

        List<String> itemsNumberList = new ArrayList<String>();
        Map<String, Map<String, String>> statsItems = this.mcc.statsItems();
        for (Map.Entry<String, Map<String, String>> statsItemsEntry : statsItems
                .entrySet()) {
            String key = statsItemsEntry.getKey();
            Map<String, String> statsItemsValue = statsItemsEntry.getValue();
            for (String itemsNumber : statsItemsValue.keySet()) {
                Matcher matcher = this.pattern.matcher(itemsNumber);
                if (matcher.find()) {
                    itemsNumberList.add(matcher.group(1));
                }
            }
        }

        List<String> keyList = new ArrayList<String>();
        for (String itemsNumber : itemsNumberList) {
            //とりあえず、1000にしたけど、必要に応じて増やす?
            //key-valueといわれても、Memcached内部のデータ構造がわかってない
            //どうなっているんだろう。
            Map<String, Map<String, String>> statsCacheDump = this.mcc
                    .statsCacheDump(Integer.valueOf(itemsNumber), 1000);
            for (Map.Entry<String, Map<String, String>> statsCacheDumpEntry : statsCacheDump
                    .entrySet()) {
                String key = statsCacheDumpEntry.getKey();
                Map<String, String> statsCacheDumpValue = statsCacheDumpEntry
                        .getValue();
                keyList.addAll(statsCacheDumpValue.keySet());
            }
        }

        return keyList;
    }

    private void debug(String name, Map<?, ?> map) {
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            String log = MessageFormat.format("key={0} value={1}",
                    entry.getKey(), entry.getValue());
            logger.debug(log);
        }
    }

    public static void main(String[] args) {
        MyMemcachedClient client = new MyMemcachedClient();
        client.setAndGetExampleData();
        client.showKeyAndValue();

    }
}

実行結果、コンソール出力

bar=This is a test String
testBean=TestBean [stringField=a, integerField=0]
key=bar value=This is a test String
key=testBean value=TestBean@1a457b6[stringField=a,integerField=0]

おおー、うまくいった。

Memcachedで、一覧取得、全件取得ってのが、やりたかったわけで、もっと簡単にはできないのだろうか

みんな、実際、どうしているんだろうな。開発中あると便利だと思うんだが。データ量が増えると、全件取得なんてのは、やっぱり、実際的でないのか。

MySQLとか、RDBだったら、selectで全件取得はできるけど、やっぱり、データが多いときは、あんまりうれしくないしな。