MongoDB中的$in和$elemMatch

PC客户端开发多年,一直想涉足下Web开发,进入15年,想起14年有一个计划是用ruby写一个网站,已经无法完成了!亡羊补牢也不晚,于是动手搞起,打算山寨goodreads的quotes板块,已经草草的实现了部分功能-看这里, 其中数据库部分使用到是MongoDB, 使用MongoDB的原因是新鲜、好玩、简单,MongoDB确实比较容易理解,花2个小时看了一遍the-little-mongodb-book就可以上手干活了。

但高级些的用法还是需要细读官方文档,不然还是会出错,最近自己就在$in和$elemMatch上栽跟头,花费了大把时间。

1
{ _id: 1, quote: "不要着急,最好的总会在最不经意的时候出现", author: 泰戈尔, tags: [ "哲理", "人生" ]}

比如(上面的例子),我需要根据tag哲理查找tags中包含哲理的记录,怎样写query语句呢?借助于万能的google,$in和$elemMatch很快进入了我的视线,从字面理解,$in是查询指定field是否在某个集合中,看mongo的官方文档$in的用法也确实是这样讲的:

The $in operator selects the documents where the value of a field equals any value in the specified array.

一眼看去,并不适于我们的需求。再看看$elemMatch:

The $elemMatch operator matches documents in a collection that contain an array field with at least one element that matches all the specified query criteria.

$elemMatch专门用于查询数组Field中元素是否满足指定的条件。看起来适合我们的需求,像这样:

1
 db.items.find({"tags": {$elemMatch: {$equal: "哲理"}}}) ;错误

上面的用法是错误的,因为mongo并没有$equal查询指令, 所以我们无法用$elemMatch达成我们的需求。

当发现$elemMatch无法满足功能需求时,自己陷入了错误的方向:怀疑用错了$elemMatch, 然后就一直在网上看别人如何使用$elemMatch的, 而没有回头看下$in到用法,直到走投无路时。我发现$in到第二个用处:

Use the $in Operator to Match Values in an Array

并且官方示例和自己的需求场景几乎一模一样,接着当然自己很快实现了按tag查找的功能:

1
db.items.find({"tags": {$in: ["哲理"]}})

总结下$in和$elemMatch:

  • $in要查询的Field可以是普通值也可以是数组,而$elemMatch则是专门用于查询array Field的。
  • $in的查询相对简单,主要是查询值或数组中某个值是否在待查询列表中.
  • $elemMatch的查询则比较复杂,是查询Array Field中的元素是否满足一系列的查询条件。

Windbg关联dmp文件

最近一段时间和Crash斗争,每天必不可少的事情是分析dump,每天少则分析几个,多则分析几十个是常有的。而打开dump到进入windbg cmd窗口输入!analyze -v命令是机械枯燥的事情。Windbg是没有默认关联.dmp文件的,只能自己动手了。分享关联方法之前,看下我现在dump文件的打开方式:

我设置了三种打开方式:

  • 直接使用Windbg打开dump
  • 使用windbg打开dump并进行分析
  • 使用windbg打开dump,切换到32位,然后分析(针对加载的wow64)

配置dmp关联的方法

1. 设置dmp文件的关联

Windows下文件的关联指的是在explorer下,双击或通过右键打开文件时选择指定程序打开。文件的关联方式可以通过注册表进行配置,在HKEY_CLASSES_ROOT、HKEY_CURRENT_USER\Software\Classes、HKEY_LOCAL_MACHINE\Software\Classes有以 “.文件格式” 命名的键和另一个自定义的键里面保存具体的配置,用来配置这个文件格式的关联,比如我们要配置的dmp文件的关联,最终会是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HKEY_CURRENT_USER\Software\Classes
   dmpfile
     DefaultIcon
       default = "C:\Program Files (x86)\Debugging Tools for Windows (x86)\windbg.exe",0
     shell
       Analyze with windbg
           command
              default=C:\Program Files (x86)\Debugging Tools for Windows (x86)\windbg.exe" -c "!analyze -v" -z "%1\"
       Analyze with windbg - wow64
           command
              default="C:\Program Files (x86)\Debugging Tools for Windows (x86)\windbg.exe" -c "!wow64exts.sw; !analyze -v" -z "%1"
       open
           command
              default="C:\Program Files (x86)\Debugging Tools for Windows (x86)\windbg.exe" -z "%1"
   .dmp
     default = dmpfile

