(转)GoF的23个经典设计模式

目录
  1. 概述
  2. 创建型
  3. 结构型
  4. 行为型

img

以文本和思维导图的方式简明扼要的介绍了GoF的23个经典设计模式,可当成学习设计模式的一个小手册,偶尔看一下,说不定会对大师的思想精髓有新的领悟。

GoF(“四人帮”,又称Gang of Four,即Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides)

概述

创建型模式(5个):单例模式、原型模式、建造者模式、工厂模式、抽象工厂模式。

结构型模式(7个):桥接模式、外观模式、组合模式、装饰模式、适配器模式、代理模式、享元模式。

行为型模式(11个):迭代器模式、解释器模式、观察者模式、中介者模式、访问者模式、备忘录模式、状态模式、策略模式、模版方法模、命令模式、职责链模式式。

创建型

  • Singleton(单例模式):保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  • Prototype(原型模式):用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。

  • Builder(建造者模式):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

  • Factory Method(工厂模式):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。

  • Abstract Factory(抽象工厂模式):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

结构型

  • Bridge(桥接模式):将抽象部分与它的实现部分分离,使它们都可以独立地变化。

  • Facade(外观模式):为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

  • Composite(组合模式):将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。

  • Decorator(装饰模式):动态地给一个对象添加一些额外的职责。就扩展功能而言, 它比生成子类方式更为灵活。

  • Adapter(适配器模式):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

  • Proxy(代理模式):为其他对象提供一个代理以控制对这个对象的访问。

  • Flyweight(享元模式):运用共享技术有效地支持大量细粒度的对象。

行为型

  • Iterator(迭代器模式):提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

  • Interpreter(解析器模式):给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。

  • Observer(观察者模式):定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。

  • Mediator(中介者模式):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

  • Visitor(访问者模式):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

  • Memento(备忘录模式):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。

  • State(状态模式):允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。

  • Strategy(策略模式):定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。

  • Template Method(模板方法模式):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

  • Command(命令模式):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。

  • Chain of Responsibility(职责链模式):为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。 将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。

转载链接:GoF的23个经典设计模式

syslog日志

目录
  1. syslog日志格式
  2. TLS编程
    1. 小贴士

syslog日志格式

header(信息长度mes_len) “SYSLOGPRO_VERSION TIMESTAMP FQDN/IP APP_NAME PROC_ID(pid) MSGID STRUCTURED_DATA BOM(\xEF\BB\xBF) MSG”

  • header: “”里面的信息长度
  • PRI=logFacility * 8 + logLevel
  • SYSLOGPRO_VERSION: syslog protocol version(such as: 1)
  • TIMESTAMP:时间戳 如,2017-01-01T00:00:00.000000+02:00 (最多精确到us)
  • STRUCTURED_DATA:结构化数据
  • BOM:指出编码为UTF8
  • MSG:信息主体

TLS编程

利用openssl库提供的ssl编程接口,可以容易实现TLS交互,其客户端编程步骤如下:

  1. 创建socket,调用connect连接服务端
  2. 调用SSL_CTX_new创建SSL context,调用SSL_set_verify设置证书校验回调函数,可以定制一些特殊的校验场景;SSL_set_cipher_list设置客户端的加密套件等操作
  3. 调用SSL_new创建SSL结构,调用SSL_set_fd,SSL_connect进行TLS握手,然后调用 SSL_set_connect_state 设置ssl工作在客户端模式
  4. 调用SSL_read/SSL_write进行读写操作
  5. 交互完成后,调用SSL_shutdown、SSL_free等进行清理工作

详细例子可以参考openssl中的apps/s_server.c s_client.c的实现


小贴士

如何设置使用TLS的版本?
Ans: If you only want to allow TLSv1.2 and TLSv1.0 handshakes you will need to try a connection with the TLSv1_2_client_method and then by TLSv1_client_method. You can also use SSLv23_client_method to negotiate only TLSv1.2 and TLSv1.0 using an SSLv2 handshake by explicitly disabling SSLv2, SSLv3 and TLSv1.1 using SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1_1).

visual studio快捷键

目录
  • ctrl+’-‘:回到上一个位置,相当于vim的ctrl+’o’
  • ctrl+shift+’-‘:回到之后的位置,ctrl+’-‘的逆操作
  • F12:转到定于
  • 光标停留在需要查找的词上面,使用快捷键ctrl+F3可以跳转到下一个相同的词;使用shift+F3可以往上查找

yaa and lex

目录

lex就是语法扫描器,yacc就是语法分析器。
lex 源文件中的yywrap函数是必须的,因为给出了这个函数实现之后可以不需要依赖flex库了。
yywrap直接返回1,表示输入已经结束了。
基本的lex文件:

1
%{ int yywrap(void); %} %% %% int yywrap(void) { return 1; }

基本的yacc文件:

1
2
%{ void yyerror(const char *s); %} %% program: ; %% void yyerror(const char *s) { } int main() %% {
yyparse(); return 0; }

