目录
Redis
概念
Redis是NoSQL中比较常典型的一个非关系型数据库,在日常工作中也是最为常见的。Redis是一个由C语言编写的开源的、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
这里我们引入一下 cookie 和 session ,session 一般是存在于服务器的,每次我们进行操作,都要跟这个 session 值进行校验,如果校验不上,就会重定向登录页。那么如果这个 session 放在普通的关系型数据库内,比如 Mysql ,那么首先读取的速度会相对较慢,而且会占用正常业务逻辑对于数据库的连接池,磁盘 io 等。在这种情况下,那么我们的 session 一般是存在缓存内。那么我们可以用 Redis 来存储这个 session。
安装&启动
1、下载并安装 Redis
我们可以使用wget下载,也可以将redis的包下载下来并且导入到linux中
$ wget http://download.redis.io/releases/redis-2.8.17.tar.gz $ yum install gcc tcl -y $ .tar.gz $ cd redis- $ make $ mkdir /usr/local/redis $ make install PREFIX=/usr/local/redis
2、启动/停止 Redis
./redis-server
redis-cli shutdown
默认启动redis使用的是默认配置,端口号为 6379,密码为空。如果需要后台启动的话,可以使用nohup来启动。
nohup redis-server &
当然,如果需要指定redis的配置文件,那我们可以在启动命令中指定好配置文件就可以。
nohup redis-server /usr/local/redis/redis.conf &
3、redis.conf 配置
# Protected mode is a layer of security protection, in order to avoid that # Redis instances left open on the internet are accessed and exploited. # # When protected mode is on and if: # # ) The server is not binding explicitly to a set of addresses using the # "bind" directive. # ) No password is configured. # # The server only accepts connections from clients connecting from the # IPv4 and IPv6 loopback addresses , and from Unix domain # sockets. # # By default protected mode is enabled. You should disable it only if # you are sure you want clients from other hosts to connect to Redis # even if no authentication is configured, nor a specific set of interfaces # are explicitly listed using the "bind" directive. protected-mode yes ## 保护模式,保护 Redis 不受外网连接 # Accept connections on the specified port, default is (IANA #). # If port is specified Redis will not listen on a TCP socket. port 6379 ## Redis 的端口,可自行设置 # Unix socket. # # Specify the path for the Unix socket that will be used to listen for # incoming connections. There is no default, so Redis will not listen # on a unix socket when not specified. # # unixsocket /tmp/redis.sock # unixsocketperm # Close the connection after a client is idle to disable) timeout # TCP keepalive. # # If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence # of communication. This is useful for two reasons: # # ) Detect dead peers. # ) Take the connection alive from the point of view of network # equipment in the middle. # # On Linux, the specified value (in seconds) is the period used to send ACKs. # Note that to close the connection the double of the time is needed. # On other kernels the period depends on the kernel configuration. # # A reasonable value seconds, which is the new # Redis default starting with Redis . tcp-keepalive 300 ## tcp 连接数 # By default Redis does not run as a daemon. Use 'yes' if you need it. # Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 # Note that Redis will write a pid file in /var/run/redis.pid when daemonized. # 启用守护进程后,Redis会把pid写到一个pidfile中,在/var/run/redis.pid daemonize yes ## 后台启动 # Set the number of databases. The default database is DB , you can select # a different one on a per-connection basis using SELECT <dbid> where # dbid is a number between and databases 16 ## Redis 到底起多少库 ################################ SNAPSHOTTING ################################ # # Save the DB on disk: # # save <seconds> <changes> # # Will save the DB if both the given number of seconds and the given # number of write operations against the DB occurred. # # In the example below the behaviour will be to save: # after sec ( min) key changed # after sec ( min) keys changed # after sec keys changed # # Note: you can disable saving completely by commenting out all "save" lines. # # It is also possible to remove all the previously configured save # points by adding a save directive with a single empty string argument # like in the following example: # # save "" save 900 ## 900s 内 1 次数据更改操作,存一次数据库 save 300 ## 300s 内 10次数据更改操作,存一次数据库 save 60 ## 60s内10000次数据更改操作,存一次数据库 # Compress string objects using LZF when dump .rdb databases? # For default that's set to 'yes' as it's almost always a win. # If you want to save some CPU in the saving child set it to 'no' but # the dataset will likely be bigger if you have compressible values or keys. rdbcompression yes # The filename where to dump the DB dbfilename dump.rdb # The working directory. # # The DB will be written inside this directory, with the filename specified # above using the 'dbfilename' configuration directive. # # The Append Only File will also be created inside this directory. # # Note that you must specify a directory here, not a file name. dir ./
这里,我们可以创建一个模板配置文件,命名为 redis6380.conf :
daemonize yes pidfile /usr/local/redis/redis.pid_6380 logfile /usr/local/redis/redis.log_6380 port maxmemory 128mb dir /usr/local/redis/redis6380 ## 该路径要自己创建 requirepass appendonly yes databases
然后,我们要启动该 Redis 的话,要指定该配置文件,否则会按照 redis.conf 文件启动:
./redis-server redis6380.conf
# ps -ef | grep redis ## 查看 redis 是否启动以及启动端口
root 13098 1 0 16:40 ? 00:00:00 ./redis-server *:6380
root 13105 13063 0 16:41 pts/1 00:00:00 grep redis
连接 redis
远程连接redis,可以使用redis自带的工具 redis-cli,具体使用方法如下:
./redis-cli -h -n -h <hostname> Server hostname (default: 127.0.0.1). -p <port> Server port (default: ). -a <password> Password to use when connecting to the server. --help 显示帮助信息
切换数据库:
那么我们如果是自己的配置文件启动,就像我们刚刚的 6380 ,怎么连接?
# ./redis-cli -h -a >
利用客户端连接:
注意,图形化工具虽然直观,但是数据量一旦太大,是显示不了的,所以,命令行才是最牛批的
那么,代表 Redis 已经启动,并且可以远程连接
在图形化内,可以查看 Redis 相关配置信息
Redis 常用命令
Redis 是一个高性能的key-value数据库,和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。不过既然redis是一个数据库,那么也就逃脱不开增删改查的操作,接下来把每种数据类型常用的增删改查命令列举一次。
如果对某个命令不知道该如何使用的时候可以使用 -help 来查询,SET 就是新增以及修改数据,GET 就是查询数据
> help set SET key value [EX seconds] [PX milliseconds] [NX|XX] ## EX seconds 代表这条数据在 Resdis 内存多久。这里我们结合 session 来考虑,假设我们这里设置为30min,是不是就意味着30min后登陆就失效呢?那么就得重新登录 summary: Set the string value of a key since: group: string
> help get ## 查询 key 值对应的 value GET key summary: Get the value of a key since: group: string
127.0.0.1:6380> help getset ## 先获取一个值,然后写入,相当于给之前的 key 更新一个 value
GETSET key value
summary: Set the string value of a key and return its old value
since: 1.0.0
group: string
127.0.0.1:6380> help mget ## 用的比较少,这里简单介绍,打印多个 key 值的 value
MGET key [key ...]
summary: Get the values of all the given keys
since: 1.0.0
group: string
127.0.0.1:6380> help del ## 删除键
DEL key [key ...]
summary: Delete a key
since: 1.0.0
group: generic
我们这里介绍几种常用的数据类型:String、Hash:
1、String
redis中最简单的数据类型,可以存储文字、数字、浮点数还可以进行二进制的存储,value存储最大数据量为512M。String 很简单就是 key - value 键值对
> SET num OK > GET num " > SET num ## 这一步是修改 num 的 value 值为 OK > GET num "
> set num EX ## 这里我们将 num 设置为 10s内可查询 OK > get num " > get num ## 超过 10s 就查不到了 (nil)
127.0.0.1:6380> set num 123456 ## 这个相当于说,假设 key 为用户,value 对应的去除 session 的时间点(设置为 30min)
OK
127.0.0.1:6380> getset num 654321 ## 那么假设过了 15min ,用户操作了,那么需要更新一下 session 的清除时间,这个就是更新时间点,并且旧的值会被返回出来
"123456"
127.0.0.1:6380> get num
"654321"
127.0.0.1:6380> set num 123
OK
127.0.0.1:6380> set sex man
OK
127.0.0.1:6380> set len 180
OK
127.0.0.1:6380> mget num sex len ## 将多个 key 值对应的 value 打印
1) "123"
2) "man"
3) "180"
127.0.0.1:6380> mset num 321 len 200 sex women ## 批量更新值
OK
127.0.0.1:6380> mget num len sex
1) "321"
2) "200"
3) "women"
2、Hash
hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。Redis 中每个 hash 可以存储 232 – 1 键值对(40多亿)。理解为套了一个壳子的 string 类型
实际上还是 key - value 的格式,但是在 key - value 之上加了一个 hash 的组管理。实际上,这个 Hash 表用的还是挺多的,比如说:我们一个用户登录,有用户个人信息,订单信息,地址信息,消息信息等……那么每个用户都有这些信息。所以:可以将用户个人信息作为一个 Hash ,里面的 key 对应某个用户,一个 value 对应该用户的用户信息的 value 值;同样的,订单信息也是一个 Hash 值,里面的 key - value 对应不同用户的键值对。这样可以理解方便管理。
命令这里就不详细讲,可以用 help hset 或者 help hget 来查看。hget 和 hset 对应 String 类型的 get 以及 set 命令。例如: hset key field value ,后面 field value 就是对应 String 内的 key - value
hkeys 可以显示一个 Hash 内的所有 key 值
Redis 命令参考以下链接:http://doc.redisfans.com/
监控 Redis
图形化监控工具
TreeSoft
命令行监控
- 吞吐量
- 延迟
- 持续实时监控
- 慢操作日志
吞吐量:Redis提供的INFO命令不仅能够查看实时的吞吐量(ops/sec),还能看到一些有用的运行时信息。下面用grep过滤出一些比较重要的实时信息,比如已连接的和在阻塞的客户端、已用内存、拒绝连接、实时的tps和数据流量等
[root@liml redis]# /usr/local/redis/redis-cli -h -a info | > grep -e "connected_clients" -e "blocked_clients" -e "used_memory_human" -e "used_memory_peak_human" -e "rejected_connections" -e "evicted_keys" -e "instantaneous" connected_clients: #连接数 blocked_clients: #阻塞连接数 used_memory_human:3.37M # redis占用内存 used_memory_peak_human:3.37M #redis占用内存峰值 instantaneous_ops_per_sec: #每秒处理请求数 instantaneous_input_kbps:11.82 #每秒读字节数 instantaneous_output_kbps:0.75 #每秒写字节数 rejected_connections: #拒绝连接数 evicted_keys: #运行以来删除的key数量
延迟:监控redis的延迟可以使用redis提供的ping命令来不断的ping服务器,并且记录服务器响应ping的时间
[root@liml ~]# /usr/local/redis/redis-cli -h -a --latency min: , max: , avg: samples) [root@liml ~]# /usr/local/redis/redis-cli -h -a > monitor ] "PING" ] "PING" ] "PING" ] "PING" ] "PING" ] "PING"
持续实时监控:使用的是linux自带的watch命令和我们前面监控命令结合到一起就可以变成持续的实时监控工具了
# 先将前面的命令保存到一个shell脚本中, vi testredis.sh #!/bin/bash /usr/local/redis/redis-cli -h -a info | grep -e "connected_clients" -e "blocked_clients" -e "used_memory_human" -e "used_memory_peak_human" -e "rejected_connections" -e "evicted_keys" -e "instantaneous" #保存后并给予执行的权限 testredis.sh # 使用watch命令实时监控 watch -n -d "/usr/local/redis/testredis.sh"
慢操作日志:SORT、LREM、SUNION等操作在大对象上会非常耗时,使用时要注意参照官方API上每个命令的算法复杂度。和主流数据库提供的慢SQL日志一样,Redis也提供了记录慢操作的日志。注意这部分日志只会计算纯粹的操作耗时
压力测试redis
redis-benchmark
默认情况下面,基准测试使用单一的 key。在一个基于内存的数据库里, 单一 key 测试和真实情况下面不会有巨大变化。当然,使用一个大的 key 范围空间, 可以模拟现实情况下面的缓存不命中情况。
这时候我们可以使用 -r 命令。比如,假设我们想设置 10 万随机 key 连续 SET 100 万次,我们可以使用下列的命令:
./redis-benchmark -p -a -t set -n -r ====== set -n -r ====== requests completed in 1.24 seconds parallel clients bytes payload keep alive: milliseconds milliseconds milliseconds milliseconds milliseconds 80450.52 requests per second
有几个因素直接决定 Redis 的性能。它们能够改变基准测试的结果, 所以我们必须注意到它们。一般情况下,Redis 默认参数已经可以提供足够的性能, 不需要调优。
- 网络带宽和延迟通常是最大短板。建议在基准测试之前使用 ping 来检查服务端到客户端的延迟。根据带宽,可以计算出最大吞吐量。 比如将 4 KB 的字符串塞入 Redis,吞吐量是 100000 q/s,那么实际需要 3.2 Gbits/s 的带宽,所以需要 10 GBits/s 网络连接, 1 Gbits/s 是不够的。 在很多线上服务中,Redis 吞吐会先被网络带宽限制住,而不是 CPU。 为了达到高吞吐量突破 TCP/IP 限制,最后采用 10 Gbits/s 的网卡, 或者多个 1 Gbits/s 网卡。
- CPU 是另外一个重要的影响因素,由于是单线程模型,Redis 更喜欢大缓存快速 CPU, 而不是多核。这种场景下面,比较推荐 Intel CPU。AMD CPU 可能只有 Intel CPU 的一半性能(通过对 Nehalem EP/Westmere EP/Sandy 平台的对比)。 当其他条件相当时候,CPU 就成了 redis-benchmark 的限制因素。
- 在小对象存取时候,内存速度和带宽看上去不是很重要,但是对大对象(> 10 KB), 它就变得重要起来。不过通常情况下面,倒不至于为了优化 Redis 而购买更高性能的内存模块。
- Redis 在 VM 上会变慢。虚拟化对普通操作会有额外的消耗,Redis 对系统调用和网络终端不会有太多的 overhead。建议把 Redis 运行在物理机器上, 特别是当你很在意延迟时候。在最先进的虚拟化设备(VMWare)上面,redis-benchmark 的测试结果比物理机器上慢了一倍,很多 CPU 时间被消费在系统调用和中断上面。
MongoDB
当我的 Tomcat 需要存储一些比较复杂的,大文本的数据,存储要快,数据会频繁发生改变,而且我们要保留历史的改变,这时候我们就可以考虑用 MongoDB 来存数据。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
MongoDB的应用场景:
- 大而复杂的数据
- 移动和社会基础设施数据
- 内容管理和交付
- 用户数据管理
- 数据中心
用途:存储大的聚合数据,比如说某用户对于某东西没每天的点击次数,因为存入是 json 串存入,是个文件型数据库,储存数据的形式是 json 形式。优势是:可以支持大数据量,大文本存取,查询速度很快。
安装&启动 mongoDB——端口 27017 为默认端口
1、安装MongoDB
我们日常使用更多是使用linux的服务器,所以接下来让我们看一下linux安装MongoDB的方法
# 下载MongoDB curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6.tgz # 解压 .tgz # 将解压包拷贝到指定目录 / /usr/local/mongodb
当然,也可以直接在网上下载下来后解压使用
2、启动MongoDB
启动MongoDB之前需要先创建一个文件存储目录,默认会去使用/data/db这个目录
mkdir /data/db
创建好存储目录,使用mongod来启动MongoDB,如果指定的目录不是/data/db这个目录,则需要使用-dbpath 来声明目录位置。
mongod -dbpath=/opt/data/
使用上面的命令以后我们会发现,我们的MongoDB确实是启动了,但是只是在前台启动,我们要做什么操作都会使它停掉,所以我们就需要使用–fork 或者nohup来让MongDB进入到后台运行。
在使用nohup或者 –fork启动MongoDB的时候需要先指定log文件的路径,注意这里需要指定到具体的log文件名,而不是目录
mkdir /usr/local/mongodb/logs
好了,日志的路径我们已经创建好了,接下来让我们启动MongoDB服务吧。
./mongod --dbpath /usr/local/mongodb/data/db --logpath /usr/local/mongodb/logs/mongodb.log --logappend --fork这里,我们要指定 dbpath,logpath,以及log的添加方式为append(追加),fork代表后台运行
当然我们可以看一下 mongo 的配置文件:
dbpath = /usr/local/mongodb/data/db #数据文件存放目录 logpath = /usr/local/mongodb/logs/mongodb.log #日志文件存放目录 port = #端口 fork = true #以守护程序的方式启用,即在后台运行 nohttpinterface = true
连接 mongoDB
连接 mongo 一般不要密码,一般在内网内使用。当然也可以设置密码,如果生产环境,不设置密码就要做一步操作:监听只能够允许自己的内网去访问
前面我们启动了MongoDB,接下来让我们尝试一下连接MongoDB。
1、连接MongoDB使用bin目录下的mongo工具
通过 shell 连接 MongoDB 服务:
$ ./mongo MongoDB shell version: connecting to: test ... # 访问远程MongoDB ./mongo 192.168.0.5/foo # 访问远程MongoDB 端口号9999 ./mongo /foo
2、使用工具连接MongDB
推荐使用 NoSQL Manager for MongoDB Freeware
MongoDB 常用命令
- 创建数据库
# 查看数据库 > show dbs besttest .078GB local .078GB # 现在我们创建一个新库 > use besttestdb switched to db besttestdb > show dbs besttest .078GB local .078GB > db besttestdb
这时使用show dbs查询时发现我们新建的库没有查询出来,这是为什么呢?让我们先来写一些数据进去
> db.besttestdb.insert({"teacher":"liml"}) WriteResult({ "nInserted" : }) > show dbs besttest .078GB besttestdb .078GB local .078GB
这样就可以看到我们刚刚创建的库了。另外需要注意的一点是MongoDB 中默认的数据库为 test,如果你没有创建新的数据库,集合将存放在 test 数据库中。
- 删除数据库
MongoDB 删除数据库的语法格式如下:
db.dropDatabase()
删除当前数据库,默认为 test,可以使用 db 命令查看当前数据库名。以下实例我们删除了数据库 besttestdb。
首先,查看所有数据库:
> show dbs besttest .078GB besttestdb .078GB local .078GB
接下来我们切换到数据库 besttestdb :
> use besttestdb switched to db besttestdb
执行删除命令:
> db.dropDatabase() { "dropped" : "runoob", "ok" : }
- 增
MongoDB 使用 insert() 或 save() 方法向集合中插入文档,语法如下:
db.COLLECTION_NAME.insert(document)
下面让我们真实的操作一下
>db.besttest.insert({title: 'MongoDB 教程', description: 'MongoDB是一个Nosql数据库', by: 'liml', url: 'http://www.limlhome.cn', tags: ['mongodb', 'database', 'NoSQL','test'] }) WriteResult({ "nInserted" : })
以上实例中 besttest是我们的集合名,如果该集合不在该数据库中, MongoDB 会自动创建该集合并插入文档。
- 删
MongoDB remove()函数是用来移除集合中的数据。MongoDB数据更新可以使用update()函数。在执行remove()函数前先执行find()命令来判断执行的条件是否正确,这是一个比较好的习惯。
remove() 方法的基本语法格式如下所示:
db.collection.remove( <query>, <justOne> )
如果你的 MongoDB 是 2.6 版本以后的,语法格式如下:
db.collection.remove( <query>, { justOne: <boolean>, writeConcern: <document> } ) query :(可选)删除的文档的条件。 justOne : (可选)如果设为 true 或 ,则只删除一个文档。 writeConcern :(可选)抛出异常的级别
- 改
update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(<query>,<update>,{upsert: <boolean>, multi: <boolean>,writeConcern: <document>}) query : update的查询条件,类似sql update查询内where后面的。 update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的 upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。 multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。 writeConcern :可选,抛出异常的级别。 更新一条数据 db.besttest.}}) db.besttest.find() 这时会发现只能修改第一条,如果我想修改更多的就要使用multi db.besttest.}},{multi:true}) db.besttest.find()
当然update只能修改数据中的某个字段,如果我想重写整个值那么就要使用save
db.besttest.}},{multi:true}); db.besttest.save({"_id" : ObjectId("5ae08bf74e7cd235458113bb"),"teacher":"andashu","interet":"andashen"}); db.besttest.find();
- 查
MongoDB 查询数据的语法格式如下:
db.collection.find(query, projection) query :可选,使用查询操作符指定查询条件 projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。 # 如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,格式如下: > db.besttest.find().pretty() { "_id" : ObjectId("5abb9ce41029dbb11d2fd317"), "teacher" : "liml" } { "_id" : ObjectId("5abb9e7ec16802eb02743e6d"), "teacher" : "liml" } { "_id" : ObjectId("5abb9e80c16802eb02743e6e"), "teacher" : "liml" } { "_id" : ObjectId("5abb9e80c16802eb02743e6f"), "teacher" : "andashu" }
要在一些条件的基础上查询文档,可以使用以下操作。
操作 | 语法 | 示例 | RDBMS等效语句 |
---|---|---|---|
相等 | {<key>:<value>} |
db.mycol.find({"by":"yiibai"}).pretty() |
where by = 'yiibai' |
小于 | {<key>:{$lt:<value>}} |
db.mycol.find({"likes":{$lt:50}}).pretty() |
where likes < 50 |
小于等于 | {<key>:{$lte:<value>}} |
db.mycol.find({"likes":{$lte:50}}).pretty() |
where likes <= 50 |
大于 | {<key>:{$gt:<value>}} |
db.mycol.find({"likes":{$gt:50}}).pretty() |
where likes > 50 |
大于等于 | {<key>:{$gte:<value>}} |
db.mycol.find({"likes":{$gte:50}}).pretty() |
where likes >= 50 |
不等于 | {<key>:{$ne:<value>}} |
db.mycol.find({"likes":{$ne:50}}).pretty() |
where likes != 50 |