当然,更改HKEY_CURRENT_USER是对当前用户有效,HKEY_LOCAL_MACHINE是对所有用户有效,而HKEY_CLASSES_ROOT是兼容的产物,不推荐在直接配置HKEY_CLASSES_ROOT。

2. 去除用户自定义的打开方式-自定义程序

如果你设置过打开方式里面的自定义程序,必须先删除这一项,否则我们上一步设置的文件关联将不会生效,打开方式自定义程序的配置也是保存在注册表中,具体位置在:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.dmp\UserChoice

将这个UserChoice键删除即可。

3. 到这里就讲完了,最后奉上一个bat:

(windbg-dump-asso.bat) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@echo off

set dbgpath=\"C:\Program Files (x86)\Debugging Tools for Windows (x86)\windbg.exe\"


REG ADD "HKCU\Software\Classes\.dmp" /f /d dmpfile

set val=%dbgpath%,0
REG ADD "HKCU\Software\Classes\dmpfile\DefaultIcon" /f /d "%val%"

set val=%dbgpath% -z \"%%1\"
REG ADD "HKCU\Software\Classes\dmpfile\Shell\open\command" /f /d "%val%

set val=%dbgpath% -c \"!wow64exts.sw; !analyze -v\" -z \"%%1\"
REG ADD "HKCU\Software\Classes\dmpfile\Shell\Analyze With Windbg  - wow64\command" /f /d "%val%"

set val=%dbgpath% -c \"!analyze -v\" -z \"%%1\"
REG ADD "HKCU\Software\Classes\dmpfile\Shell\Analyze With Windbg\command" /f /d "%val%"

REG DELETE "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.dmp\UserChoice" /f

set /p finish="finsh!"

参考:

http://msdn.microsoft.com/en-us/library/cc144158%28VS.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/hardware/ff561306.aspx

Windbg定位内存泄露的一种简单方法

前两天接到一个反映进程内存占用过G的投诉。问题是必現的,一定是内存泄露,应该容易定位,一同事远程看过现场,使用gflags和windbg试图找到泄露的堆栈,同事是一步步按照这篇文章的方法来的,但在最后一步Windbg没有找到出问题的堆栈,用户给我们远程的时间很短,无法深究gflags+windbg不灵验的原因,只得另辟蹊径。

步骤如下:

0. 安装windbg, 设置symbols, 用windbg attach到发生内存泄露的进程

1. 打印出heap的使用情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0:003> !heap -s
LFH Key : 0x7ce97b7b
LFH Key : 0x7ce97b7b
Termination on corruption : ENABLED
Heap     Flags    Reserv Commit  Virt   Free   List    UCR    Virt  Lock Fast
                  (k)    (k)     (k)    (k)    length  blocks cont. heap
-----------------------------------------------------------------------------
002c0000 00000002 1024    372     1024   54    13      1      0     0    LFH
00010000 00008000 64      4       64 2   1     1       0      0
00020000 00008000 64      64      64     62    1       1      0     0
004d0000 00001002 1088    152     1088   7     4       2      0     0    LFH
007c0000 00001002 1088    188     1088   18    7       2      0     0    LFH
00880000 00001002 1280    276     1280   14    5       2      0     0    LFH
01db0000 00001002 64      12      64     2     3       1      0     0
021f0000 00001002 15488   12024   15488  144   7       5      0     0    LFH
00810000 00001002 64      12      64     2     3       1      0     0

很明显这一行:021f0000 00001002 15488 12024 15488 144 7 5 0 0 LFH是有异常的。

2. 显示异常heap的信息