yacc文件中的代码被%%分成3个部分,第一部分表示要写入到c文件的c/c++代码,由%{和%}括起来;中间部分是yacc的语法规则;第三部分是可以直接写入c/c++代码,不需要任何的修饰。

tcp_keepalive编程

KeepAlive机制中设置的参数包含如下:

  • SO_KEEPALIVE 设置套接字的keepalive状态
  • TCP_KEEPIDLE 关闭一个非活跃连接之前进行探测的最大时长,默认值为2h
  • TCP_KEEINTVL 两次探测的时间间隔,默认值为150s,即每次间隔75s
  • TCP_KEEPCNT 关闭一个非活跃连接之前进行的最大操作次数,默认值为8
    这些默认值位于/proc/sys/net/ipv4目录下。
    程序实现时可以通过setsockopt函数进行相关设置,代码段如下:
//设置socket为TCP_KEEPALIVE
int keepAlive = 1;
setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
int keepIdle = 6;
int keepInterval = 5;
int keepCount = 3;
setsockopt(listenfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));
setsockopt(listenfd, SOL_TCP,TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval)); 
setsockopt(listenfd,SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

tcp释放连接的close_wait, FIN_WAIT2, TIME_WAIT大量存在的原因

目录
  1. 存在close_wait的原因
  2. 存在FIN_WAIT2的原因
  3. 存在TIME_WAIT的原因

存在close_wait的原因

close_wait这个状态存在于服务端,当服务端发送FIN(之前客户端已经发送过fin),请求关闭连接之后进入close_wait,然而没有收到客户端的响应,可能由于客户端掉线了(如网络故障或者掉电),没有及时给予客户端回复造成问题。
或者由于客户端已经调用close(socket)退出,而服务端对其监测并断开连接,这种是服务端问题。
解决方法:一般是编程问题,可用keep_alive机制加以解决

存在FIN_WAIT2的原因

这个状态存在于主动发起断开请求的一端,如果服务器存在大量的这个状态,那么这个服务器就充当客户端的角色,如网络爬虫,出现的原因是由于客户端发起FIN请求结束连接之后,收到了服务端的应答之后进入FIN_WAIT2,之后就没收到服务端发送的FIN信号导致。
解决方法:可以配置FIN_WAIT2的时长,当超过时长后自动断开加以解决

存在TIME_WAIT的原因

TIME_WAIT状态存在有两个原因:其一是响应服务端发送的FIN报文,保证服务端断开连接;其二是保证之前请求断开连接的请求,由于网络原因滞留在网络中,后续又到达了,导致后面重新建立的连接断开。
解决方法:设置TIME_WAIT状态的等待时间规避。即设置/proc/sys/net/ipv4下的相关参数

信号处理机制

目录
  1. 常用信号
  2. 信号集
  3. 信号处理函数
  4. 阻塞信号
  5. 产生信号函数
  6. 睡眠函数及定时器
  7. 线程与信号

常用信号

信号定义在文件中,可以通过在shell下键入kill –l查看信号列表,或者man 7 signal查看说明。
不能忽略的信号:SIGKILL及SIGSTOP 这两个信号是不能忽略的,即进程接收到这两个信号后,只能接受系统的默认处理,及终止进行。
中断信号:SIGINT(ctrl+c触发),SIGQIUT(ctrl+\触发)

信号集

POSIX.1 定义数据类型sigset_t用于表示一个信号集,并且定义了以下的一组信号能对sigset_t结构体进行处理的函数,用于初始化及增删信号集:

int sigemptyset(sigset_t *set);      //清空信号集合set
int sigfillset(sigset_t *set);      //将所有信号填充进set中
int sigaddset(sigset_t *set, int signum);    //往set中添加信号signum
int sigdelset(sigset_t *set, int signum);    //从set中移除信号signum
int sigismember(const sigset_t *set, int signum); //判断signnum是不是包含在set中
int sigpending(sigset_t *set);  //返回一信号集

信号处理函数

  1. signal
    其不能处理当正在执行信号处理函数期间又触发相同信号的情况。
    对应的信号处理函数:void handler(int)

  2. sigaction
    其为signal函数的升级版,此函数取代了unix早期版本使用的signal函数。其原型如下:
    int sigaction(int signo, const struct sigaction *act, struct sigaction *oldact);
    其中,signo是要检测或修改其具体动作的信号编号,act指针是需要设置的动作参数,oact参数用于存放该信号的上一个动作。其中struct sigation定义如下:

struct  sigaction {
             void (*sa_handler)(int);  /* signal handler */
             void (*sa_sigaction)(int, siginfo_t *, void *);
             sigset_t sa_mask;               /* signal mask to apply */
             int     sa_flags;               /* see signal options below */
     };

其中,sa_handler和sa_sigaction为信号处理函数。通常,使用sa_handler作为信号处理函数,如果在sigaction结构中使用SA_SIGINFO标志,则使用sa_sigaction作为信号处理程序为。有的系统把sa_handler和sa_sigaction用一个union表示(如mac os),所以应用只能一次使用这两个字段中的一个。sa_handler和sa_sigaction的原型如下:
void sa_handler(int)
void sa_sigaction (int iSignNum, siginfo_t *pSignInfo, void *pReserved);
对应的三个参数含义为:

  • iSignNum: 传入的信号
  • pSignInfo: 与信号相关的一些信息
  • pReserved: 保留
    sa_mask字段为信号屏蔽字,其指出来在调用该信号捕捉程序之前,这一信号集要加到进程的信号屏蔽字中;仅当从信号捕捉函数返回时再将进程的信号屏蔽字恢复为原先值。
    一旦对给定的信号设置一个动作,那么在调用sigaction显式地改变它之前,该设置就一直有效,这种方式与早期的不可靠信号机制不同,符合POSIX.1的要求。
    sa_flags指定对信号进行处理的各个选项,如SA_INTERRUPT(由信号中断的系统调用不自动重启),SA_RESTART(信号中断的系统调用自动重启),SA_SIGINFO(使用si_sigation处理函数,支持附加siginfo信息)
    其中,几个常见的选项描述如下:
  • SA_RESETHAND 处理完毕要捕捉的信号后,将自动撤消信号处理函数的注册,即必须再重新注册信号处理函数,才能继续处理接下来产生的信号。该选项不符合一般的信号处理流程,现已经被废弃。
  • SA_NODEFER 在处理信号时,如果又发生了其它的信号,则立即进入其它信号的处理,等其它信号处理完毕后,再继续处理当前的信号,即递规地处理。如果sa_flags包含了该掩码,则结构体sigaction的sa_mask将无效!
  • SA_RESTART 如果在发生信号时,程序正阻塞在某个系统调用,例如调用read()函数,则在处理完毕信号后,接着从阻塞的系统返回。该掩码符合普通的程序处理流程,所以一般来说,应该设置该掩码,否则信号处理完后,阻塞的系统调用将会返回失败!
  • SA_SIGINFO 指示结构体的信号处理函数指针是哪个有效,如果sa_flags包含该掩码,则sa_sigactiion指针有效,否则是sa_handler指针有效。

    阻塞信号

  1. sigaction阻塞
    函数sigaction中设置的被阻塞信号集合只是针对于要处理的信号,例如
struct sigaction act;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGQUIT);
sigaction(SIGINT,&act,NULL);

