分类 nosql 下的文章

redis里zset命令的用法举例(有序集合)

假设的场景为,幼儿园老师给小朋友奖励红花或者贴纸,然后统计他们获得贴纸的情况

#tom第一天获得5个贴纸
> zadd day1 5 tom
(integer) 1
#jacy第一天获得2个贴纸
> zadd day1 2 jack
(integer) 1
#merry第一天获得8个贴纸
> zadd day 8 merry
(integer) 1
#jacy第一天又多增加获得1个贴纸
> zincrby day1 1 jack
"3"
#查看jack的排名
> zrank day1 jack
(integer) 0
#查看merry的排名
> zrank day1 merry
(integer) 2
#查看所有人的排名
> zrange day1 0 -1
1) "jack"
2) "tom"
3) "merry"
#查看获得5到9个之间的小朋友的名单
> zrangebyscore day1 5 9
1) "tom"
2) "merry"
#查看day1总共有多少个人
> zcard day1
(integer) 3
#jack第二天又获得了3个贴纸
> zadd day2 3 jack
(integer) 1
#统计day1,day2两天总共获得的贴纸排名,并且把结果放到union:day1andday2这个key里面
> zunionstore union:day1andday2 2 day1 day2 
(integer) 3
#统计排名并显示他们获得贴纸的个数
> zrangebyscore union:day1andday2 0 1000 withscores
1) "tom"
2) "5"
3) "jack"
4) "6"
5) "merry"
6) "8"

其它的使用场景:
1:统计日活
2:统计最近24小时的收益等等
3:打赏日榜
4:爬升榜
5:还有个特殊的应用,可以把score设置成过期时间,根据socre去定制清理一些东西

实现方式:
sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

zset(sorted set)操作的命令

zadd(key, score, member):向名称为key的zset中添加元素member,score用于排序。如果该元素已经存在,则根据score更新该元素的顺序。

zrem(key, member) :删除名称为key的zset中的元素member

zincrby(key, increment, member) :如果在名称为key的zset中已经存在元素member,则该元素的score增加increment;否则向集合中添加该元素,其score的值为increment

zrank(key, member) :返回名称为key的zset(元素已按score从小到大排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”

zcard统计个数

zrevrank(key, member) :返回名称为key的zset(元素已按score从大到小排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”

zrange(key, start, end):返回名称为key的zset(元素已按score从小到大排序)中的index从start到end的所有元素

zrevrange(key, start, end):返回名称为key的zset(元素已按score从大到小排序)中的index从start到end的所有元素

zrangebyscore(key, min, max):返回名称为key的zset中score >= min且score <= max的所有元素 zcard(key):返回名称为key的zset的基数 zscore(key, element):返回名称为key的zset中元素element的score zremrangebyrank(key, min, max):删除名称为key的zset中rank >= min且rank <= max的所有元素 zremrangebyscore(key, min, max) :删除名称为key的zset中score >= min且score <= max的所有元素

zunionstore / zinterstore(dstkeyN, key1,…,keyN, WEIGHTS w1,…wN, AGGREGATE SUM|MIN|MAX):对N个zset求并集和交集,并将最后的集合保存在dstkeyN中。对于集合中每一个元素的score,在进行AGGREGATE运算前,都要乘以对于的WEIGHT参数。如果没有提供WEIGHT,默认为1。默认的AGGREGATE是SUM,即结果集合中元素的score是所有集合对应元素进行SUM运算的值,而MIN和MAX是指,结果集合中元素的score是所有集合对应元素中最小值和最大值。

redis里set命令的用法举例

以计算日活的方式来看set这个数据类型的用法

#添加测试数据,例如第一天有a,b,c登录了
127.0.0.1:6379[2]> sadd day1 "a" "b" "c"
(integer) 3
#查看第一天有哪些用户登录了
127.0.0.1:6379[2]> smembers day1
1) "b"
2) "c"
3) "a"
#随机抽查一个用户
127.0.0.1:6379[2]> srandmember day1
"a"
#添加测试数据,例如第一天有b,x,y,z登录了
127.0.0.1:6379[2]> sadd day2 "b" "x" "y" "z"
(integer) 4
#交集,查看着两天有谁都登录了
127.0.0.1:6379[2]> sinter day1 day2
1) "b"
#并集,查看着两天所有登录用户的记录
127.0.0.1:6379[2]> sunion day1 day2
1) "b"
2) "z"
3) "c"
4) "x"
5) "y"
6) "a"
#差集,查看day1登录过,但是day2没有登录的
127.0.0.1:6379[2]> sdiff day1 day2
1) "a"
2) "c"
#差集,查看day2登录过,但是day1没有登录的
127.0.0.1:6379[2]> sdiff day2 day1
1) "z"
2) "x"
3) "y"
#把结果记录到result这个key里面,给后续的计算带来方便
127.0.0.1:6379[2]> sunionstore result day1 day2
(integer) 6
#看结果
127.0.0.1:6379[2]> smembers result
1) "b"
2) "z"
3) "c"
4) "x"
5) "y"
6) "a"
#这两天总登录用户个数
127.0.0.1:6379[2]> scard result
(integer) 6

