• 内存数据库专题(MemCached 和Redis)


    内存数据库专题

    为什么要把数据存入内存?

    常见的内存数据库:
    MemCached:看成Redis前身,严格来说,MemCached不能叫数据库,只能叫缓存
    不支持持久化。如果内存停电,数据丢失

    Redis:内存数据库,支持持久化,支持HA

    Oracle TimesTen

    session一致性

    MemCached + keepalive实现

    一、Memcached

    1、基本原理和体系架构

    (*)在内存中,维护了一张巨大的Hash表
    (*)通过路由算法来决定数据存储的位置。—> 客户端路由

    Memcached基本原理和体系架构

    Memcached基本原理和体系架构.

    注意:默认,官方版本MemCached实例彼此不会进行通信

    第三方版本可以实现通信

    2、安装配置MemCached

    前提:
    (1)gcc编译器
    yum install gcc
    gcc –version

    (2)libevent库
    tar -zxvf libevent-2.0.21-stable.tar.gz
    cd libevent-2.0.21-stable
    ./configure --prefix=/root/hd/libevent
    make
    make install

    tar -zxvf memcached-1.4.25.tar.gz
    cd memcached-1.4.25

    ./configure --prefix=/root/hd/memcached --with-libevent=/root/hd/libevent
    make
    make install
    cd bin/
    ./memcached -u root -d -m 128 -p 11211
    ./memcached -u root -d -m 128 -p 11212
    ./memcached -u root -d -m 128 -p 11213
    ps -ef | grep memcached

    注意
    -u:root用户需要注明(其他用户可以不写)
    -d:启动守护线程(在后天运行)
    -m:占用多少内存
    -p:运行在哪个端口

    3、操作MemCached

    (*)命令行
    telnet 192.168.116.121 11211

    保存数据:
    set 如果key存在,替换原来的值
    add 如果key存在,返回错误

    set key1 0 0 4
    key名字 标识位 数据过期时间0表示不过期 value的长度

    abcd
    get key1

    统计命令
    stats items
    stats

    package day1;
    import net.spy.memcached.MemcachedClient;
    import net.spy.memcached.internal.OperationFuture;
    import java.io.IOException;
    import java.io.Serializable;
    import java.net.InetSocketAddress;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Future;
    /**
    * java 操作Memcached
    */
    public class Demo1 {
    public static void main(String[] args) throws Exception {
    //hello();
    //testGet();
    //setStudent();
    testSets() ;
    }

    [root@hsiehchou121 memcached]# cd bin/
    [root@hsiehchou121 bin]# ./memcached -u root -d -m 128 -p 11211
    [root@hsiehchou121 bin]# ./memcached -u root -d -m 128 -p 11212
    [root@hsiehchou121 bin]# ./memcached -u root -d -m 128 -p 11213

    插入数据

    public static void hello() throws Exception {
    //连接到集群,set key
    //建立MemcachedClient实例,指定Memcached服务器的IP地址和端口
    MemcachedClient client = new MemcachedClient(new InetSocketAddress("192.168.116.121",11211));
    Future<Boolean> f = client.set("key1", 0, "hello World");
    if (f.get().booleanValue()){
    client.shutdown();
    }
    }

    [root@hsiehchou121 ~]# telnet 192.168.116.121 11211
    get key1

    查询数据

    public static void testGet() throws Exception{
    //连接到集群,set key
    //建立MemcachedClient实例,指定Memcached服务器的IP地址和端口
    MemcachedClient client = new MemcachedClient(new InetSocketAddress("192.168.116.121",11211));
    //按照key取值,不存在的话返回null
    Object o = client.get("key1");
    System.out.println("取到的值为: " + o);
    client.shutdown();
    }

    取到的值为:hello World

    插入类

    public static void setStudent() throws Exception{
    //连接到集群,set key
    //建立MemcachedClient实例,指定Memcached服务器的IP地址和端口
    MemcachedClient client = new MemcachedClient(new InetSocketAddress("192.168.116.121",11211));
    Future<Boolean> f = client.set("stu1", 0, new Student());
    if (f.get().booleanValue()){
    client.shutdown();
    }
    }

    [root@hsiehchou121 ~]# telnet 192.168.116.121 11211
    get stu1

    基于客户端的分布式插入数据

    public static void testSets() throws Exception{
    //测试客户端路由算法
    //构造每台Memcached服务器加入List
    List<InetSocketAddress> list = new ArrayList<>();
    list.add(new InetSocketAddress("192.168.116.121",11211));
    list.add(new InetSocketAddress("192.168.116.121",11212));
    list.add(new InetSocketAddress("192.168.116.121",11213));
    //建立Memcached Client实例
    MemcachedClient client = new MemcachedClient(list);
    for (int i = 0; i < 20; i++){
    System.out.println("插入数据:" + i);
    client.set("key"+i,0, "value"+i);//(key1,value1)(key2,value2)
    Thread.sleep(1000);
    }
    client.shutdown();
    }

    [root@hsiehchou121 ~]# telnet 192.168.116.121 11211
    get key1
    VALUE key1 0 6
    value1
    [root@hsiehchou121 ~]# telnet 192.168.116.121 11212
    get key2
    VALUE key2 0 6
    value2
    [root@hsiehchou121 ~]# telnet 192.168.116.121 11213
    get key3
    VALUE key3 0 6
    value3

    }
    class Student implements Serializable{}
    4、MemCached路由算法

    1)求余数hash算法
    用key做hash运算得到一个整数,根据余数路由
    例如:服务器端有三台MemCached服务器
    根据key,做hash运算
    7%3=1,那么就路由到第2台服务器
    6%3=0,那么路由到第1台服务器
    5%3=2,那么路由到第3台服务器

    优点:数据分布均衡在多台服务器中,适合大多数据需求
    缺点:如果需要扩容或者有宕机的情况,会造成数据的丢失

    2)一致性hash算法
    基本原理
    key1 1-333 放在node1上
    key2 334-666放在node2上
    key3 667-1000放在hsiehchou121上

    一致性hash算法下扩容
    key1 1-333 放在node1上
    key2 334-666放在node2上
    key3 667-831放在hsiehchou121上
    key4 832-1000放在node4上
    如果扩容,增加一个新的节点,只影响扩容的节点,对其他节点不影响

    一致性hash算法下DOWN机
    key1 1-333 放在node1上
    key2 334-666放在node2上
    key3 667-1000放在hsiehchou121上
    如果宕机,对key1 和 key2不产生影响,只对key3产生影响

    5、MemCached的主主复制和HA

    1)Memcached主主复制
    架构图

    主服务器 主服务器
    memcached <——-> memcached
    1. 安装具有复制功能的memcached版本
      tar zxvf memcached-1.2.8-repcached-2.2.tar.gz
      cd memcached-1.2.8-repcached-2.2
      ./configure --prefix=/root/hd/memcached_replication
      --with-libevent=/root/hd/libevent/ --enable-replication
      make
      make install

    出现以下错误

    memcached.c: 696: error: `IOV MAX` undeclared (first use in this function)
    memcached.c: 696: error: (Each undeclared identifier is reported only once
    memcached.c: 696: error: for each function it appears in.)

    解决办法
    编辑memcached.c文件如下
    55 /* FreeBSD 4.x doesn’t have IOV_MAX exposed. */
    56 #ifndef IOV_MAX
    57 #if defined(FreeBSD) || defined(APPLE)
    58 # define IOV_MAX 1024
    59 #endif
    60 #endif

    修改成如下形式
    55 /* FreeBSD 4.x doesn’t have IOV_MAX exposed. */
    56 #ifndef IOV_MAX
    57 //#if defined(FreeBSD) || defined(APPLE)
    58 # define IOV_MAX 1024
    59 //#endif
    60 #endif

    启动第一台MemCached,使用-x指定对端服务器的地址
    ./memcached -u root -d -m 128 -x 192.168.116.121

    启动第二台MemCached,使用-x指定对端服务器的地址
    ./memcached -u root -d -m 128 -x 192.168.116.122

    出现以下错误
    ./memcached: error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: No such file or directory

    解决办法
    查找 libevent-2.0.so.5
    whereis libevent-2.0.so.5

    使用ldd命令查看memcached命令,发现找不到
    [root@hsiehchou121 bin]# ldd /root/hd/memcached-1.2.8-repcached-2.2/bin/memcached
    linux-gate.so.1 => (0x00255000)
    libevent-2.0.so.5 => not found
    libc.so.6 => /lib/libc.so.6 (0x00110000)
    /lib/ld-linux.so.2(0x003a4000)

    建立软连接
    ln -s /root/hd/libevent/lib/libevent-2.0.so.5 /usr/lib/libevent-2.0.so.5

    2)Memcached的HA(High Availablity)
    Keepalived是一个交换机制的软件。Keepalived的作用是检测服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作,当服务器工作正常后Keepalived自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务器

    利用Keepalived实现MemCached的主主复制高可用架构
     Keepalived在memcached主服务器产生一个虚拟IP(VIP)
     Keepalived可以通过不断的检测memcached主服务器的11211端口是否正常工作,
     如果发现memcached Down机,虚拟IP就从主服务器移到从服务器

    配置Keepalived(每台机器都要配置)
     rpm -ivh keepalived-1.2.13-4.el6.i686.rpm

     配置:主从节点都要配置,配置文件
     /etc/keepalived/keepalived.conf

    主节点配置信息

    ! Configuration File for keepalived
    global_defs {
    notification_email {
    417952939@qq.com
    }
    notification_email_from collen_training@126.com
    smtp_server 127.0.0.1
    smtp_connect_timeout 30
    router_id LVS_DEVEL
    }
    vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 101
    advert_int 1
    authentication {
    auth_type PASS
    auth_pass 1111
    }
    virtual_ipaddress {
    192.168.116.88
    }
    }

    从节点配置信息

    ! Configuration File for keepalived
    global_defs {
    notification_email {
    417952939@qq.com
    }
    notification_email_from collen_training@126.com
    smtp_server 127.0.0.1
    smtp_connect_timeout 30
    router_id LVS_DEVEL
    }
    vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
    auth_type PASS
    auth_pass 1111
    }
    virtual_ipaddress {
    192.168.116.88
    }
    }

    验证Keepalived: 使用命令 ip ad sh 查看虚拟ip地址
    inet 192.168.116.88/32 scope global eth0

    二、Redis

    1、Redis简介

    (1)Redis的前身:Memcached
    (2)和Memcached区别“
    (*)支持持久化:RDB快照、AOF日志
    (*)支持丰富的数据类型

    2、安装Redis

    833 tar -zxvf redis-3.0.5.tar.gz
    839 cd redis-3.0.5/
    841 make
    842 make PREFIX=/root/hd/redis install

    redis-benchmark : Redis提供的压力测试工具。模拟产生客户端的压力
    redis-check-aof: 检查aof日志文件
    redis-check-dump: 检查rdb文件
    redis-cli: Redis客户端脚本
    redis-sentinel: 哨兵
    redis-server: Redis服务器脚本

    核心配置文件:redis.conf
    [root@hsiehchou121 redis-3.0.5]# cp redis.conf /root/hd/redis/
    [root@hsiehchou121 redis]# mkdir conf
    [root@hsiehchou121 redis]# mv redis.conf conf/
    [root@hsiehchou121 conf]# vi redis.conf

    42 daemonize yes //后台方式运行
    50 port 6379

    启动redis ./bin/redis-server conf/redis.conf
    检测是否启动好
    [root@hsiehchou121 redis]# ./bin/redis-server conf/redis.conf

    3、操作Redis

    1)命令行
    redis-cli
    ./bin/redis-cli

    127.0.0.1:6379> set key1 value1
    OK
    127.0.0.1:6379> get key1
    “value1”
    127.0.0.1:6379> keys *
    1) “key1”

    对数据的操作:
    127.0.0.1:6379> set money 100
    OK
    127.0.0.1:6379> incr money
    (integer) 101
    127.0.0.1:6379> get money
    “101”
    127.0.0.1:6379> incrby money 10000
    (integer) 10101

    2)数据类型
    ① 字符串
    127.0.0.1:6379> set key1 “hello”
    OK
    127.0.0.1:6379> get key1
    “hello”
    127.0.0.1:6379>
    127.0.0.1:6379> append key1 “*******
    (integer) 12
    127.0.0.1:6379> get key1
    “hello*******

    ② 链表
    127.0.0.1:6379>
    127.0.0.1:6379> lpush list 11 22 33 44 55
    (integer) 5
    127.0.0.1:6379> lrange list 0 2
    1) “55”
    2) “44”
    3) “33”
    127.0.0.1:6379> lrange list 0 -1
    1) “55”
    2) “44”
    3) “33”
    4) “22”
    5) “11”
    127.0.0.1:6379> lpop list
    “55”

    ③ Hash
    127.0.0.1:6379> hset hashkey1 name ls
    (integer) 1
    127.0.0.1:6379> hset hashkey2 age 23
    (integer) 1
    127.0.0.1:6379> hmset user001 name ls age 23 gender mals
    OK
    127.0.0.1:6379> hmset user002 name xz age 24 gender mals
    OK
    127.0.0.1:6379> hmget user001 name age gender
    1) “ls”
    2) “23”
    3) “mals”
    127.0.0.1:6379> hgetall user001
    1) “name”
    2) “ls”
    3) “age”
    4) “23”
    5) “gender”
    6) “mals”

    ④ 无序集合
    无序,不可重复的集合
    127.0.0.1:6379> sadd setkey1 11 22 33 44 55
    (integer) 5
    127.0.0.1:6379> sadd setkey2 33 44 55 66 77 88
    (integer) 6
    127.0.0.1:6379> smembers setkey1
    1) “11”
    2) “22”
    3) “33”
    4) “44”
    5) “55”

    sdiif: 差集
    sinter: 交集
    suntion:并集

    ⑤ 有序集合
    有序可以重复的集合,根据一个score来进行排序
    127.0.0.1:6379> zadd chinese 90 Tom 92 Nary 83 Nike
    (integer) 3
    127.0.0.1:6379> zrange chinese 0 100
    1) “Nike”
    2) “Tom”
    3) “Nary”
    127.0.0.1:6379> zrange chinese 0 100 withscores
    1) “Nike”
    2) “83”
    3) “Tom”
    4) “90”
    5) “Nary”
    6) “92”

    ⑥ Redis数据类型案例分析:网站统计用户登录的次数
    a. 1亿个用户,有经常登录的,也有不经常登录的
    b. 如何来记录用户的登录信息
    c. 如何查询活跃用户:比如:一周内,登录3次的

     解决方案一:采用关系型数据库
    建立表:记录一周内,每天登录的情况

    采用关系型数据库保存登录信息存在的问题,每天产生一亿条数据,一周就是7亿条数据

     解决方案二:采用Redis存储登录信息
    可以使用Redis的setbit,登录与否:有1和0就可以表示
    一亿个用户,每天是否登录,用1或者0表示即可,每天产生约12M的数据
    一亿个用户一周的登录信息:
    12M*7=84M

    3)Java Api
    ① 基本操作
    @Test
    public void testString(){
    Jedis jedis = new Jedis("192.168.116.121",6379);
    //添加数据
    jedis.set("name","ls");//向key-->name中放入了value-->ls
    System.out.println(jedis.get("name"));//执行结果:ls
    jedis.append("name"," is my lover");//拼接
    System.out.println(jedis.get("name"));//执行结果:ls is my lover
    jedis.del("name");//删除某个键
    System.out.println(jedis.get("name"));//执行结果:null
    //设置多个键值对
    jedis.mset("name","tom","age","23","qq","123456789");
    jedis.incr("age");//进行加1操作
    System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq"));//执行结果:tom-24-123456789
    jedis.disconnect();
    }

    ② 连接池

    public class RedisUtils {
    private static JedisPool jedisPool = null;
    static {
    try {
    JedisPoolConfig config = new JedisPoolConfig();
    //可用连接实例的最大数目,默认值为8 如果赋值为-1,则表示不限制
    config.setMaxTotal(1024);
    //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8
    config.setMaxIdle(200);
    //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时
    config.setMaxWaitMillis(10000);
    //在borrow一个jedis实例时,是否提前进行validate操作,如果为true,则得到的jedis实例均是可用的
    config.setTestOnBorrow(true);
    jedisPool = new JedisPool(config, "192.168.116.121");
    }catch (Exception e) {
    e.printStackTrace();
    }
    }
    public synchronized static Jedis getJedis(){
    try {
    if (jedisPool != null)
    return jedisPool.getResource();
    }catch (Exception e){
    e.printStackTrace();
    }
    return null;
    }
    public static void returnResource(final Jedis jedis){
    if (jedis != null)
    jedisPool.returnResource(jedis);
    }
    }

    ③ 使用Redis实现分布式锁
    使用Maven搭建工程:

    <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    </dependency>
    4、Redis的事务:不是真正的事务,是一种模拟

    1)复习:事务(关系型数据库)
    (*)什么是事务?
    事务有一组DML(Data Manipulation Language)语句组成。DML 插入更新删除操作

    (*)事务的特点
    要么都成功,要么都失败

    (*)Oracle中事务的本质:将事务的DML操作写入日志。日志写入成功,则事务执行成功

    2)Redis事务的本质:将一组操作放入队列中,一次执行(批处理)

    3)对比Oracle和Redis事务的区别

    操作 Oracle Redis
    开启事务 自动开启 命令:multi
    执行语句 DML Redis命令
    提交事务 显式提交:commit;隐式提交:DDL语句(create table) 命令:exec 执行放在multi里面的操作
    回滚事务 显式回滚:rollback;隐式回滚:系统掉电,客户端退出 命令:discard 把队列中的操作废弃掉

    注意:不是真正的事务,只是一种模拟

    4)举例:模拟银行转账
    set tom 1000
    set mike 1000
    tom –> mike 转账操作必须在事务中,要么都成功,要么都不成功
    multi
    decrby tom 100
    incrby mike 100
    exec

    127.0.0.1:6379> set tom 1000
    OK
    127.0.0.1:6379> set mike 1000
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> decrby tom 100
    QUEUED
    127.0.0.1:6379> incrby mike 100
    QUEUED
    127.0.0.1:6379> exec
    1) (integer) 900
    2) (integer) 1100

    5)举例:买票
    set tom 1000
    set ticket 1
    multi
    decrby tom 500
    decr ticket
    exec

    在exec前,从另一个窗口,decr ticket
    127.0.0.1:6379> set tom 1000
    OK
    127.0.0.1:6379> set ticket 1
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> decrby tom 500
    QUEUED
    127.0.0.1:6379> decr ticket
    QUEUED
    127.0.0.1:6379> exec
    1) (integer) 500
    2) (integer) -1

    5、Redis锁机制

    执行事务操作的时候,如果监视的值发生了变化,则提交失败
    命令:watch

    举例:买票
    set tom 1000
    set ticket 1
    watch ticket —–> 相当于给ticket加了锁。认为在下面执行事务的时候,值不会变
    multi
    decrby tom 500
    decr ticket
    exec

    127.0.0.1:6379> set tom 1000
    OK
    127.0.0.1:6379> set ticket 1
    OK
    127.0.0.1:6379> watch ticket
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> decrby tom 500
    QUEUED
    127.0.0.1:6379> decr ticket
    QUEUED
    127.0.0.1:6379> exec
    (nil)
    127.0.0.1:6379> get tom
    “1000”

    nil 代表操作没有执行或者执行失败

    Java应用程序中的事务和锁
    ① 事务

    @Test
    public void testTransaction(){
    Jedis jedis = new Jedis("192.168.116.121",6379);
    Transaction tc = null;
    try{
    //开启事务
    tc = jedis.multi();
    tc.decrBy("tom", 100);
    tc.incrBy("mike", 100);
    //提交事务
    tc.exec();
    }catch (Exception e){
    e.printStackTrace();
    //回滚事务
    tc.discard();
    }
    jedis.disconnect();
    }

    ② 锁

    @Test
    public void testLock(){
    Jedis jedis = new Jedis("192.168.116.121",6379);
    //对ticket加锁,如果在事务执行过程中,该值有变化,则抛出异常
    jedis.watch("ticket");
    Transaction tc = null;
    try{
    //开启事务
    tc = jedis.multi();
    tc.decr("ticket");//车票数量减一
    Thread.sleep(5000);
    tc.decrBy("tom", 100);//扣tom 100块钱买票的钱
    //提交事务
    tc.exec();
    }catch (Exception e){
    e.printStackTrace();
    //回滚事务
    tc.discard();
    }
    jedis.disconnect();
    }
    6、Redis的消息机制:消息系统

    1)消息的类型
    (*)Queue消息:队列,点对点
    (*)Topic消息:主题,群发:发布消息,订阅消息

    2)Redis消息机制
    只支持Topic消息

    命令:发布消息 publish  格式:publish channel名称 “消息内容”

    订阅:subscribe  格式:subscribe channel名称

    psubscribe 订阅消息 —— 可以用通配符来订阅消息
     格式:psubscribe channel*名称

    3)常用的消息系统:
    Redis 只支持 Topic
    Kafka 只支持Topic 需要Zookeeper支持
    JMS Java Messging Service java消息服务标准。支持Queue Topic
    产品:Weblogic

    例子:
    窗口1(发):
    127.0.0.1:6379> PUBLISH c1 hello
    (integer) 2
    127.0.0.1:6379> PUBLISH c1 test
    (integer) 2

    窗口2(订):
    127.0.0.1:6379> SUBSCRIBE c1
    Reading messages… (press Ctrl-C to quit)
    1) “subscribe”
    2) “c1”
    3) (integer) 1
    1) “message”
    2) “c1”
    3) “hello”
    1) “message”
    2) “c1”
    3) “test”

    窗口3(订):
    127.0.0.1:6379> SUBSCRIBE c1
    Reading messages… (press Ctrl-C to quit)
    1) “subscribe”
    2) “c1”
    3) (integer) 1
    1) “message”
    2) “c1”
    3) “hello”
    1) “message”
    2) “c1”
    3) “test”

    通过通配符订阅
    窗口1(发):
    127.0.0.1:6379> PUBLISH c2 hello
    (integer) 1
    127.0.0.1:6379> PUBLISH c4 hello
    (integer) 1

    窗口2(发):
    127.0.0.1:6379> PUBLISH c1 dfg
    (integer) 1

    窗口3(订):
    127.0.0.1:6379> PSUBSCRIBE c*
    Reading messages… (press Ctrl-C to quit)
    1) “psubscribe”
    2) “c*”
    3) (integer) 1
    1) “pmessage”
    2) “c*”
    3) “c2”
    4) “hello”
    1) “pmessage”
    2) “c*”
    3) “c4”
    4) “hello”
    1) “pmessage”
    2) “c*”
    3) “c1”
    4) “dfg”

    使用Java程序实现消息的发布与订阅
    需要继承JedisPubSub类

    @Test
    public void testMessage(){
    Jedis jedis = new Jedis("192.168.116.121", 6379);
    //subscribe和psubcribe不能同时订阅
    jedis.subscribe(new MyListener(), "channel");
    //jedis.psubscribe(new MyListener(), "channel*");
    }
    class MyListener extends JedisPubSub{
    public void onMessage(String channel, String message){
    System.out.println("onMessage channel is " + channel + " message is " + message);
    }
    public void onPMessage(String pattern, String channel, String message){
    System.out.println("onPMessage channel is " + pattern);
    System.out.println("onPMessage channel is " + channel);
    System.out.println("onPMessage message is " + message);
    }
    public void onPSubscribe(String arg0, int arg1){}
    public void onPUnsubscribe(String arg0, int arg1){}
    public void onSubscribe(String arg0, int arg1){}
    public void onUnsubscribe(String arg0, int arg1){}
    }
    7、Redis持久化

    本质:备份和恢复
    1)RDB快照:默认
    (*)看成一种快照,备份。每隔段时间,将内存汇总的数据保存到硬盘上。产生RDB文件

    (*)RDB 生成策略
    redis.conf中
    147 save 900 1 900秒内,有1个key发生变化,执行RDB
    148 save 300 10 300内,如果有10个key发生变化,执行RDB
    149 save 60 10000 60秒内,如果有10000个key发生变化,执行RDB

    save —- 时间 —– 发生变化的key的个数

    (*)其他参数
    164 stop-writes-on-bgsave-error yes 当后台写进程出错时,禁止写入新的数据

    170 rdbcompression yes 是否压缩。如果看重性能,设置成no
    压缩会节省空间,但会影响备份和恢复性能

    182 dbfilename dump.rdb RDB的文件名字
    192 dir ./ RDB的文件地址

    (*)RDB的优点和缺点
    优点:快,恢复速度快
    缺点:在两次RDB之间,可能会造成数据的丢失
    解决:AOF

    2)AOF日志
    客户端在操作Redis时,把操作记录到文件中,如果发生崩溃,读取日志,把操作完全执行一遍

    (*)默认是禁用
    509 appendonly no 参数修改成yes

    (*)AOF记录策略
    538 # appendfsync always 每条操作都记录日志:优点安全 缺点:慢
    539 appendfsync everysec 每秒写入一次
    540 # appendfsync no 由操作系统来决定记录日志的方式。不会用的到。

    (*)AOF日志重写:overwrite
    举例:
    set money 0
    incr money
    ..100次

    set money 100

    ./redis-benchmark -n 100000
    模拟客户端100000次请求

    (*)参数设置
    561 no-appendfsync-on-rewrite no 执行重写的时候,不写入新的aof日志
    //561 no-appendfsync-on-rewrite yes 生成rdb的时候,是否不写入aof
    580 auto-aof-rewrite-percentage 100 aof文件比上次重写时,超过的百分比
    581 auto-aof-rewrite-min-size 64mb 执行重写的文件大小。到64M触发重写

    3)当两个同时存在时,优先执行哪个?
    504 # If the AOF is enabled on startup Redis will load the AOF, that is the file
    505 # with the better durability guarantees.

    AOF开启时,优先使用AOF

    8、Redis的主从复制

    1)Redis主从复制集群
    作用:
    主从复制,主从备份,防止主节点down机
    任务分离:分摊主节点压力。读写分离

    Memcacached:主主复制
    Redis:主从复制

    Redis集群两种部署方式
    星型模型
    优点:效率高,两个slave地位一样,可以直接从主节点取出信息
    缺点:HA比较麻烦

    线性模型
    优点:HA简单,master宕机后,可以直接切换到slave1
    缺点:效率不如星型模型

    cp redis.conf redis6379.conf
    cp redis.conf redis6380.conf
    cp redis.conf redis6381.conf

    主节点(redis6379.conf ):关闭rdb aof
    509 appendonly no
    147 #save 900 1
    148 #save 300 10
    149 #save 60 10000

    从节点(redis6380.conf redis6381.conf)
    不同机器有的可以不改
    改端口号
    50 port 6380
    改aof rdb文件名:
    182 dbfilename dump6380.rdb
    211 slaveof 192.168.116.121 6379
    513 appendfilename “appendonly6380.aof”

    改端口号
    50 port 6381
    改aof rdb文件名:
    182 dbfilename dump6381.rdb
    211 slaveof 192.168.116.121 6379
    513 appendfilename “appendonly6381.aof”

    []root@hsiehchou121 redis]# ./bin/redis-server ./conf/redis6379.conf
    [root@hsiehchou121 redis]# ./bin/redis-server ./conf/redis6380.conf
    [root@hsiehchou121 redis]# ./bin/redis-server ./conf/redis6381.conf

    [root@hsiehchou121 redis]# ps -ef | grep redis
    root 6371 1 1 16:31 ? 00:00:00 ./bin/redis-server *:6379
    root 6375 1 4 16:31 ? 00:00:01 ./bin/redis-server *:6380
    root 6381 1 0 16:31 ? 00:00:00 ./bin/redis-server *:6381
    root 6395 5432 0 16:31 pts/0 00:00:00 grep –color=auto redis

    [root@hsiehchou121 redis]# ./bin/redis-cli -p 6379
    127.0.0.1:6379> set tom 10000
    OK
    127.0.0.1:6379> quit
    [root@hsiehchou121 redis]# ./bin/redis-cli -p 6380
    127.0.0.1:6380> get tom
    “10000”

    默认情况下,从节点只读,不可以写入数据

    注意:一次性启动从节点不要太多

    Redis 主从服务的通信原理

    Redis 主从服务的通信原理

    2)Redis的分片
    多个从节点分摊读的压力

    客户端代理分片工具:Twemproxy

    解压
    [root@hsiehchou121 nutcracker-0.3.0]# ./configure --prefix=/root/hd/nutcracker
    [root@hsiehchou121 nutcracker-0.3.0]# make
    [root@hsiehchou121 nutcracker-0.3.0]# make install

    cp /root/hd/nutcracker-0.3.0/conf/nutcracker.yml ./conf/

    修改server信息

    alpha:
    listen: 127.0.0.1:22121
    hash: fnv1a_64
    distribution: ketama
    auto_eject_hosts: true
    redis: true
    server_retry_timeout: 2000
    server_failure_limit: 1
    servers:
    - 192.168.116.121:6380:1
    - 192.168.116.121:6381:1

    启动redis
    [root@hsiehchou121 redis]# ./bin/redis-server ./conf/redis6379.conf
    [root@hsiehchou121 redis]# ./bin/redis-server ./conf/redis6380.conf
    [root@hsiehchou121 redis]# ./bin/redis-server ./conf/redis6381.conf

    检查配置文件是否正确
    [root@hsiehchou121 nutcracker]# ./sbin/nutcracker -t conf/nutcracker.yml

    启动代理分片
    [root@hsiehchou121 nutcracker]# ./sbin/nutcracker -d -c conf/nutcracker.yml

    [root@hsiehchou121 redis]# ./bin/redis-cli -p 22121 访问

    9、Redis的HA(哨兵机制)

    主从结构,存在单点故障问题

    redis2.4版本之后有

    redis-sentinel 就是哨兵

    vi redis6380.conf
    211 slaveof 192.168.116.121 6379

    vi redis6381.conf
    211 slaveof 192.168.116.121 6379

    配置
    cp /root/hd/redis-3.0.5/sentinel.conf ./conf/

    vim sentinel.conf

    53 sentinel monitor mymaster 192.168.116.121 6379 1

    [root@hsiehchou121 redis]# ./bin/redis-sentinel conf/sentinel.conf

    看日志:
    3085:X 23 Apr 17:04:17.522 # +monitor master mymaster 192.168.116.121 6379 quorum 1
    3085:X 23 Apr 17:04:18.524 * +slave slave 192.168.116.121:6380 192.168.116.121 6380 @ mymaster 192.168.116.121 6379
    3085:X 23 Apr 17:04:18.526 * +slave slave 192.168.116.121:6381 192.168.116.121 6381 @ mymaster 192.168.116.121 6379

    kill master 检测到
    看日志:
    try-failover master mymaster 192.168.109.134 6379
    检测到6379挂了

    3085:X 23 Apr 17:05:14.647 # +selected-slave slave 192.168.116.121:6381 192.168.116.121 6381 @ mymaster 192.168.116.121 6379
    select slave 选举新的主节点

    3085:X 23 Apr 17:05:16.830 * +slave slave 192.168.116.121:6380 192.168.116.121 6380 @ mymaster 192.168.116.121 6381
    把其他的从节点连接到主节点上

    注意:一定要按步骤来,一步一步配置

    亲测排坑
    划重点
    此处的Redis的HA高可用的redis主节点和从节点的变化会导致sentinel monitor mymaster(sentinel.conf的第53行)和slaveof一起变化(从节点的第211行)。而且这个过程是不可逆的,就是更新了变只有自己手动去修改下。

    所以,如果停止重新运行,便会报错,需要自己自行修改这些内容。

  • 相关阅读:
    python virtualenv
    ICMP
    正则表达式
    tcpdump命令
    vim命令
    IP网际协议
    链路层
    python模块学习 logging
    Angular2+如何去除url中的#
    angular5懒加载之模块划分
  • 原文地址:https://www.cnblogs.com/hsiehchou/p/10757877.html
Copyright © 2020-2023  润新知