什么是单机 MySQL?c++
在90年代,一个网站的访问量通常都不大,用单个数据库彻底能够轻松应付。面试
Memcached(缓存)+MySQL+垂直拆分redis
MySQL 主从读写分离算法
因为数据库的写入压力增长,Memcached只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术来达到读写分离,以提升读写性能和读库的可扩展性。MySQL 的 Master-Slave 模式成为这个时候的网站标配了。数据库
分表分库+水平拆分+MySQL集群缓存
在 Memcached 的高速缓存,MySQL 的主从复制,读写分离的基础之上,这时 MySQL 主库的写压力开始出现瓶颈,而数据量的持续猛增,因为 MyISAM 使用表锁,在高并发下会出现严重的锁问题,大量的高并发 MySQL 应用开始使用 InnoDB 引擎代替MyISAM。安全
同时,开始流行使用分表分库来缓解写压力和数据增加的扩展问题。这个时候,分表分库成了一个热门技术,是面试的热门问题也是业界讨论的热门技术问题。也就在这个时候,MySQL 推出了还不太稳定的表分区,这也给技术实力通常的公司带来了但愿。虽然 MySQL 推出了 MySQL Cluster 集群,但性能也不能很好知足互联网的要求,只是在高可靠性上提供了很是大的保证。服务器
MySQL 的扩展问题网络
MySQL 数据库也常常存储一些大文本字段,致使数据库表很是的大,在作数据库恢复的时候就致使很是的慢,不容易快速恢复数据库。好比1000万 4KB 大小的文本就接近 40GB 的大小,若是能把这些数据从 MySQL 省去,MySQL 将变得很是的小。关系数据库很强大,可是它并不能很好的应付全部的应用场景。MySQL 的扩展性差(须要复杂的技术来实现),大数据下IO压力大,表结构更改困难,正是当前使用 MySQL 的开发人员面临的问题。多线程
泛指非关系数据库,随着互联网 Web2.0 网站的兴起,传统的关系数据库在应付 Web2.0,特别是超大规模、高并发的 SNS 类型的纯动态网站已经力不从心,而非关系数据库凭借自身的特色获得了迅猛的发展。NoSQL 数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤为是大数据应用难题,包括超大规模数据的存储。
大数据时代的 3V:
海量 Volume
多样 Variety
实时 Velocity
互联网需求的 3 高:
高并发
高可扩
高性能
C:强一致性(Consistency)
A:可用性(Availability)
P:分区容错性(Partition tolerance)
CAP 的理论就是说在分布式存储系统中,最多只能实现上面的两点,而因为当前的网络硬件确定会出现延迟丢包问题,因此分区容忍性是咱们必须实现的。
因此咱们只能从一致性和可用性之间进行权衡,没有 NoSQL 可以同时保证三点。
CA:传统 Oracle 数据库
AP:大多数网站架构的选择
CP:Redis、Mongodb
基本可用(Basically Available)
软状态(Soft state)
最终一致(Eventually consistent)
它的思想是经过让系统放松对某一时刻数据一致性的要求来保证总体伸缩性和性能,缘由就是大型系统每每因为地域分布和极高性能的要求,不可能采用分布式事务来完成这些指标,要想得到这些指标,咱们必须采用一种方式来完成,这里 BASE 就是解决这个问题的办法。
由多台计算机和通讯软件经过计算机网络链接组成,分布式系统是创建在网络之上的软件系统,正是由于软件的特性,因此分布式系统具备高度的内聚性和透明性,所以,网络和分布式系统之间的区别更多的在于高层软件,而不是硬件,分布式系统能够应用在不一样的平台。
分布式和集群的区别:
分布式:在多台不一样的机器上部署不一样的服务,它们之间使用 Rpc/Rml 之间通讯和调用,对外提供服务和和组内协做。
集群:在多台不一样的机器上部署相同的服务,经过分布式调用软件进行统一的调度,对外提供服务和访问。
开源、底层采用 C语言、遵照 BSD 协议。
高性能 key/value 分布式内存数据库,基于内存运行。
支持持久化的 NoSQL 数据库。
和其余 NoSQL 相比,Redis 有三个特色:
支持数据的持久化,能够将内存中的数据持久化到磁盘中,重启以后能够再次使用。
不只仅支持 key/value,还提供对 list、set、zset、hash 等数据类型的支持。
支持主从备份。
将 Redis 安装包移动到 opt 目录下。
经过 tar -zxvf redis-3.0.4.tar.gz 解压。
进入解压后的目录执行 make,假如没有安装 gcc,就执行 yum install gcc-c++ 命令,而后运行 make distclean 以后执行 make。
make install。
Redis 是一个单进程的。
它有16个数据库,默认为0号数据库。
SELECT:切换数据库。
DBSIZE:查看当前数据库的 key 的数量。
FLUSHDB:清空当前库。
FLUSHALL:清空全部库。
赞成密码管理,16个库都是相同密码。
默认端口是 6379。
keys * 查看当前库的全部 key
exists key 判断 key 是否存在
move key db 将当前库的某个 key 移到其余库
expire key 设置 key 的过时时间
ttl key 查看 key 的剩余使用实现
type key 查看 key 的类型
String 是 Redis 最基本的类型,一个 key 对应一个 vlaue,String 是二进制安全的,因此它能够包含全部数据,value 最多能够包含512M数据。
// 设置一个值 SET k1 v1 // 获取key对应的value GET k1 // 添加一个value APPEND k1 // 查看key对应的value的长度 STRLEN k1 // 每次增长1 INCR k1 // 每次减小1 DECR k1 // 每次增长2 INCRBY k1 2 // 每次减小2 DECRBY k1 2 // 获取范围内值 GETRANGE k1 0 2 // 设置键存活的时间 SETEX k1 20 v1 // 设置时判断是否存在key,若是存在就不作改变 SETNX k1 v1 // 同时设置多个值,若是键存在,修改值 MSET k1 v1 k2 v2 // 同时获取多个值 MGET k1 k2 // 同时设置多个值,若是存在键,执行失败 MSETNX k1 v1 k2 v2 // 设置并返回设置的value getset k1 v1
是一个键值对集合,相似与 Java 里的 Map<String, Object>。
KV 模式不变,但 V 是一个键值对。
// 设置一个user,user的id属性是11 HSET user id 1 // 获取 user 的 id 属性 HGET user id // 设置一个user的多个值 HMSET user name kernel age 18 // 获取多个属性 HMGET user name age // 获取全部属性 HGETALL // 删除user中的name属性 HDEL user name // 查看user有几个属性 HLEN user // 查看user中是否存在name属性 HEXISTS user name // 查看user的全部key HKEYS user // 查看user的全部value HVALS user // 自增2 HINCRBY user age 2 // 增长小数 HINCRBYFLOAT user score 0。5 // 不存在才能往里放 HSETNX user email kernel@163。com
简单的字符串列表,按照插入顺序排序,底层是一个链表。
单键多值。
它是一个字符串链表,能够从左右两边添加。
若是键不存在,建立链表,键存在,增长值,弹出全部值,键就失效了。
操做头尾效率高,中间元素效率低。
// 从左边向key中压入值 LPUSH list01 1 2 3 4 5 // 从右边向 key 中压入值 RPUSH list02 1 2 3 4 5 // 从左边查看范围内的值 Lrange list01 0 -1 // 从左边弹栈 LPOP // 从右边弹栈 RPOP // 查看指定索引的值 LINDEX list01 2 // 查看指定key的长度 LLEN list01 // 从key中从左边删除3个3 LREM list01 3 3 LTRIM list01 0 3 截取0-3赋值给key // 从前面的key右边弹出一个值赋值给后面key RPOPLPUSH list01 list02
String 类型的无序集合,采用 HashTable 实现的。
单键多值。
// 添加多个值,重复值不添加 SADD set01 1 2 3 4 5 1 2 3 4 5 // 查看set01的值 SMEMBERS set01 // 查看set01是否存在5 SISMEMBER set01 5 // 查看set01的元素个数 SCARD set01 // 删除set01中的3 SREM set01 3 // 从set01中随机取3个数 SRANDMEMBER set01 3 // 随机弹栈 SPOP set01 // 将 set01 中的5 移动到 set02 中 SMOVE set01 set02 5 // 差集,在set01不在set02中的值 SDIFF set01 set02 // 交集 SINTER set01 set02 // 并集 SUNION set01 set02
set 和 zset 的异同点:
相同点:String 类型元素的集合,不容许重复的成员。
不一样点:每一个元素都会关联一个 double 类型的分数。
// 设置值 ZADD zset01 60 v1 70 v2 80 v3 90 v4 100 v5 // 范围内取值,withscores表示包含score ZRANGE zset01 0 -1 // 范围内取值包括score // 区间内取值,(表示不包含,limit 开始下标,走多少步 ZRANGEBYSCORE zset01 60 80 // 删除某score对应的value ZREM zset01 v4 // 查看zset01的个数 ZCARD zset01 // 查看区间内个数 ZCOUNT zset01 60 90 // 取下标 ZBANK zset01 60 // 取v4对应的值 ZSCORE zset01 v4 // 逆序得到v3对应的下标 ZREVRANK zset01 v3 // 逆序取范围内的值 ZREVRANGE zset01 0 -1 // 逆序区间内取值 ZREVRANGEBYSCORE zset01 90 60
1k => 1000 bytes
1kb => 1024 bytes
1m => 1000000 bytes
1mb => 10241024 bytes
1g => 1000000000 bytes
1gb => 10241024*1024 bytes
能够经过 includes 包含,redis.conf 能够做为总闸,包含其余配置。
daemonize:配置做为守护进程运行。
pidfile:如之后台进程运行,Redis 将会把 pid 写入到 /var/run/redis.pid 文件,能够指定文件。
bind 127.0.0.1:绑定的主机地址,若是想所有访问不作限制的话,全为0.0.0.0。
timeout 300:当客户端闲置多长时间后关闭链接,若是指定为0,表示关闭该功能。
loglevel verbose:日志等级,有 debug、verbose、notice、warning,默认是 verbose。
logfile "" :日志文件保存位置,若是配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null。
tcp-keepalive:单位为秒,若是设置为0,则不会进行 Keepalive 检测,建议设置成60。
syslog-enabled:是否把日志输出到 syslog 中。
syslog-ident:指定 syslog 里的日志标志。
syslog-facility:指定 syslog 设备,值能够是USER或 LOCAL0-LOCAL7。
Save 默认出厂设置若是1分钟写了10000次、5分钟写了10次、15分钟写了1次都会触发备份,若是想禁用 RDB 持久化策略,将 save 设为 save ""。
stop-writes-on-bgsave-error:若是配置成 no,表示你不在意数据不一致或者有其余的手段发现和控制。
rdbcompression:对于存储到磁盘中的快照,能够设置是否进行压缩存储。若是是的话,Redis 会采用 LZF 算法进行压缩。若是你不想消耗 CPU 来进行压缩的话,能够设置为关闭此功能。
rdbchecksum:在存储快照后,还可让 Redis 使用 CRC64 算法来进行数据校验,可是这样作会增长大约10%的性能消耗,若是但愿获取到最大的性能提高,能够关闭此功能。
dbfilename
dir
CONFIG SET requirepass 123456 将密码改成 123456,默认为空,设置后,执行任何命令以前必须执行 auth 123456。
maxclients 最大链接客户端数量
maxmemory 设置 Redis 可使用的内存量。一旦到达内存使用上限,Redis 将会试图移除内部数据。
maxmemory-policy :
volatile-lru:使用 LRU 算法移除 key,只对设置了过时时间的键。
allkeys-lru:使用 LRU 算法移除 key。
volatile-random:在过时集合中移除随机的 key,只对设置了过时时间的键。
allkeys-random:移除随机的 key。
volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过时的 key。
noeviction:不进行移除。针对写操做,只是返回错误信息。
maxmemory-samples:设置样本数量,LRU 算法和最小 TTL 算法都并不是是精确的算法,而是估算值,因此你能够设置样本的大小,Redis 默认会检查这么多个 key 并选择其中 LRU 的那个。
appendonly:配置 AOF 持久化。
appendfilename:持久化文件名。
appendfsync:同步
always:同步持久化 每次发生数据变动会被当即记录到磁盘 性能较差但数据完整性比较好
everysec:出厂默认推荐,异步操做,每秒记录 若是一秒内宕机,有数据丢失。
no:从不一样步。
no-appendfsync-on-rewrite:重写时是否能够运用 Appendfsync,用默认 no 便可,保证数据安全性。
auto-aof-rewrite-min-size:设置重写的基准值。
参数说明
redis.conf 配置项说明以下:
Redis 会单首创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程当中,主进程是不进行任何 IO 操做的,这就确保了极高的性能,若是须要进行大规模数据的恢复,且对于数据恢复的完整性不是很是敏感,那 RDB 方式要比 AOF 方式更加的高效。RDB 的缺点是最后一次持久化后的数据可能丢失。
RDB 保存的是 dump.rdb 文件。
做用是复制一个与当前进程同样的进程,新进程的全部数据都和原进程同样,可是是一个新的进程,并做为原进程的子进程。
在 redis.conf 文件中的 SNAPSHOTTING 下面。
按照配置触发。
执行 save 或者 bgsave,save 只管保存,其余所有堵塞,bgsave 是后台异步进行快照,还能够接收用户请求。
执行 flushall 命令也会触发,可是因为执行了该命令清空全部库的数据,全部 dump.rdb 文件中是空的。
适合大规模的数据恢复。
对数据完整性和一致性要求不高。
在必定间隔时间作一次备份,因此若是 Redis 意外 down 掉的话,就会丢失最后一次快照后的全部修改。
fork 的时候,内存中的数据被克隆了一份,大体2倍的膨胀性须要考虑。
动态中止:全部中止 RDB 保存规则的方法:redis-cli config set save ""。
RDB 是一个很是紧凑的文件,在保存 RDB 文件的时候父进程会 fork 出一个子进程,接下来所有工做由子进程负责,父进程不须要作任何 IO 操做,全部 RDB 方式能够最大化 Redis 的性能,与 AOF 相比,它更适合作大规模的数据回复,对数据完整性、一致性要求不高,数据丢失风险高,由于该方式 fork 子进程来持久化,当数据量大的时候,fork 的过程很是耗时,可能会致使在毫秒级上 Redis 不能响应客户端请求。
以日志的形式来记录每一个写操做,将 Redis 执行过的全部写指令记录下来(读操做不记录),只许追加文件但不能够改写文件,Redis 启动之初会读取该文件从新构建数据,换言之,Redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工做。
AOF 保存的是 appendonly.aof 文件。
APPEND ONLY MODE,将 appendonly no 改成 yes。
正常恢复:
将有数据的 AOF 文件复制一份保存到对应目录 (config get dir)。
恢复:重启 Redis 而后从新加载。
异常恢复:
备份被写坏的 AOF 文件。
redis-check-aof --fix 文件名 进行修复。
恢复:重启 Redis 而后从新加载。
AOF 采用文件追加方式,随着时间的推移文件愈来愈大,为了不出现这种状况,新增了重写机制,当 AOF 文件超过预先设定的阈值时,Redis 就会启动内容压缩,只保留能够恢复数据的最小指令集,可使用命令 bgrewriteaof。
原理:
AOF 文件持续增加而过大时,会 fork 出一条新进程来将文件重写,遍历新进程的内存中数据,每条记录有一条的Set语句。重写 AOF 文件的操做,并无读取旧的 AOF 文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的 AOF 文件,这点和快照有点相似。
触发机制:
Redis 会记录上次重写时的 AOF 大小,默认配置是当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于64M 时触发。
每秒同步:appendfsync always 同步持久化每次发生数据变动会被当即记录到磁盘,性能较差但数据完整性比较好。
每修改同步:appendfsync everysec 异步操做,每秒记录,若是一秒内宕机,有数据丢失。
不一样步:appendfsync no 从不一样步。
相同数据集的数据而言 AOF 文件要远大于 RDB 文件,恢复速度慢于 RDB。
AOF 文件是一个只进行追加的日志文件,Redis 能够在 AOF 文件体积过大时进行重写,AOF 文件有序的保存了对数据的写操做,这些写操做以 Redis 协议被很好的保存,所以,AOF 文件的内容很是容易被读懂,对文件的分析也很容易。对于相同的数据集来讲,AOF 的体积一般要大于 RDB,根据所使用的 fsync 策略,AOF 的速度要慢于 RDB。
RDB 持久化方式可以在指定的时间间隔能对你的数据进行快照存储。
AOF 持久化方式记录每次对服务器写的操做,当服务器重启的时候会从新执行这些命令来恢复原始的数据,AOF 命令以 Redis 协议追加保存每次写的操做到文件末尾。Redis 还能对 AOF 文件进行后台重写,使得 AOF 文件的体积不至于过大。
若是你只但愿你的数据在服务器运行的时候存在,你也能够不使用任何持久化方式。
在这种状况下,当 Redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,由于在一般状况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。
RDB 的数据不实时,同时使用二者时服务器重启也只会找 AOF 文件。
那要不要只使用 AOF 呢?
建议不要,由于 RDB 更适合用于备份数据库 (AOF在不断变化很差备份),快速重启,并且不会有 AOF 可能潜在的 bug,留着做为一个万一的手段。
由于 RDB 文件只用做后备用途,建议只在 Slave 上持久化 RDB 文件,并且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。
若是 Enalbe AOF,好处是在最恶劣状况下也只会丢失不超过两秒数据,启动脚本较简单只 load 本身的 AOF 文件就能够了。代价一是带来了持续的 IO,二是 AOF rewrite 的最后将 rewrite 过程当中产生的新数据写到新文件形成的阻塞几乎是不可避免的。只要硬盘许可,应该尽可能减小 AOF rewrite 的频率,AOF 重写的基础大小默认值 64M 过小了,能够设到 5G 以上。默认超过原大小100%大小时重写能够改到适当的数值。
若是不 Enable AOF ,仅靠 Master-Slave Replication 实现高可用性也能够。能省掉一大笔 IO 也减小了 rewrite 时带来的系统波动。代价是若是 Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave中的 RDB 文件,载入较新的那个。新浪微博就选用了这种架构。
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,因此每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block 直到它拿到锁。传统的关系型数据库里边就用到了不少这种锁机制,好比行锁,表锁等,读锁,写锁等,都是在作操做以前先上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,因此不会上锁,可是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可使用版本号等机制。乐观锁适用于多读的应用类型,这样能够提升吞吐量,乐观锁策略:提交版本必须大于记录当前版本才能执行更新。
DISCARD 取消事务,放弃执行事务块内的全部命令
EXEC 执行全部事务块内的命令
MULTI 标记一个事务块的开始
UNWATCH 取消 WATCH 命令对全部 key 的监视
WATCH key [key...] 监视一个或多个 key,若是在事务执行以前这个 key 被其余命令所改动,那么事务将被打断
正常执行:
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k2 v22 QUEUED 127.0.0.1:6379> set k3 v33 QUEUED 127.0.0.1:6379> EXEC 1) OK 2) OK
放弃事务:
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> DISCARD OK 127.0.0.1:6379> get k3 "v33"
全体连坐:
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> setget k2 v2 (error) ERR unknown command 'setget' 127.0.0.1:6379> EXEC (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k3 "v33"
怨头债主:
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> INCR k2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> EXEC 1) OK 2) (error) ERR value is not an integer or out of range 3) OK
WATCH 监控:
对某个 key 监控以后,若是有别的客户端将这个 key 修改,则事务执行失败。
UNWATCH 的做用是放弃监控。
一旦执行 EXEC 不管成功与否全部监控所都被取消掉了。
WATCH 命令至关于乐观锁,事务提交时,若是 key 已经被修改,整个事务队列将执行失败,若是监视了多个 key,只要其中有一个 key 被修改,则整个队列执行失败,同时返回 Nullmulti-bulk 通知调用者事务执行失败。
开启:以MULTI开始一个事务
入队:将多个命令入队到事务中,接到这些命令并不会当即执行,而是放到等待执行的事务队列里面
执行:由EXEC命令触发事务
单独的隔离操做:事务中的全部命令都会序列化、按顺序地执行。事务在执行的过程当中,不会被其余客户端发送来的命令请求所打断。
没有隔离级别的概念:队列中的命令没有提交以前都不会实际的被执行,由于事务提交前任何指令都不会被实际执行,也就不存在事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题。
不保证原子性:Redis 同一个事务中若是有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
进程间的一种消息通讯模式:发送者(pub)发送消息,订阅者(sub)接收消息。
首先须要订阅者订阅才能够收到消息,SUBSCRIBE c1 c2 c3。
而后发布者向频道发送消息,订阅者就能够收到了,好比 PUBLISH c3 word。
订阅者订阅频道时候还可使用通配符,例如 SUBSCRIBE new*。
这样不管向 new1 频道仍是 new2 频道发送消息订阅者均可以收到。
也就是咱们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的 Master/Slaver 机制,Master 以写为主,Slave 以读为主。
读写分离
容灾恢复
配从不配主。
从库配置:slaveof 主库 IP 主库端口,没次与主库断开链接后,都须要从新链接,除非写入到配置文件中。能够经过 info replication 查看当前数据库的配置。
经常使用三种方式:
一主二仆:
一个 Master 配置两个 Slave。Salve 只能读,不能够写,当 Master 挂掉,Salve 原地等待,Master 恢复后,自动链接,若是 Slave 挂掉,须要手动从新链接。
薪火相传:Master 配置一个 Slave,Slave 能够是下一个 Salve 的 Master,有效减轻 Master 的压力。
反客为主:经过 Salve no one 使当前数据库中止与其余数据库的同步,转成 Master。
Slave 启动成功链接到 Master 后会发送一个 sync 命令。
Master 接到命令启动后台的存盘进程,同时收集全部接收到的用于修改数据集命令,在后台进程执行完毕以后,Master 将传送整个数据文件到 Slave,以完成一次彻底同步。
全量复制:而 Slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master 继续将新的全部收集到的修改命令依次传给 Slave,完成同步。
可是只要是从新链接 Master,一次彻底同步(全量复制)将被自动执行。
反客为主的自动版,监控主库是否挂掉,若是挂掉自动选出一个从库做为主库。
新建 sentinel.conf 文件,配置哨兵, sentinel monitor 被监控数据库名字 127.0.0.1 6379 1,1表示主机挂掉 Salve 投票看谁能当主机,得票多的当主机。
启动哨兵,redis-sentinel sentinel.conf 。
若是原有的 Master 挂掉,会自动选举出一个 Master,当 Master 恢复后,原 Master 做为 新 Master 的 Salve。
缺点:因为全部的写操做都是先在 Master 上操做,而后同步更新到 Slave 上,因此从 Master 同步到 Slave 机器有必定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave 机器数量的增长也会使这个问题更加严重。
开放端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
配置阿里云安全组规则
查看开放端口
firewall-cmd --zone=public --list-ports
commons-pool-1.6.jar
jedis-2.1.0.jar
获取 Jedis 实例须要从 JedisPool 中获取,用完要返还给 JedisPool,出错也要返还给 JedisPool。
自定义 JedisPoolUtil
public class JedisPoolUtil { // 保证此变量对全部的线程的可见性,禁止指令重排序优化 private static volatile JedisPool jedisPool = null; // 私有化构造方法 private JedisPoolUtil() { } // 提供池子,采用双重检验锁机制 public static JedisPool getJedisPool() { if (null == jedisPool) { synchronized (JedisPool.class) { if (null == jedisPool) { JedisPoolConfig poolConfig = new JedisPoolConfig(); // 最大链接数 poolConfig.maxActive = 1000; // 最大等待链接数 poolConfig.maxIdle = 32; // 最大等待时间 poolConfig.setMaxWait(100 * 1000); // 得到实例时是否检测可用性 poolConfig.setTestOnBorrow(true); jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379); } } } return jedisPool; } public static void release(JedisPool jedisPool, Jedis jedis) { if (jedis != null) { jedisPool.returnResourceObject(jedis); } } }
双重检验锁是对同步块加锁的方法,为何教双重检验呢,那是由于有两次判空操做。
为什么在同步块外面判空?
为了提升性能,若是去掉同步块外面判空,只要调用此方法,都会拿到一个静态内部锁,下降了效率,因此在外面加判空,下降了同步块的执行次数。
为何还在同步块内判空呢?
可能会有多个线程同时进入同步块外面的判空,可是有线程先一步拿到了对象,若是再也不内部判空,可能会生成多个实例。
必定要使用 volatile 修饰静态变量,不然因为多线程,仍是会建立多个实例。
JedisPool 的配置参数大部分是由 JedisPoolConfig 的对应项来赋值的。
maxActive:控制一个 pool 可分配多少个 jedis 实例,经过 pool.getResource() 来获取,若是赋值为-1,则表示不限制;若是 pool 已经分配了 maxActive 个 jedis 实例,则此时 pool 的状态为 exhausted。
maxIdle:控制一个 pool 最多有多少个状态为 idle(空闲)的 jedis 实例。
whenExhaustedAction:表示当 pool 中的 jedis 实例都被 allocated 完时,pool 要采起的操做,默认有三种:
WHEN_EXHAUSTED_FAIL --> 表示无 jedis 实例时,直接抛出 NoSuchElementException。
WHEN_EXHAUSTED_BLOCK --> 则表示阻塞住,或者达到 maxWait 时抛出 JedisConnectionException。
WHEN_EXHAUSTED_GROW --> 则表示新建一个 jedis 实例,也就说设置的 maxActive 无用。
maxWait:表示当 borrow 一个 jedis 实例时,最大的等待时间,若是超过等待时间,则直接抛 JedisConnectionException。
testOnBorrow:得到一个 jedis 实例的时候是否检查链接可用性(ping());若是为true,则获得的 jedis 实例均是可用的。
testOnReturn:return 一个 jedis 实例给 pool 时,是否检查链接可用性(ping())。
testWhileIdle:若是为 true,表示有一个 idle object evitor 线程对 idle object 进行扫描,若是 validate 失败,此 object 会被从 pool 中 drop 掉;这一项只有在 timeBetweenEvictionRunsMillis 大于0时才有意义。
timeBetweenEvictionRunsMillis:表示 idle object evitor 两次扫描之间要sleep的毫秒数。
numTestsPerEvictionRun:表示 idle object evitor 每次扫描的最多的对象数。
minEvictableIdleTimeMillis:表示一个对象至少停留在 idle 状态的最短期,而后才能被 idle object evitor 扫描并驱逐;这一项只有在 timeBetweenEvictionRunsMillis 大于0时才有意义。
softMinEvictableIdleTimeMillis:在 minEvictableIdleTimeMillis 基础上,加入了至少 minIdle 个对象已经在pool里面了。若是为-1,evicted 不会根据 idle time 驱逐任何对象。若是 minEvictableIdleTimeMillis>0,则此项设置无心义,且只有在timeBetweenEvictionRunsMillis 大于0时才有意义。
lifo:borrowObject 返回对象时,是采用 DEFAULT_LIFO(last in first out,即相似cache的最频繁使用队列),若是为 False,则表示 FIFO 队列。
其中 JedisPoolConfig 对一些参数的默认设置以下:testWhileIdle=trueminEvictableIdleTimeMills=60000timeBetweenEvictionRunsMillis=30000numTestsPerEvictionRun=-1