除此之外,交集在实际使用中,还可以用于接口结果的匹配,例如:比喻某条数据有两个维度的条件,一个是渠道,一个是版本
数据A,可在应用宝渠道发布,版本不限制
数据B,渠道无限制,版本为5.0才行
数据C,渠道在应用宝,版本5.0才行
接口通过条件来匹配,可能通过交集运算得出只有数据C才符合要求,例如,通过条件来匹配,只需要渠道维度。

redis里set命令的其它方法:
对Set操作的命令

sadd(key, member):向名称为key的set中添加元素member
srem(key, member) :删除名称为key的set中的元素member
spop(key) :随机返回并删除名称为key的set中一个元素
smove(srckey, dstkey, member) :将member元素从名称为srckey的集合移到名称为dstkey的集合
scard(key) :返回名称为key的set的基数
sismember(key, member) :测试member是否是名称为key的set的元素
sinter(key1, key2,…key N) :求交集
sinterstore(dstkey, key1, key2,…key N) :求交集并将交集保存到dstkey的集合
sunion(key1, key2,…key N) :求并集
sunionstore(dstkey, key1, key2,…key N) :求并集并将并集保存到dstkey的集合
sdiff(key1, key2,…key N) :求差集
sdiffstore(dstkey, key1, key2,…key N) :求差集并将差集保存到dstkey的集合
smembers(key) :返回名称为key的set的所有元素
srandmember(key) :随机返回名称为key的set的一个元素

实现方式:(参考网上其它文章)
set 的内部实现是一个 value 永远为 null 的 HashMap,实际就是通过计算 hash 的方式来快速排重的,这也是 set 能提供判断一个成员是否在集合内的原因。

mongodb占用太大内存的问题

mongodb对内存占用是否可怕,服务器内存是8G,但是mongodb并没有多少记录,大概删掉后只有几千条,但是却占用了35%的内存(先top后,然后 shift+m 把当前进场按占用内存的多少排序),惊人。没有使用swap。
mongodb把内存的管理交给了系统,所以在程序里面并没有办法处理这部分内存的占用,db.repairDatabase(),返回OK后,内存占用依然很高,最后只能暂用重启的办法,use admin;db.shutdownServer();或者kill,之后再启动,启动成功后,内存和CPU粉笔只占用l%.mongostat后看也很正常。
但是这个问题并没有很好的解决,怎样才能在不重启的情况下解决这个问题还不知道。另外,在linux里面,可以结合ulimit来控制mongodb的内存使用大小。

mongodb常用操作