表示只有在处理信号SIGINT时,才阻塞信号SIGQUIT;

  1. sigprocmask阻塞
    函数sigprocmask是全程阻塞,在sigprocmask中设置了阻塞集合后,被阻塞的信号将不能再被信号处理函数捕捉,直到重新设置阻塞信号集合。其原型为:
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    参数how的值为如下3者之一:
  • SIG_BLOCK: 将参数2的信号集合添加到进程原有的阻塞信号集合中
  • SIG_UNBLOCK: 从进程原有的阻塞信号集合移除参数2中包含的信号
  • SIG_SET: 重新设置进程的阻塞信号集为参数2的信号集
    参数set为阻塞信号集
    参数oldset是传出参数,存放进程原有的信号集。

产生信号函数

  • kill
    int kill(pid_t pid, int sig);
    用于将信号发送给进程或进程组.根据pid的参数设置,kill有以下4种不同的情况:
    1、 pid > 0 将信号发送给进程ID为pid的进程
    2、 pid == 0 将信号发送给与发送进程同一个进程组的所有进程
    3、 pid == -1 将信号发送给发送进程有权限向它们发送信号的所有进程
    4、 pid < 0 将信号发送给其进程组ID等于pid绝对值,而且发送进程具有权限向其发送信号的所有进程。
    信号编号为0的信号为空信号。如果signo为0,则kill仍执行正常的错误检查,但不发送信号,这常被用来确定一个特点进程是否仍然存在。如果向一个并不存在的进程发送信号,则kill返回-1,errno被设置为ESRH.
    返回值:成功返回0,否则为-1
  • raise
    int raise(int signo);
    等价于 kill(getpid(), signo) 即进程向自身发送信号。
  • alarm
    unsigned int alarm(unsigned int seconds);
    seconds指定经过多少秒产生SIGALRM信号;alarm函数可以设置一个定时器,在将来的某个时刻该定时器会超时,产生SIGALRM信号,如果忽略或不捕捉该信号,则其默认动作是终止调用alarm函数的进程。
    如果seconds值为0,则取消以前的闹钟时间,其余留值作为alarm函数的返回值。
  • sigqueue
    sigqueue也可以发送信号,并且能传递附加的信息
    int sigqueue(pid_t pid, int sig, const union sigval value);
    其中value为一整型与指针类型的联合体
union sigval {
int sival_int;
void *sival_ptr;
};