1
2
3
4
5
6
7
8
0:003> !heap -stat -h 021f0000
heap @ 021f0000
heap @ 021f0000
group-by: TOTSIZE max-display: 20
  size   #blocks  total  ( %) (percent of total busy bytes)
  a45c   11d   -  b6fa6c (99.75)
  75a8   1     -  75a8   (0.25)
  20     1     -  20     (0.00)

上面11d块size为a45c的内存极有可能是泄露的内存。

3. 根据泄露内存的Size找到CallStack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
0:003> bp ntdll!RtlAllocateHeap "j(poi(@esp+c) = 0x0a45c) 'k';'gc'"
0:003> g
0:003> g
Unable to deliver callback, Unable to deliver callback, 3131

ChildEBPChildEBP RetAddrRetAddr


0021de54 1000ba7e 0021de54 1000ba7e ntdll!RtlAllocateHeapntdll!RtlAllocateHeap


WARNING: Stack unwind information not available. Following frames may be wrong.
WARNING: Stack unwind information not available. Following frames may be wrong.
0021de6c 1000bbcc 0021de6c 1000bbcc mfnspstd32mfnspstd32++0xba7e0xba7e


0021de8c 1000beb1 0021de8c 1000beb1 mfnspstd32mfnspstd32++0xbbcc0xbbcc


0021deb8 1000ea4d 0021deb8 1000ea4d mfnspstd32mfnspstd32++0xbeb10xbeb1


0021dee4 75de9986 0021dee4 75de9986 mfnspstd32!WSPStartupmfnspstd32!WSPStartup++0x9d0x9d


0021e3b8 75de975b 0021e3b8 75de975b WS2_32!DPROVIDER::InitializeWS2_32!DPROVIDER::Initialize++0x1850x185


0021e3d8 75df5a2f 0021e3d8 75df5a2f WS2_32!DCATALOG::LoadProviderWS2_32!DCATALOG::LoadProvider++0x6d0x6d


0021e678 75df5fe8 0021e678 75df5fe8 WS2_32!DCATALOG::FindIFSProviderForSocketWS2_32!DCATALOG::FindIFSProviderForSocket++0x630x63


0021e68c 75de4204 0021e68c 75de4204 WS2_32!DSOCKET::FindIFSSocketWS2_32!DSOCKET::FindIFSSocket++0x370x37


0021e6cc 00d48444 0021e6cc 00d48444 WS2_32!setsockoptWS2_32!setsockopt++0xb00xb0


0021e6ec 00d4900c 0021e6ec 00d4900c t**b!OPENSSL_Applinktadb!OPENSSL_Applink++0x7c140x7c14


0021e71c 00d3e50c 0021e71c 00d3e50c t**b!OPENSSL_Applinktadb!OPENSSL_Applink++0x87dc0x87dc


0021e738 00d4a654 0021e738 00d4a654 t**b++0x4e50c0x4e50c


0021e768 00d44f79 0021e768 00d44f79 t**b!OPENSSL_Applinktadb!OPENSSL_Applink++0x9e240x9e24


0021f8c8 00d4a6a4 0021f8c8 00d4a6a4 t**b!OPENSSL_Applinktadb!OPENSSL_Applink++0x47490x4749


0021f8d8 00cfb1f7 0021f8d8 00cfb1f7 t**b!OPENSSL_Applinktadb!OPENSSL_Applink++0x9e740x9e74


0021f91c 7700ee1c 0021f91c 7700ee1c t**b++0xb1f70xb1f7


0021f928 7731377b 0021f928 7731377b kernel32!BaseThreadInitThunkkernel32!BaseThreadInitThunk++0xe0xe


0021f968 7731374e 0021f968 7731374e ntdll!__RtlUserThreadStartntdll!__RtlUserThreadStart++0x700x70


0021f980 00000000 0021f980 00000000 ntdll!_RtlUserThreadStartntdll!_RtlUserThreadStart++0x1b0x1b

4. 最后甄别CallStack是否真正的发生内存泄露

总结