以下是在mongodb3.2实际操作。
数据备份:
mongodump --host IP --port 端口 -u 用户名 -p 密码 -d 数据库 -o 文件存在路径

[dev@aws17-pg ~]$ mongodump --host 10.0.1.12 --port 27020 -d applock -o /home/dev/backup/
2017-02-15T03:39:38.115-0500    writing applock.lockdetail to 
2017-02-15T03:39:41.116-0500    [######..................]  applock.lockdetail  932703/3502968  (26.6%)
2017-02-15T03:39:44.158-0500    [############............]  applock.lockdetail  1854275/3502968  (52.9%)
2017-02-15T03:39:47.116-0500    [###################.....]  applock.lockdetail  2832777/3502968  (80.9%)
2017-02-15T03:39:49.174-0500    [########################]  applock.lockdetail  3502968/3502968  (100.0%)
2017-02-15T03:39:49.174-0500    done dumping applock.lockdetail (3502968 documents)

数据还原:
mongorestore -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 --drop 文件存在路径
--drop的意思是,先删除所有的记录,然后恢复。

[root@pg-test ~]# mongorestore -d applock /root/applock
connected to: 127.0.0.1
2017-02-15T17:20:50.057+0800 /root/applock/lockdetail.bson
2017-02-15T17:20:50.057+0800    going into namespace [applock.lockdetail]
2017-02-15T17:20:50.110+0800    Created collection applock.lockdetail with options: { "create" : "lockdetail" }
2017-02-15T17:20:53.034+0800        Progress: 52164170/622513894    8%  (bytes)
2017-02-15T17:20:56.000+0800        Progress: 104398989/622513894   16% (bytes)
2017-02-15T17:20:59.016+0800        Progress: 156726761/622513894   25% (bytes)
2017-02-15T17:21:02.015+0800        Progress: 201102195/622513894   32% (bytes)
2017-02-15T17:21:05.030+0800        Progress: 252299457/622513894   40% (bytes)
2017-02-15T17:21:08.033+0800        Progress: 306753671/622513894   49% (bytes)
2017-02-15T17:21:11.020+0800        Progress: 361233088/622513894   58% (bytes)
2017-02-15T17:21:14.027+0800        Progress: 408729106/622513894   65% (bytes)
2017-02-15T17:21:17.011+0800        Progress: 462108350/622513894   74% (bytes)
2017-02-15T17:21:20.000+0800        Progress: 512628006/622513894   82% (bytes)
2017-02-15T17:21:23.013+0800        Progress: 570320886/622513894   91% (bytes)
3502968 objects found
2017-02-15T17:21:25.962+0800    Creating index: { key: { _id: 1 }, name: "_id_", ns: "applock.lockdetail" }

查看版本:
db.version()

关闭:
use admin
db.shutdownServer()

另外:
如果mongodb启动不起来,报:child process failed, exited with error number 1,或者其它数字,检查是否目录都存在且具有写入权限,另外尝试用repaire修复

slave执行show dbs
rs.slaveOk()
不然会报错 Error: listDatabases failed:{ "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" }

MongoDB中查询转换(将时间戳转变通用日期格式)

在mongodb中日期保存的是long形的,但是打印出来不好看,需要在查询的时候做一定的转化,代码如下:

Date.prototype.Format = function (fmt) { //author: meizz 
var o = {
    "M+": this.getMonth() + 1, //月份 
    "d+": this.getDate(), //日 
    "h+": this.getHours(), //小时 
    "m+": this.getMinutes(), //分 
    "s+": this.getSeconds(), //秒 
    "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
    "S": this.getMilliseconds() //毫秒 
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
db.getCollection('state').find({"isall":"1"}).sort({"st":-1}).forEach(function (a) { a["st"] = (new         Date(a["st"]).Format("yyyy-MM-dd"));a["ut"] = (new Date(a["ut"]).Format("yyyy-MM-dd")); printjson(a) })

核心是需要利用forEach去遍历。