由sigqueue函数发送的信号的第3个参数value的值,可以被进程的信号处理函数的第2个参数info->si_ptr接收到。
附注:
siginfo_t结构体可以通过man sigaction查看其定义。

另外,pause函数可以使调用进程挂起直至捕获到一个信号。

睡眠函数及定时器

  1. sleep类函数
    sleep及usleep 前一个是以秒为单位,后一个是以毫秒为单位
    nanosleep 和sleep类似,但提高了纳秒级的精度
    其中sleep是采用信号机制进行处理的
    用到的函数有:
    unsigned int alarm(unsigned int seconds);
    int pause();
    因此:不要将alarm和sleep混用,不然会出现问题。而nanosleep则没有这个问题。
  2. 定时器
    Linux下的计时器
    Linux为每个进程维护3个计时器,分别是真实计时器、虚拟计时器和实用计时器。
  • 真实计时器(ITIMER_REAL)计算的是程序运行的实际时间;
  • 虚拟计时器(ITIMER_VITUAL)计算的是程序运行在用户态时所消耗的时间(可认为是实际时间减掉(系统调用和程序睡眠所消耗)的时间);
  • 实用计时器(ITIMER_PROF)计算的是程序处于用户态和处于内核态所消耗的时间之和。
    发送的信号分别为SIGALRM,SIGVTALRM,SIGPROF
    可结合setitimer函数来实现自己的定时器,但如果需要实现多个定时器的话,需要结果list结构来实现,可以参考用setitimer实现多个定时器

    线程与信号

    每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着当某个线程改变了某个给定信号的相关处理行为时,所有线程都必须共享这个处理行为的改变,单个线程可以通过设置自己的信号屏蔽字来达到屏蔽各自不需要的信号。
    线程中使用pthread_sigmask来设置线程的信号屏蔽字,其用法同sigprocmask,但其失败时返回错误码,不再像sigprocmask中那样设置errno并返回-1.
    线程可以使用sigwait来等待一个或多个信号的出现。其原型如下:
    int sigwait(const sigset_t * restrict set, int *restrict signop);
    其中,set参数指定了线程等待的信号集;signop返回接收的信号。函数成功时返回0;错误时返回正的错误编号.
    在sigwait返回前,sigwait将从进程中移除那些处于挂起等待的信号;如果具体实现支持信号排队,那么sigwait将会移除该信号的一个实例,其余的实例还要继续排队。
    为了避免错误发生,线程在调用sigwait之前,必须阻塞(通过调用pthread_sigmask)那些它正在等待的信号。sig_wait会原子性地取消信号集地阻塞状态,直到接收到新的信号。在返回之前,sigwait将恢复线程的信号屏蔽字。这样不这样做,那么在线程完成对sigwait调用之前的时间窗中,信号就可以发送给该线程,从而造成sig_wait长时间阻塞不返回。
    如果多个线程在sigwait的调用中等待同一个信号,那么在信号递送的时候,就只有一个线程可以从sigwait中返回(类似于条件变量的pthread_cond_wait)。如果这时即有sigwait调用,又有sigaction调用,那么将由操作心态决定是让sigwait返回,还是调用sigaction信号处理函数,但两者不会同时发生。
    另外,可以通过调用pthread_kill将信号发送给指定pthread_t tid的线程,支持传递signo = 0的信号用来检查线程是否存在。

Redis讲解

目录
  1. Redis数据结构及操作
    1. string
    2. hash
    3. list
    4. set
    5. sorted sets
  2. Redis常用命令
    1. 键值相关命令
    2. 服务器相关命令
  3. Redis高级实用命令
    1. 安全性
    2. 主从复制
      1. 主从复制特点
      2. 主从复制过程
      3. 主从复制配置
    3. 事务控制
    4. 持久化机制
      1. snapshotting方式
    5. aof方式
    6. 发布及订阅消息
    7. Pipeline 批量发送请求
    8. 虚拟内存的使用

Redis数据结构及操作

string

  • set/setnx
  • get
  • incr/incrby
  • decr/decrbys
  • setex: set value with expire time
  • setrange: 设置指定 key 的 value 值的子字符串。
  • getrange: 获取指定 key 的 value 值的子字符串。
  • mset/msetnx: 一次设置多个key的值
  • mget
  • getset:设置key的值,并返回key的旧值
  • append: 给指定 key 的字符串值追加 value,返回新字符串值的长度。
  • strlen: 取指定 key 的 value 值的长度。

