• NoSQL之Memcached


    一、Memcached概念

    MemcachedNoSQL产品之中的一个,是一个暂时性键值存储NoSQL数据库,过去被大量使用在互联网站点中,作为应用和数据库之间的缓存层,大大提高查询和訪问速度。

    Memcached有下面特点:

    1、全内存运转:数据从来不保存在硬盘中。机器一重新启动,数据就所有没有了,所有又称暂时性数据库;

    2、哈希方式存储:

    3、简单文本协议进行数据通信:不须要特定二进制代码,仅仅须要用telnet连通memcached的监听port,打入简单浅显的代码就能操作;

    4、仅仅操作字符型数据:不管往memcached放入什么。都是以字节的方式来处理。

    还原成数组、哈希表、字符串和数值等都交给应用层来解释。应用读写memcached的数据时,进行序列化和反序列化。把其解释成应用所能理解的数据类型。

    5、集群也由应用进行控制,採用一致性散列(哈希)算法。

    二、安装Memcached

    1、在linux上搭建yum环境

    2、使用yum命令进行安装Memcachedrpm

    [root@nn init.d]# yum install memcached

     

    3、启动Memcached

    首先要cd到对应文件夹

    [root@nn ~]# cd /etc/rc.d/init.d/

    执行memcached安装脚本

    [root@nn init.d]# ./memcached start

     

    4、查看Memcached是否启动

    [root@nn init.d]# pstree

     

    表示Memcached进程被启动了,以下开了5个线程

    或者使用[root@nn init.d]# ps aux命令

     

    memcached -d -p 11211 -u memcached -m 64 -c 1024 -P /var/run/memcached/memcached.pid

    -d表示程序要后台化执行,-p指定port。-u表示用memcached这个身份来执行,后面的都是memcached的控制參数

    5、连接Memcached

    [root@nn init.d]# telnet localhost 11211

     

    三、Memcached经常使用命令

    命令格式:

    <command name> <key> <flags> <exptime> <bytes>

    <data block>

    參数说明例如以下:

    <command name>

    set/add/replace

    <key>

    查找keyword

    <flags>

    客户机使用它存储关于键值对的额外信息。用于指定是否压缩,0不压缩,1压缩

    <exptime>

    该数据的存活时间,0表示永远

    <bytes>

    存储字节数

    <data block>

    存储的数据块(可直接理解为key-value结构中的value

    1、添加:setaddcas

    2、获取:getgets、 

    3、追加:appendprepend

    4、删除:delete

    5、清除全部:flush_all

    6、加减:incrdecr

    7、退出:quit

     

    三、用java连接Memcached

    眼下java提供了三种API供我们实现与Memcached的连接和存取

    1memcached client for java

    较早推出的memcached JAVAclientAPI,应用广泛,执行比較稳定。

    2pymemcached

    A simple, asynchronous, single-threaded memcached client written in java. 支持异步。单线程的memcached客户端,用到了java1.5版本号的concurrentnio,存取速度会高于前者,可是稳定性不好,測试中常报timeOut等相关异常。

    3xmemcached

    XMemcached相同是基于java nio的client。java nio相比于传统堵塞io模型来说,有效率高(特别在高并发下)和资源耗费相对较少的长处。传统堵塞IO为了提高效率,须要创建一定数量的连接形成连接池。而nio仅须要一个连接就可以(当然,nio也是能够做池化处理),相对来说降低了线程创建和切换的开销,这一点在高并发下特别明显。因此XMemcachedSpymemcached在性能都很优秀,在某些方面(存储的数据比較小的情况下)XmemcachedSpymemcached的表现更为优秀,详细能够看这个Java Memcached Clients Benchmark

    本文章使用memcached client for java为例

    在使用java连接远程的PC机的Memcached时。记得保证两台机都开启telnet服务。而且本机能telnet通远程机,远程机必须关闭防火墙。

    实例代码1:(java连接Memcached并实现数据的存取)

    import com.danga.MemCached.MemCachedClient;

    import com.danga.MemCached.SockIOPool;

    public class memcachedTest {

    public static void main(String[] args) {

    //初始化SockIOPool,管理Memcached的连接池

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

    SockIOPool pool = SockIOPool.getInstance();

    pool.setServers(servers);

    pool.setFailover(true);

    pool.setInitConn(10);

    pool.setMinConn(5);

    pool.setMaxConn(250);

    pool.setMaintSleep(30);

    pool.setNagle(false);

    pool.setSocketTO(3000);

    pool.setAliveCheck(true);

    pool.initialize();

    //建立MemcachedClient实例

    MemCachedClient memCachedClient = new MemCachedClient();

    for(int i = 0;i < 100000;i++){

    //将对象增加到memcached缓存

    boolean success = memCachedClient.set(""+i, "hello!");

    }

    for(int i = 0;i < 100000;i++){

    //从memcached缓存中按key值取对象

    String result = (String)memCachedClient.get(""+i);

    System.out.println(String.format("get(%d):%s", i,result+i));

    }

    }

    }

    四、測试Memcached性能

    为性能对照測试准备数据

    1、插入数据到oracle

    /**

     * 插入測试数据到oracle数据库

     * @param count插入记录数

     * @return

     */

    public static boolean insertIntoOracle(int count){

    try {

    con = dbConn("feng","feng");

    if(con == null){

    System.out.println("连接失败");

    System.exit(0);

    }

    System.out.println("truncate table memcached_test......");

    sql = "truncate table memcached_test";

    pstmt = con.prepareStatement(sql);

    rs = pstmt.executeQuery();

    System.out.println("truncate table memcached_test finish.");

    System.out.println("insert "+count+" values");

    sql = "insert into memcached_test (memcachedId,memcachedvalues) values (?,?

    )";

    pstmt = con.prepareStatement(sql);

    for(int i = 1;i <= count;i++){

    pstmt.setInt(1, i);

    pstmt.setString(2, "Memcached is a good thing.I like it very much !-----------"+i);

    pstmt.executeUpdate();

    }

    System.out.println("insert "+count+" values finish.");

    rs.close();

    pstmt.close();

    con.close();

    catch (ClassNotFoundException e) {

    e.printStackTrace();

    catch (SQLException e) {

    e.printStackTrace();

    }

    return true;

    }

    public static Connection dbConn(String name,String pass) throws ClassNotFoundException, SQLException{

    Connection conn = null;

    Class.forName("oracle.jdbc.driver.OracleDriver");

    conn = DriverManager.getConnection("jdbc:oracle:thin:@192.183.3.230:1522:myorcl",name,pass);

    return conn;

    }

    2、插入数据到Memcached

    /**

     * 插入測试数据到Memcached

     * @param count插入记录数

     * @return

     */

    public static boolean insertIntoMemcached(int count){

    //初始化SockIOPool。管理Memcached的连接池

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

    SockIOPool pool = SockIOPool.getInstance();

    pool.setServers(servers);

    pool.setFailover(true);

    pool.setInitConn(10);

    pool.setMinConn(5);

    pool.setMaxConn(250);

    pool.setMaintSleep(30);

    pool.setNagle(false);

    pool.setSocketTO(3000);

    pool.setAliveCheck(true);

    pool.initialize();

    //建立MemcachedClient实例

    MemCachedClient memCachedClient = new MemCachedClient();

    System.out.println("insert "+count+" values into memcached......");

    for(int i = 1;i < count;i++){

    //将对象增加到memcached缓存

    boolean success = memCachedClient.set("testData"+i, insertStr+i);

    }

    System.out.println("insert "+count+" values into memcached finish.");

    return true;

    }

    Main函数调用这两个方法后,会将count条记录,值为insertData,插入到Oracle数据和set进Memcached中。

    1、比較同一时候插入100000条数据的时间

     

    从执行结果能够看出,插入10万条数据到Memcached比插10万条数据入Oracle所用时间有一个质的降低。

    2、比較查询时间

    下面是连接oracle数据并查找10000条数据的方法

    /**

     * oracle数据库查找

     * @param count记录数

     * @return

     * @throws ParseException 

     */

    public static long searchOracle(int count) throws ParseException{

    long useTime = 0;

    try {

    con = dbConn("feng","feng");

    if(con == null){

    System.out.println("连接失败");

    System.exit(0);

    }

    StringBuffer sql =new StringBuffer("select memcachedid,memcachedvalues from memcached_test where memcachedid = ?");

    pstmt = con.prepareStatement(sql.toString());

    String memcachedvalues = "";

    System.out.println("search table memcached_test......");

    String beginTime = d.format(new Date());

    for(int i = 1;i <= count;i++){

    if(i%10 == 0){

    pstmt.setInt(1, i);

    rs = pstmt.executeQuery();

    while(rs.next()){

    memcachedvalues = rs.getString(2);

    }

    }

    }

    System.out.println("search table memcached_test finish.");

    String endTime = d.format(new Date());

    useTime = d.parse(endTime).getTime() - d.parse(beginTime).getTime();

    long ss = (useTime/1000)%60;//秒

    long MM = useTime/60000;//分

    System.out.println("Oracle中查找10000条记录的開始时间:"+beginTime);

    System.out.println("Oracle中查找10000条记录的结束时间:"+endTime);

    System.out.println("Oracle中查找10000条记录的所用时间:  "+MM+"分"+ss+"秒");

    rs.close();

    pstmt.close();

    con.close();

    catch (ClassNotFoundException e) {

    e.printStackTrace();

    catch (SQLException e) {

    e.printStackTrace();

    }

    return useTime;

    }

    下面是连接Memcached并查找10000条数据的方法

    /**

     * Memcached查找

     * @param count

     * @return

     * @throws ParseException

     */

    public static long searchMemcached(int count) throws ParseException{

    //初始化SockIOPool。管理Memcached的连接池

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

    SockIOPool pool = SockIOPool.getInstance();

    pool.setServers(servers);

    pool.setFailover(true);

    pool.setInitConn(10);

    pool.setMinConn(5);

    pool.setMaxConn(250);

    pool.setMaintSleep(30);

    pool.setNagle(false);

    pool.setSocketTO(3000);

    pool.setAliveCheck(true);

    pool.initialize();

    //建立MemcachedClient实例

    MemCachedClient memCachedClient = new MemCachedClient();

    System.out.println("search 10000 data in Memcached......");

    String memcachedvalues = "";

    String beginTime = d.format(new Date());

    for(int i = 1;i <= count;i++){

    //从memcached缓存中按key值取对象

    if(i%10 == 0){

    memcachedvalues = (String)memCachedClient.get("testData"+i);

    }

    }

    System.out.println("search 10000 data in Memcached finish.");

    String endTime = d.format(new Date());

    long useTime = d.parse(endTime).getTime() - d.parse(beginTime).getTime();

    long ss = (useTime/1000)%60;//秒

    long MM = useTime/60000;//分

    System.out.println("从Memcached查找10000条记录的開始时间:"+beginTime);

    System.out.println("从Memcached查找10000条记录的结束时间:"+endTime);

    System.out.println("从Memcached查找10000条记录的所用时间:  "+MM+"分"+ss+"秒");

    return useTime;

    }

    执行结果例如以下:

     

    从执行结果能够看出,同一时候查找10000条数据,Memcached所用时间比1Oracle所用时间降低了29

    四、启动多个节点的Memcached

    因为实验器材有限,如今在同一台pc机中启动多个Memcached,仅仅要设定port不一样,这些Memcached之间互相不会干扰。

    启动命令例如以下:

    [root@nn init.d]# memcached -d -p 11212 -u memcached -m 64 -c 1024

    [root@nn init.d]# memcached -d -p 11213 -u memcached -m 64 -c 1024

    当中-d表示在后台执行,-p表示port号,-u表示用户

    启动之后用pstree查看

    [root@nn init.d]# pstree

     

    3*[memcached───5*[{memcached}]]表示有3Memcached的进程。

    或者用ps aux命令

    [root@nn init.d]# ps aux

     

    往多节点的Memcached中插入数据的java代码

    /**

     * 往节点Memcached插入数据

     * @param count

     */

    public static void testManyNode(int count){

    //初始化SockIOPool,管理Memcached的连接池

    String[] servers = {"192.183.3.230:11211","192.183.3.230:11212","192.183.3.230:11213"};

    SockIOPool pool = SockIOPool.getInstance();

    pool.setServers(servers);

    pool.setFailover(true);

    pool.setInitConn(10);

    pool.setMinConn(5);

    pool.setMaxConn(250);

    pool.setMaintSleep(30);

    pool.setNagle(false);

    pool.setSocketTO(3000);

    pool.setAliveCheck(true);

    pool.initialize();

    //建立MemcachedClient实例

    MemCachedClient memCachedClient = new MemCachedClient();

    String beginTime = d.format(new Date());

    for(int i = 1;i <= count;i++){

    //将对象增加到memcached缓存

    boolean success = memCachedClient.set("node"+i, insertStr+i);

    }

    }

    Memcached的查询结果:

     

    从结果中能够看出,数据分布到Memcached的不同节点上。

    五、高可用方案repcached

    假如Memcached中有一个节点失效了,这个节点所管辖的数据都没有,我们必须又一次去数据库中获取数据放入新的节点中。这样会引发数据库性能的波动。这里就须要我们做一个高可用的Memcached,使得Memcached中的每个节点都有另外一个节点与之中的一个一相应。这两个一一相应的节点中的数据是一模一样的。这样当当中一个节点失效了,另外一个节点就能立即接管缓存的工作,这样就不须要又一次从数据库中获取数据库。

    以下我们使用repcached来实现Memcached的高可用

    1、下载repcached

    [root@nn ~]# wget http://downloads.sourceforge.net/repcached/memcached-1.2.8-repcached-2.2.tar.gz

     

    2、杀死本机上的全部Memcached

    [root@nn ~]# killall memcached

    3、解压下载的repcached

    [root@nn ~]# tar zxvf memcached-1.2.8-repcached-2.2.tar.gz

     

    4、进入所解压的文件夹

    [root@nn ~]# cd memcached-1.2.8-repcached-2.2

     

    5、安装依赖包,执行编译repcached所须要的

    [root@nn memcached-1.2.8-repcached-2.2]# yum install libevent-devel

    6、開始安装repcached

    [root@nn memcached-1.2.8-repcached-2.2]# ./config --enable-replication --program-transform-name=s/memcached/repcached/

     

     

    安装好之后就会产生一个Makefile文件

     

    7、能够使用Makefile来编译

    [root@nn memcached-1.2.8-repcached-2.2]# make

    [root@nn memcached-1.2.8-repcached-2.2]# make install

     

    能够看到主要安装的程序有repcachedrepcached-debug

    8、启动Memcached的高可用集群(注意不能用root用户来启动)

    [oracle@nn ~]$ /usr/local/bin/repcached -p 11211 -v -d

    [oracle@nn ~]$ /usr/local/bin/repcached -p 11212 -x localhost -v -d

     

    当中-x表示要监听高可用机器,假设是其它port要写上“:port号”,假设是默认port(11211)。就不须要写。

    9、測试:从11211port插入数据,到11212port去查找;从11212port插入数据,到11211port去查找

     

    从測试结果能够看出。这个高可用复制是双向的。不管在哪一个节点插入数据,都能够在另外一个节点中查到。

    六、Memcached的一致性

    假设有两个不同终端连接同一Memcached服务,对同一key值进行改动,结果会怎样呢?

    以下来做实验

    1、A终端连接Memcachedsetkeycounter的一个值1

     

    gets命令看出。比用get命令多最后一个数字,这个数字表示这个key的版本

    2、B终端连接Memcachedsetkeycounter的一个值2

     

    3、用set命令去改会改变一致性,这里改用cas命令

     

    我们用gets查看当前的版本是3

     

    用cas最后一个參数表示版本,假设版本不一样。不能改动。会有EXISTS提示。表示改动失败;假设版本一致,就能改动成功。

    Memcached的缺点

    1、纯内存操作的数据库。关机或者关闭Memcached进程后数据所有丢失;

    2、保存字节数,数据类型贫乏,其它数据类型都要通过应用程序来解释,这样应用端要做的事情会非常多,server端要做的事情就非常好非常好了;

    3、兼容性差。不同编程语言之间不能相互调用;

    4、LRU算法导致数据不可控的丢失。

    5、一致性处理简单。

    6、应用场景有限,难以被看成是完整的数据库产品,只用来做数据库和应用之间的缓存层。

  • 相关阅读:
    修改CentOs开机启动时的timeout
    各种快捷键
    测试实例异常
    测试实例异常
    springBoot中测试类的头注解
    学习笔记9
    stat命令的实现-mystat
    反汇编测试
    学习笔记7
    openssl截图
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6873535.html
Copyright © 2020-2023  润新知