此方法适宜,泄露亦重现,且泄露的size固定的情况

C++API设计 - 笔记

C++ API设计从书名看是关于设计C++接口的书,内容却不仅仅是关于C++接口设计。书还算实用,但如果看过代码大全和Effective系列的话,可以不用在看这本书了。

笔记

P91 重构就像汽车高速行驶时更换引擎,但还不能停车

P103 API的第一个发行版本很重要

P115 LISKOV替换原则指出,如果S是T的子类,那么在行为上,S不需要修改就可以替换T类型的对象

P117 私有继承、Was-a的关系

P119 开发封闭原则是一种启发式的原则,则不是必须遵守的

P121 一个好的名字往往可以表达类的意图,如果一个类难以命名,这往往是缺乏设计的信号

P122 函数尽量使用正面的概念命名,比如IsConnect而不是IsUnConnect

P132 C API比C++ API具有更好的二进制兼容性

P140 可以使用预处理器技术模拟模板

P176 避免使用友元,这往往意味这糟糕的设计

P182 如果没有必要使用Dynamic_Cast则常见的做法是关闭运行时信息生成

P182 不要猜测性能瓶颈的位置

P183 传值可能会导致对象切割而引起莫名其妙的问题

P185 不应该前置声明STL的类型

P192 C++11中一个构造函数可以调用其他的构造函数

P192 对象越小,就越适合缓存

P197 Donald Knuth有句名言:过早优化是万恶之源

P211 Linux 的奇数版本号代表开发版本

P216 如果确实需要做二进制不兼容,则需要考虑给信库起个新名字。比如zlib.dll –> zlib1.dll

P225 弃用标记:__declspec(deprecated)

P250 时间驱动、质量驱动和功能驱动

P257 性能测试的结果是实数而不是简单的真和假

P264 驱动测试开发的好处是推动你考虑接口会被如何调用

P286 SWIG

P286 任何脚本绑定技术都是建立在适配器模式上的

P286 Boost.Python支持Boost和Python的互相交互

P265 模拟对象又可以简单称为MOCK

P339 每个DLL有相应的到入库lib文件,虽然静态库和导入库的格式都是lib但他们的文件类型是不同的

读《摩托车修理店的未来工作哲学》

<摩托车修理店的未来工作哲学>是一本大抵关于工作的书。

薪水的意义

记得刚毕业的头一年月薪虽少,但内心竟极不安:我每天干的这点事情配这些钱吗? 到现在又换过两次工作,薪水比刚毕业高了不少,起初还是惶恐自己干的活不配拿这份工资,但随着时间的推移,慢慢麻木不再深思这件事情,将原因归结为人力市场经济的供求关系。现在看到此书中观点,自己可以心安理得的拿这份薪水了:对个体而言,薪水是一种把人限制在一个地方、一定时间做自己并不是特别享受的事情的补偿。

平素里讲的对某工作感兴趣都是相对而言,如果对工作的享受可以达到玩游戏、K歌、美食的程度那一定是自欺欺人。对工作如此兴致的话,想必不要工资也会全心投入,因为平时可以给你带来如此享受的东西都是要钱的。

站在公司的角度看,用薪水吸引人,也是无奈之举。来人是冲着薪水来的,并不是工作本身。

快乐的工作

选择程序员的工作无疑是幸福的。程序员号称是当代唯一的手工艺者,互联网公司的办公环境也相对自由,不必忍受像富士康等流水线工作中十年从事一个标准动作的枯燥。

程序员可以快乐工作的条件很简单,有代码可写、在不被打扰的工作环境里思考着软件的结构和编码。回想自己这几年来的3份工作,第一份工作上班并没有太多的事情可做,或者是布置的东西太虚,导致自己内心也空虚,终日惶惶。第二份工作,流程清晰,需求明确,类似于流水线操作不大会出问题。现在的工作,不断有有新挑战,安心下来写代码会很幸福,无奈流程待拨乱反正,每天被打断N次、往返于各种会议与讨论之中。

所以,程序员快乐的编码不容易。