hash

  • hset/hsetnx
  • hmset: 同时设置 hash 的多个 field
  • hget/hmget
  • hincrby
  • hexists: 测试指定 field 是否存在
  • hlen: 返回指定 hash 的 field 数量
  • hdel: 返回指定 hash 的 field 数量
  • hkeys: 返回 hash 的所有 field
  • hvals: 返回 hash 的所有 value
  • hgetall: 获取某个 hash 中全部的 filed 及 value

    list

    list支持头部和尾部插入元素,也支持头尾删除元素,可以用作栈和队列

  • lpush: left push, 在 key 对应 list 的头部添加字符串元素

  • lpop: lpop
  • lrang: 取某个范围的值
  • rpush: right push, 在 key 对应 list 的尾部添加字符串元素
  • linset: 在 key 对应 list 的特定位置之前或之后添加字符串元素
  • lset: 设置 list 中指定下标的元素值(下标从 0 开始)
  • lrem: 从 key 对应 list 中删除 count 个和 value 相同的元素。 count>0 时,按从头到尾的顺序删除; count<0 时,按从尾到头的顺序删除; count=0 时,删除全部
  • lpop: 从 list 的头部删除元素,并返回删除元素
  • rpop: 从 list 的尾部删除元素,并返回删除元素
  • rpoplpush: 从第一个 list 的尾部移除元素并添加到第二个 list 的头部,最后返回被移除的元素值,整个操 作是原子的.如果第一个 list 是空或者不存在返回 nil
  • lindex: 返回名称为 key 的 list 中 index 位置的元素
  • llen: 返回 key 对应 list 的长度

set

Redis 的 set 是 string 类型的无序集合。set 元素最大可以包 (2 的 32 次方)个元素。set中的元素时唯一的

  • sadd: 向名称为 key 的 set 中添加元素
  • smembers: 查看set中的元素
  • srem: 删除名称为 key 的 set 中的元素 member
  • spop: 随机返回并删除名称为 key 的 set 中一个元素
  • sdiff: 返回所有给定 key 与第一个 key 的差集
  • sdiffstore: 返回所有给定 key 与第一个 key 的差集,并将结果存为另一个 key
  • sinter: 返回所有给定 key 的交集
  • sinterstore: 返回所有给定 key 的交集,并将结果存为另一个 key
  • sunion: 返回所有给定 key 的并集
  • sunionstore: 返回所有给定 key 的并集,并将结果存为另一个 key
  • smove: 从第一个 key 对应的 set 中移除 member 并添加到第二个对应 set 中
  • scard: 返回名称为 key 的 set 的元素个数
  • sismember: 测试 member 是否是名称为 key 的 set 的元素
  • srandmember: 随机返回名称为 key 的 set 的一个元素,但是不删除元素

sorted sets

sorted sets即有序的set,类似stl中的set概念。

  • zadd: 向名称为 key 的 zset 中添加元素 member,score 用于排序。如果该元素已经存在,则根据 score 更新该元素的顺序
  • zrange: 用于查看元素
  • zrem: 删除名称为 key 的 zset 中的元素 member
  • zincrby: 如果在名称为 key 的 zset 中已经存在元素 member,则该元素的 score 增加 increment;否则 向集合中添加该元素,其 score 的值为 increment
  • zrank: 返回名称为 key 的 zset 中 member 元素的排名(按 score 从小到大排序)即下标
  • zrevrank: 返回名称为 key 的 zset 中 member 元素的排名(按 score 从大到小排序)即下标
  • zrevrange: 返回名称为 key 的 zset(按 score 从大到小排序)中的 index 从 start 到 end 的所有元素
  • zrangebyscore: 返回集合中 score 在给定区间的元素
  • zcount: 返回集合中 score 在给定区间的数量
  • zcard: 返回集合中元素个数
  • zscore: 返回给定元素对应的 score
  • zremrangebyrank: 删除集合中排名在给定区间的元素
  • zremrangebyscore: 删除集合中 score 在给定区间的元素

这些操作,只要你键入操作名后redis都会提示怎么用。如下图:

图片名称

Redis常用命令

键值相关命令

  • keys: 返回满足给定 pattern 的所有 key (使用”keys *”可以查看redis中的所有key)
  • exists: 返回满足给定 pattern 的所有 key
  • del: 删除一个 key
  • expire: 设置一个 key 的过期时间(单位:秒)
  • select: 选择数据库
  • move: 将当前数据库中的 key 转移到其它数据库中
  • persist: 移除给定 key 的过期时间(将key设置为过期)
  • randomekey: 随机返回 key 空间的一个 key
  • rename: 重命名 key
  • type: 返回值的类型

    服务器相关命令

  • ping: 测试连接是否存活
  • echo: 在命令行打印一些内容
  • select: 选择数据库。Redis 数据库编号从 0~15,我们可以选择任意一个数据库来进行数据的存取。
  • quit: 退出连接
  • dbsize: 返回当前数据库中 key 的数目
  • info: 获取服务器的信息和统计
  • monitor: 实时转储收到的请求
  • config get: 获取服务器配置信息
  • flushdb: 删除当前选择数据库中的所有 key
  • flushall: 删除所有数据库中的所有 key

Redis高级实用命令

安全性

在redis 配置文件中开启配置: masterauth <master-password>

主从复制

主从复制特点

  • master 可以拥有多个 slave
  • 多个 slave 可以连接同一个 master 外,还可以连接到其他 slave
  • 主从复制不会阻塞 master,在同步数据时,master 可以继续处理 client 请求
  • 提高系统的伸缩性

    主从复制过程

    当配置好 slave 后,slave 与 master 建立连接,然后发送 sync 命令。无论是第一次连接还是重新连接,master 都会启动一个后台进程,将数据库快照保存到文件中,同时 master 主进 程会开始收集新的写命令并缓存。后台进程完成写文件后,master 就发送文件给 slave,slave 将文件保存到硬盘上,再加载到内存中,接着 master 就会把缓存的命令转发给 slave,后续 master 将收到的写命令发送给 slave。如果 master 同时收到多个 slave 发来的同步连接命令, master 只会启动一个进程来写数据库镜像,然后发送给所有的 slave。

    主从复制配置

  1. 只需在slave的配置文件中添加
    # slaveof <masterip> <masterport>
    如,slaveof 192.168.1.1 6379
  2. 修改主从redis server的启动端口为不同的值,分别启动master和slave server。
  3. 分别连接主从 redis server进行操作,可见数据是同步的
    tips:
  • client端使用info命令,查看其输出的role值可知连接的是master还是slave。如下图:
    图片名称
    同时还有一个 master_link_status 用于标明主从是否异步,如果此值=up,说明同步正常;如果此值=down, 说明同步异步;
    db0:keys=1,expires=0,avg_ttl=0, 用于说明数据库有几个 key,以及过期 key 的数量。

事务控制

redis 对事务的支持目前还比较简单。redis 只能保证一个 client 发起的事务中的命令可以连 续的执行,而中间不会插入其他 client 的命令。 由于 redis 是单线程来处理所有 client 的请 求的所以做到这点是很容易的。一般情况下 redis 在接受到一个 client 发来的命令后会立即 处理并 返回处理结果,但是当一个 client 在一个连接中发出 multi 命令有,这个连接会进入 一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此连接 受到 exec 命令后,redis 会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到 一起返回给 client.然后此连接就 结束事务上下文。

  1. 简单的事务控制
    使用multi + command1 + … + commandN + exec, 可以将多个命令一起发送到redis server,然后一起返回结果。这记为一个事务
  2. 取消事务
    通过在执行exec前调用discard可以取消事务
  3. 通过watch(乐观锁)控制事务
    在多个session同时操作数据库中的数据时,可以用watch对操作字段加锁来实现事务同步。其操作原理如下
session 1 session 2
redis 127.0.0.1:6379> get age
“10”
redis 127.0.0.1:6379> watch age
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> set age 30
OK
redis 127.0.0.1:6379> get age
“30”
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> set age 20
QUEUED
redis 127.0.0.1:6379> exec
(nil)
redis 127.0.0.1:6379> get age
“30”
redis 127.0.0.1:6379>

从以上实例可以看到在
第一步,Session 1 还没有来得及对 age 的值进行修改
第二步,Session 2 已经将 age 的值设为 30
第三步,Session 1 希望将 age 的值设为 20,但结果一执行返回是 nil,说明执行失败,之后 我们再取一下 age 的值是 30,这是由于 Session 1 中对 age 加了乐观锁导致的。

redis 的事务实现是如此简单,当然会存在一些问题
第一个问题是 redis 只能保证事务的每 个命令连续执行,但是如果事务中的一个命令失败了,并不回滚其他命令,比如使用的命令类型不匹配。
如:

1
2
3
redis 127.0.0.1:6379> get age
"30" redis 127.0.0.1:6379> get name "HongWan" redis 127.0.0.1:6379> multi OK redis 127.0.0.1:6379> incr age QUEUED redis 127.0.0.1:6379> incr name QUEUED redis 127.0.0.1:6379> exec 1) (integer) 31 2) (error) ERR value is not an integer or out of range
redis 127.0.0.1:6379> get age "31" redis 127.0.0.1:6379> get name "HongWan" redis 127.0.0.1:6379>

从以上操作我们可以看出由于name是非数字,不支持incr操作,导致事务失败了,但age却成功加1。 不过我现在用的redis 3.2.7版本已经没有这个问题。

持久化机制

redis 是一个支持持久化的内存数据库,也就是说 redis 需要经常将内存中的数据同步到磁盘 来保证持久化。redis 支持两种持久化方式,一种是 Snapshotting(快照)也是默认方式,另 一种是 Append-only file(缩写 aof)的方式。下面分别介绍:

snapshotting方式

这是默认的持久化方式。这种方式就是将内存中数据以snapshot方式写入到二进制文件中,默认的文件名为dump.rdb。可以通过配置自动做快照持久化的方式。redis可以配置在n秒内如果超过m个key被修改就自动做snapshot。其默认的配置如下:

1
2
3
4
################################ SNAPSHOTTING ################################
save 900 1 #900 秒内如果超过 1key 被修改,则发起快照保存
save 300 10 #300 秒内容如超过 10key 被修改,则发起快照保存
save 60 10000

snapshot过程如下:

  1. redis调用fork,创建子进程
  2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由写时拷贝原理,父子进程共享相同的物理页面。当父进程需要写操作时会重新创建副本,故子进程地址空间内的数据是fork时刻整个数据库的一个快照。
  3. 当子进程将快照写入临时文件完毕后,用临时文件替换之前的快照文件,然后子进程退出。
    p.s: redis也支持使用save或bgsave命令通知redis做一次快照操作。不过save操作是在主进程中保存快照的,这是会阻塞所有的client请求,不推荐使用。
    redis的快照文件为dump.rdb

aof方式

由于快照是在一定间隔时间做一次的,因此如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果要求应用不能丢失任何修改的话,可以采用aof持久化方式。
aof(append-only file)比snapshot具有更好的持久化性,当使用aof方式时,redis会将收到的命令通过write函数追加到文件中(默认是appendonly.aof)。当redis重启时会通过重新执行文件中保存的写命令来重建整个数据库的内容。
由于os会在内核中缓存write做的修改,所以aof文件可能不是立即写到磁盘上,也存在丢失部分修改的情况。不过我们可以通过配置文件告诉redis通过fsync函数强制写入磁盘的时机。aof方式可以通过配置appendonly yes启用,其有进行sync的时间有如下三种方式:

  • appendfsync always //收到命令立即写入磁盘,最慢,但是持久性最好
  • appendfsync everysec //默认方式,每秒钟写入磁盘一次,是持久性和性能的折中
  • appendfsync no //由操作系统选择,性能最优,但持久性没保证

aof方式的一个问题是持久化文件会越变越大(因为其为保存了每一条命令,如执行incr var100次,则要保存100条,但其实恢复状态只需set var var 100即可[假设var初值为0])。为了压缩aof的持久化文件。redis提 供了 bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令 的方式保存到临时文件中,最后替换原来的文件。具体过程如下:

  1. redis 调用 fork ,现在有父子两个进程
  2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
  3. 父进程继续处理 client 请求,除了把写命令写入到原来的 aof 文件中。同时把收到的写命 令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
  4. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然 后父进程把缓存的写命令也写入到临时文件。
  5. 现在父进程可以使用临时文件替换老的 aof 文件,并重命名,后面收到的写命令也开始 往新的 aof 文件中追加。

发布及订阅消息

redis 作为一个 pub/sub 的 server,在订阅者 和发布者之间起到了消息路由的功能。订阅者可以通过 subscribe 和 psubscribe 命令向 redis server 订阅自己感兴趣的消息类型,redis 将消息类型称为通道(channel)。当发布者通过 publish命令向redis server发送特定类型的消息时, 订阅该消息类型的全部client都会收到此消息。这里消息的传递是多对多的。一个 client 可以订阅多个 channel,也可以向多个 channel 发送消息。redis提供相关的命令如下:

  • SUBSCRIBE channel [channel …]: 用于订阅某个频道或者多个频道
  • PUBLISH channel message: 用于发布频道信息
  • PSUBSCRIBE pattern [pattern …]: 批量订阅满足某一pattern的频道,如tv*,表示以tv开头的频道

Pipeline 批量发送请求

由于redis server处理命令的速度很快,如果客户端命令多的话,可以一起打包发送给server,从而减少大部分的网络延迟,达到提高性能的目的。我们可以利用 pipeline 的方式 从 client 打包多条命令一起发出,不需要等待单条命令的响应返回,而 redis 服务端会处理 完多条命令后会将多条命令的处理结果打包到一起返回给客户端。同时,打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令 越多越好。具体多少合适需要根据具体情况测试。

java代码:

1
2
//usepipeline
private static void usePipeline() { try { ConnectionSpec spec = DefaultConnectionSpec.newSpec("192.168.115.170", 6379, 0, null); JRedis jredis = new JRedisPipelineService(spec); for (int i = 0; i < 100000; i++) { jredis.incr("test2");} jredis.quit(); } catch (Exception e) { } }
1
2
//withoutPipleline
private static void withoutPipeline() { try { JRedis jredis = new JRedisClient("192.168.115.170", 6379); for (int i = 0; i < 100000; i++) { jredis.incr("test2");} jredis.quit(); } catch (Exception e) { } }

虚拟内存的使用

redis实现了自己的虚拟内存管理。redis 没有使用操作系统提供的虚拟内存机制而是自己在实现了自己的虚拟内存机制,主要的理由有两点:

  1. 操作系统的虚拟内存是已 4k 页面为最小单位进行交换的。而 redis 的大多数对象都远小 于 4k,所以一个操作系统页面上可能有多个 redis 对象。另外 redis 的集合对象类型如 list,set 可能存在与多个操作系统页面上。最终可能造成只有 10%key 被经常访问,但是所有操作系 统页面都会被操作系统认为是活跃的,这样只有内存真正耗尽时操作系统才会交换页面。
  2. 相比于操作系统的交换方式,redis 可以将被交换到磁盘的对象进行压缩,保存到磁盘的对 象可以去除指针和对象元数据信息,一般压缩后的对象会比内存中的对象小 10 倍,这样 redis 的虚拟内存会比操作系统虚拟内存能少做很多 io 操作

参考资料:《redis实战》

Mysql基本命令

目录
  1. mysql入门
    1. 安装
    2. 启动与停止mysql服务器
    3. 连接mysql服务器
    4. 命令
    5. 相关文档

sql 中文html手册
mysql文档中心
MySQL参考手册

mysql入门

命令是接触mysql的第一扇窗户,下面介绍mac下mysql的使用

安装

brew install mysql
安装时间可能比较久,需要耐心等待

启动与停止mysql服务器

  1. 启动mysql服务:support-files/mysql.server start
  2. 停止mysql服务: support-files/mysql.server stop
  3. 查询mysql服务器状态:support-files/mysql.server status
    启动和停止成功后,terminal上会有对应的提示信息

连接mysql服务器

  1. 连接到mysql服务器
    要连接到服务器,需要使用登陆名和密码(对于密码为空的情况,则只需要用户名);如果mysql服务器运行在与客户端不同的机器上,还需要清楚mysql服务器的主机名,连接方法如下:
    shell> mysql [-h hostname] -u user [-p]
    在需要密码的情况下会弹出如下提示,让你输入密码:
    Enter password:
  2. 断开连接
    可以使用quit或exit命命进行登出

命令

  1. 选择与创建数据库
    show databases 查看服务器当前存在的数据库
    use databasename 选择数据库
    create databse databasename 创建数据库
  2. 设置登录密码
    alter user ‘root’@’localhost’ identified by ‘newpassword’ (5.7.6及以后的版本)
    set password for ‘root’@’localhost’ = password(‘newpassword’)
    其中,newpassword是我们将要设置的新密码
  3. 创建表
    create table tablename (columnname type, …);
    show tables 查看表
    describe pet 查看表信息(字段,类型等)
  4. 查询,更新,删除
    同sql语法

相关文档

详细说明可见文档:
mysql command
mysql-reference-manual

建议:

1.坚持阅读官方手册,看MySQL书籍作用不会特别大;(挑选跟工作相关的内容优先阅读,例如InnoDB存储引擎,MySQL复制,查询优化)

2.阅读官方手册,同时对阅读的内容做对应的测试;

3.结合你现在的工作内容,多实战即可;

4.外部的BLOG ,也包过我的 http://mysqlops.com 只是作为参考即可,更多要培养自己的分析思考的模式;

备注:

国内人写的MySQL书籍都不要作为重点,包过我可能2014年出版一本关于MySQL的书籍,推荐大家只作为辅助的,可以上厕所的时候看看,坚持官方手册+测试+实战+思考总结为主的模式。

作者:mysqlops
链接:https://www.zhihu.com/question/21760988/answer/19357455
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

2016年阅读的书籍列表

目录
  1. Done
  2. Reading
  3. To do
  • 2017 reading Books
  • Done

    • unix编程艺术
    • 代码大全(Code Completed)
    • 人月神话
    • 代码整洁之道
    • tomcat原理
    • progit
    • java编程思想
    • openssl编程
    • tomcat权威指南(第二版).pdf
    • Linux多线程服务端编程
    • 人人都是产品经理
    • redis实战
    • python语言及其应用 (爬虫、算法研究、架构web服务器,操纵redis等)
    • 代码大全

    Reading

    • 计算机程序的构造与解释
    • 高性能MySQL.pdf
    • 企业应用架构模式
    • 分布式系统概念与设计

    To do

    • 《Tomcat与Java Web开发技术详解(第2版)》.(孙卫琴).[PDF].&ckook
    • Linux多线程服务端编程
    • 编程珠玑
    • TCP/IP详解
    • redis实战
    • 搜索引擎原理技术与系统
    • Java Concurrency in Pratice
    • 现代操作系统(可以看看)
    • 语法分析器lex及语句输入器yacc lex与yacc

    语法分析器lex及语句输入器yacc lex与yacc

    进程通信及线程同步的方法
    线程同步:

    • 互斥量pthread_mutex_t
    • 条件变量pthread_cond_t
    • 信号量sem_t
    • 全局变量
    • 读写锁pthread_rwlock_t

    进程通信:

    • socket
    • pipe
    • 文件
    • 锁机制(文件锁,共享内存+互斥锁)
    • 共享内存
    • 信号
    • 信号量(共享内存)
    • 消息队列msg_queue

    今年提高的部分:
    python脚本 达到熟练使用
    Linux 多进程多线程编程 积累经验
    存储处理等大数据通信存储 看书,代码
    熟悉NoSQL、key-value存储原理使用、列存储
    安全漏洞发现及监测
    Reactor proactor spring模式

    2017 reading Books

    • Linux内核精髓
    • Nignx模块开发
    • Android开发
    • 分布式系统概念与设计
    本站总访问量