æ¬çªç°å¢ã§mongaã䜿çšããããšã¯è°è«ã®äœå°ã®ãããããã¯ã§ãã
äžæ¹ã§ã¯ããã¹ãŠãã·ã³ãã«ã§äŸ¿å©ã§ããããŒã¿ãé
眮ããã¬ããªã±ãŒã·ã§ã³ãèšå®ããããŒã¿éã®å¢å ã«äŒŽã£ãŠããŒã¿ããŒã¹ãåå²ããæ¹æ³ãç解ããŸãã ãã®äžæ¹ã§ãå€ãã®ææç©èªããããŸã ãã¢ãã£ãŒã«ã¯åœŒã®æåŸã®ãžã§ãã»ã³ãã¹ãã§ããŸãè¯å®çãªçµè«ãåºããŸããã§ããã
å®éãmongoãã¡ã€ã³ã®ããŒã¿ãŠã§ã¢ããŠã¹ã§ãããããžã§ã¯ããéåžžã«å€ãã1ã¡ãŒãã«ãããã®mongodbãµããŒãã«ã€ããŠããå°ããããŸããã åã«ã¡ããªãã¯ãåéããŠã¢ã©ãŒããèšå®ããããããæå³ã®ãããç£èŠãäœæããã®ãã¯ããã«é£ããããããã®ã¿ã¹ã¯ãé·ãéãã©ãã°ããŸããã ã©ã®ææšã远跡ããããç解ããã«ã¯ããŸããœãããŠã§ã¢ã®åäœã®ç¹åŸŽãç解ããå¿ èŠããããŸãã
å°é£ãåé¡ã«ã€ããŠã¯ãmongodbãžã®ãªã¯ãšã¹ããç£èŠããäŸã玹ä»ããããšæããŸãã
3ã€ã®åŽé¢ããããŒã¿ããŒã¹ã調ã¹ãå¿ èŠããããŸãã
ãµãŒããŒãªãœãŒã¹ïŒããã»ããµãã¡ã¢ãªããã£ã¹ã¯ãµãã·ã¹ãã ããããã¯ãŒã¯ïŒã®ç£èŠã è€éãªããšã¯ãããŸãããã»ãšãã©ã®ç£èŠã·ã¹ãã ã¯ãããéåžžã«ããŸãåŠçããŸãã
ããŒã¿ããŒã¹ã®å éšïŒæ¥ç¶ãã€ã³ããã¯ã¹ããã£ãã·ã¥ããã£ã¹ã¯ã®æäœãäžæããŒãã«ãã¬ããªã±ãŒã·ã§ã³ããœãŒããªã©ïŒã®ç£èŠã éåžžããã®ãããªã¡ããªãã¯ã¯ãããŒã¿ããŒã¹ã®åæ§ææ¹æ³ãäžè¶³ããŠãããµãŒããŒãªãœãŒã¹ãäœæããã€ã³ããã¯ã¹ãç解ããããã«å¿ èŠã§ãã
ãªã¯ãšã¹ãã®ç£èŠïŒäœä»¶ãã©ã®çš®é¡ã®ãªã¯ãšã¹ããè² è·ãçºçããããããã©ãã£ãã¯ããªã¯ãšã¹ãæéïŒã ç§ãã¡ã®çµéšã§ã¯ãããŒã¿ããŒã¹ã®åé¡ã®ã»ãšãã©ã¯ãã¢ããªã±ãŒã·ã§ã³ããè² è·/èŠæ±ãããã¡ã€ã«ãå€æŽãããšãã«çºçããŸããããšãã°ïŒ
- ã¢ããªã±ãŒã·ã§ã³ããããã€ãã®æ°ããæé©ã§ãªãèŠæ±ããããŸãã
- ã¯ãšãªæ¡ä»¶ãå€æŽãããã€ã³ããã¯ã¹ãå¹æçã«æ©èœããªããªã£ã
- ããŒãã«ãæé·ããã·ãŒã±ã³ã·ã£ã«èªã¿åããé«éã§ãªããªã£ã
ãããŸã§ã®ãšããããªã¯ãšã¹ãã®ç£èŠã«éå®ããŠããŸãã
ç£èŠã«ã€ããŠè©±ããŠããã®ã§ãç¹å®ã®ãªã¯ãšã¹ãã«ã¯é¢å¿ããªãããã¹ãŠã®ãªã¯ãšã¹ããããã€ãã®åäžã®å®è¡ãã©ã³ã«åŸã£ãŠã°ã«ãŒãåããŸãïŒããšãã°ã pg_stat_statementsã® postgresqlã¯å®éã®ãã©ã³ã«åŸã£ãŠãªã¯ãšã¹ããã°ã«ãŒãåããŸãïŒã
mongodbã®å Žåããªã¯ãšã¹ãèå¥åã¯ãªã¯ãšã¹ãã®ã¿ã€ãïŒæ€çŽ¢ãæ¿å
¥ãæŽæ°ãfindAndModifyãéçŽãªã©ïŒãããŒã¿ããŒã¹ãã³ã¬ã¯ã·ã§ã³ãããã³ãªã¯ãšã¹ãèªäœãå«ãbsonããã¥ã¡ã³ãã§ãã
ç°¡åã«ããããã«ãã¯ãšãªã®ãã¹ãŠã®ãã£ãŒã«ãå€ããïŒãã«çœ®ãæããããšã§ã¯ãšãªãã°ã«ãŒãåã§ãããšå€æããŸããã ãã£ãŒã«ãã§äžŠã¹æ¿ããŸãã
ããšãã°ããªã¯ãšã¹ãïŒ
{"country": "RU", "city": "Moscow", "$orderby": {"age": -1}}
ã«å€ãã
{country: ?, city: ?, $orderby: {age: ?}}
ãããŠãããŒã§ãœãŒããã
{$orderby: {age: ?}, city: ?, country: ?}
ã»ãšãã©ã®å Žåããã®ãããªã¯ãšãªã¯ãç¹å®ã®æ¡ä»¶ã«é¢ä¿ãªããåãã€ã³ããã¯ã¹ã䜿çšããŸãã
次ã®å€§ããªè³ªåïŒãªã¯ãšã¹ãã®ã¹ããªãŒã å šäœããªã¢ã«ã¿ã€ã ã§åä¿¡ããæ¹æ³ã
mongodbã§ã®å¯äžã®éåžžã®æ¹æ³ã¯ã profilerã§ãã åãªã¯ãšã¹ãã®çµ±èšããã£ããä»ãã³ã¬ã¯ã·ã§ã³ã«æžã蟌ã¿ãŸãã ãããã¡ã€ã©ãŒã¯ãé ããªã¯ãšã¹ãã®ã¿ãèšé²ãããïŒå®è¡æéãslowOpThresholdMsã§æå®ããããã®ããé·ãå ŽåïŒããŸãã¯ãã¹ãŠã®ãªã¯ãšã¹ããå®å šã«èšé²ã§ããŸãã 2çªç®ã®ã±ãŒã¹ã§ã¯ãmongodbèªäœã®ããã©ãŒãã³ã¹ãäœäžããå¯èœæ§ããããŸãã
ãã®ã¢ãããŒãã®å©ç¹ã«ã¯ãåãªã¯ãšã¹ãã®ããã©ãŒãã³ã¹ã«é¢ããéåžžã«è©³çŽ°ãªçµ±èšãå«ãŸããŸãã
ãã ããã¯ã©ã€ã¢ã³ãã®ãµãŒããŒã®ããã©ãŒãã³ã¹ã«æªåœ±é¿ãäžããªãããšãéåžžã«éèŠã§ãããããã£ãŠããã¹ãŠã®ãªã¯ãšã¹ãã®èšé²ã¢ãŒãã§ãããã¡ã€ã©ãŒã䜿çšããããšã¯ã§ããŸããã å šäœåãèŠããªãããããé ããã¯ãšãªã ãã§ã¯ååã§ã¯ãããŸããã
- ã©ã®ãªã¯ãšã¹ãããµãŒããŒã«æ倧ã®è² è·ããããã
- ã©ã®ãªã¯ãšã¹ãããµãŒããŒãžã®ã¡ã€ã³ã®çä¿¡/çºä¿¡ãã©ãã£ãã¯ãäœæããã
- é¢å¿ã®ãããªã¯ãšã¹ãã«ã€ããŠã¯ãå¿çæéã®ååžãåç §ããŠãã ãã
- æ¯ç§äœåãèŠæ±ããã®ã
ç§ãã¡ã®çµéšã§ã¯ãåé¡ã¯å€ãã®å Žåã以å1ããªç§å®è¡ãããŠããé«åšæ³¢ãªã¯ãšã¹ãã«ãã£ãŠäœæãããäœããã®çç±ã§5ããªç§å®è¡ãããããã«ãªããŸããã ãŸãããªã¯ãšã¹ã> 100msïŒããã©ã«ãã¯slowOpThresholdMsïŒã¯éåžžå ¬åŒïŒç®¡çè /çµ±èšïŒã§ãããéåžžã«ãŸãã§ãã
æšæºãããã¡ã€ã©ãé©åããªãã£ãããããã©ãã£ãã¯ã¹ãããã£ã³ã°ã®æ¹åãæãå§ããŸããã æåã®æ®µéã§ã¯ãããã€ãã®è³ªåãèŠã€ããå¿ èŠããããŸããã
- ã¹ãããã£ã³ã°çšã®goã©ã€ãã©ãªïŒãšãŒãžã§ã³ãã¯golangã§èšè¿°ãããŠããŸãïŒ
- ããã©ãŒãã³ã¹ïŒå€§éã®ãã©ãã£ãã¯ãããŒããªãã¹ã³ãããšãã«ãªãœãŒã¹ãæ¶è²»ãããšãŒãžã§ã³ãã®éïŒ
- Mongodbãããã³ã«è§£æ
mongodbãã©ã°ã€ã³ã®ãããã¿ã€ãã¯ã gopacketã©ã€ãã©ãªã䜿çšããŠæ°æ¥ã§äœæãããŸããã libpcapãä»ããŠãã±ãããã€ã³ã¿ãŒã»ãããããããã³ã«ã解æããbsonããã¥ã¡ã³ãã¯mgoã䜿çšããŠãã·ãªã¢ã©ã€ãºããŸãã ã
è² è·ã®ããã£ãmongodbã®ã€ã³ã¹ããŒã«ããªããããã¹ã¿ã³ããäœæããæ¢è£œã®ãã³ãããŒã¯ãéå§ããŸããã ç§ãã¡ã®å Žåãmongodbãšã·ã³ã«ãŒã¯ã2ã€ã®ã³ã¢ãš2Gbã®ã¡ã¢ãªãåããåãä»®æ³ãã·ã³äžã«ãããŸããã è² è·ã®èŠ³ç¹ããèŠããšã1ç§ãããçŽ1äžãã±ããããã©ãã£ãã¯ã¯60Mbit / sã§ããã
ãã®ãããªè² è·ã®äžã§ã®ãããã¿ã€ãã¯ã1ã€ã®ããã»ããµã³ã¢ã®çŽ70ïŒ ã䜿çšããŸããã ã³ãŒãã®ãããã¡ã€ã«ãšæé©åãå¿ èŠã§ããããšãæããã«ãªããŸããã ããã§ã¯ãæšæºã®golang ãããã¡ã€ã©ãŒã«æ¬æãè¡šãã䟡å€ããããŸããäœãçºæããå¿ èŠã¯ãããŸããã§ãããã³ãŒãã®æãCPUãæ¶è²»ããã»ã¯ã·ã§ã³ã調æŽããGCã®è² è·ãæžããããã«ã§ããã ãã¡ã¢ãªãå²ãåœãŠãªãããã«ããŸããã
æé©åããã»ã¹ãæ£ç¢ºã«åçŸããããšã¯ã§ããŸããããæãéèŠãªå€æŽã®äŸã瀺ããŸãã
bson.Unmarshal slow
mongoã®bsonãªã¯ãšã¹ãããã¥ã¡ã³ãã¯ãããããèŸæžã§ããããã®æå³ãåãèŸæžã«ããããšãã§ããŸãã
æåããã¯ãšãªãæ£èŠåããããšã決ãããããå
ã®èŸæžã®èŠçŽ ã®å€ãèŸæžã§ãªãå Žåããããã®å€ããŸã£ããèªã¿åããªãå¯èœæ§ããããŸãã
ä»æ§ãååŸããŠãç¬èªã®ããªããã£ããã·ãªã¢ã©ã€ã¶ãŒãäœæããŸãã çµæã¯ã100è¡ã®é¢æ°ã§ã
elementValueType, err = reader.ReadByte() if err != nil { break } payload, err = reader.ReadBytes(nullByte) if err != nil { break } elementName = string(payload) switch elementValueType { case bsonDouble, bsonDatetime, bsonTimestamp, bsonInt64: if _, err = reader.ReadN(8); err != nil { break } case bsonString: l, err = reader.ReadInt() if err != nil { break } payload, err = reader.ReadN(l) if err != nil { break } elementValue = string(payload[:len(payload)-1]) case bsonJsCode, bsonDeprecated, bsonBinary, bsonJsWithScope, bsonArray: l, err = reader.ReadInt() if err != nil { break } if _, err = reader.ReadN(l - 4); err != nil { break } case bsonDoc: elementValue, _, _, err = readDocument(reader) if err != nil { break } case bsonObjId: if _, err = reader.ReadN(12); err != nil { break } case bsonBool: if _, err = reader.ReadByte(); err != nil { break } case bsonRegexp: if _, err = reader.ReadBytes(nullByte); err != nil { break } if _, err = reader.ReadBytes(nullByte); err != nil { break } case bsonDbPointer: l, err = reader.ReadInt() if err != nil { break } if _, err = reader.ReadN(l - 4 + 12); err != nil { break } case bsonInt32: if _, err = reader.ReadN(4); err != nil { break } }
ãã¹ãŠã®ãã£ãŒã«ããªãã·ã§ã³ã®äžã§ãbsonDocumentïŒååž°åŒã³åºãïŒãšbsonStringïŒã³ã¬ã¯ã·ã§ã³ãšãªã¯ãšã¹ãã®ã¿ã€ãã決å®ããããã®è¿œå ã®ããžãã¯ããããŸãïŒã®å€ã®ã¿ãèªã¿åããæ®ãã®ãã£ãŒã«ããã¹ãããããŸãã
ããã±ãŒãžããã£ããããæ¹æ³
ãã¹ãã§ã¯ã rawãœã±ããã䜿çšãããšãpcapã䜿çšãããããé«éã§ããããšãçŽæ¥èšŒæãããŸããã
ããããããã¯å€ãããŒãžã§ã³ã®libpcapã«ãããã®ã§ããããLinuxã§ã®ã¿ã¹ããã¡ãŒãå®è¡ããããšãèšç»ããŠããããããããç解ããã«gopacket.af_packetã䜿çšããããšã«ããŸãã ïŒç¹ã«ãšãŒãžã§ã³ããlibpcapã«ãªã³ã¯ããå¿
èŠããªãããïŒã
rawãœã±ããã¯Linuxã®ç¹å¥ãªãœã±ããã§ãããã䜿çšããŠããŠãŒã¶ãŒç©ºéïŒã«ãŒãã«ã§ã¯ãªãïŒã§å®å šã«åœ¢æããããã±ãããéä¿¡ããããç¹å®ã®ãããã¯ãŒã¯ã€ã³ã¿ãŒãã§ã€ã¹ãããã±ãããåä¿¡ãããã§ããŸãã ã¹ãããã£ã³ã°ã«ã€ããŠè©±ããšãã«ãŒãã«ããã®ãã±ããã¯åŸªç°ãããã¡ãŒãä»ããŠãŠãŒã¶ãŒç©ºéã«éããããããsyscallãåãã±ãããã€ã³ã¿ãŒã»ããããå¿ èŠããªããªããŸãã ã«ãŒãã«ã®ããã¥ã¡ã³ãã«ã¯ãäž»é¡ã«é¢ãã詳现ãªçæžãããããŸãã
ãŒãã³ããŒ
1ã€ã®ã¹ããªãŒã ã§ãã±ãããåŠçããããã ZeroCopyã¹ããã¡ãŒã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšã§ããŸãã ãããåæã«ããã®ã¡ã¢ãªãžã®ãªã³ã¯ãã³ãŒãå ã«æ®ããŠã¯ãªããªãããšãèŠããŠããå¿ èŠããããŸãã
ããã±ãŒãžã®è§£æ
gopacketã®ãã±ããåæã€ã³ã¿ãŒãã§ã€ã¹ã¯éåžžã«æè»ã§ãããã«äœ¿çšã§ããå€ãã®ç°ãªããããã³ã«ããµããŒãããŸãããŠãŒã¶ãŒã¯ãããã¬ãã«ã®ããŒã¿ãã©ã®ããã«ã«ãã»ã«åãããããèããå¿ èŠã¯ãããŸããã ãã ããããã«äŒŽãããã®ã€ã³ã¿ãŒãã§ã€ã¹ã§ã¯å€§éã®ããŒã¿ã³ããŒãå¿ èŠã«ãªãããã®çµæãCPUãšGCã®äž¡æ¹ã«å€§ããªè² è·ãããããŸãã
ç¹°ãè¿ããŸãããäžèŠãªãã®ã¯ãã¹ãŠæšãŠãããšã«ããŸããã
ãœãŒã¹ã€ãŒãµããããã¬ãŒã ããã®ã¿ã¹ã¯ïŒããã³AF_PACKETã®åºåã§ã¯åžžã«ã€ãŒãµããããååŸããŸãïŒã¯ã次ã®ãã®ãååŸããããšã§ãã
- ãœãŒã¹IP
- å®å IP
- éä¿¡å ããŒã
- å®å ããŒã
- TCP seqïŒä»¥äžã§å¿ èŠãªçç±ã説æããŸãïŒ
- TCPãã€ããŒãïŒãããã¬ãã«ãããã³ã«ããŒã¿èªäœïŒ
ç°¡åã«ããããã«ããŸã IPv6ããµããŒãããªãããšã決å®ãããŸããã
func DecodePacket(data []byte, linkType layers.LinkType, packet *TcpIpPacket) (err error) { var l uint16 switch linkType { case layers.LinkTypeEthernet: if len(data) < 14 { ethernetTooSmall.Inc(1) err = errors.New("Ethernet packet too small") return } l = binary.BigEndian.Uint16(data[12:14]) switch layers.EthernetType(l) { case layers.EthernetTypeIPv4: data = data[14:] case layers.EthernetTypeLLC: l = uint16(data[2]) if l&0x1 == 0 || l&0x3 == 0x1 { data = data[4:] } else { data = data[3:] } default: ethernetUnsupportedType.Inc(1) err = errors.New("Unsupported ethernet type") return } default: unsupportedLinkProto.Inc(1) err = errors.New("Unsupported link protocol") return } //IP var cmp int if len(data) < 20 { ipTooSmallLength.Inc(1) err = errors.New("Too small IP length") return } version := data[0] >> 4 switch version { case 4: if binary.BigEndian.Uint16(data[6:8])&0x1FFF != 0 { ipNonFirstFragment.Inc(1) err = errors.New("Non first IP fragment") return } if len(data) < 20 { ipTooSmall.Inc(1) err = errors.New("Too small IP packet") return } hl := uint8(data[0]) & 0x0F l = binary.BigEndian.Uint16(data[2:4]) packet.SrcIp[0] = data[12] packet.SrcIp[1] = data[13] packet.SrcIp[2] = data[14] packet.SrcIp[3] = data[15] packet.DstIp[0] = data[16] packet.DstIp[1] = data[17] packet.DstIp[2] = data[18] packet.DstIp[3] = data[19] if l < 20 { ipTooSmallLength.Inc(1) err = errors.New("Too small IP length") return } else if hl < 5 { ipTooSmallHeaderLength.Inc(1) err = errors.New("Too small IP header length") return } else if int(hl*4) > int(l) { ipInvalieHeaderLength.Inc(1) err = errors.New("Invalid IP header length > IP length") return } if cmp = len(data) - int(l); cmp > 0 { data = data[:l] } else if cmp < 0 { if int(hl)*4 > len(data) { ipTruncatedHeader.Inc(1) err = errors.New("Not all IP header bytes available") return } } data = data[hl*4:] case 6: ipV6IsNotSupported.Inc(1) err = errors.New("IPv6 is not supported") return default: ipInvalidVersion.Inc(1) err = errors.New("Invalid IP packet version") return } //TCP if len(data) < 13 { tcpTooSmall.Inc(1) err = errors.New("Too small TCP packet") return } packet.SrcPort = binary.BigEndian.Uint16(data[0:2]) packet.DstPort = binary.BigEndian.Uint16(data[2:4]) packet.Seq = binary.BigEndian.Uint32(data[4:8]) dataOffset := data[12] >> 4 if dataOffset < 5 { tcpInvalidDataOffset.Inc(1) err = errors.New("Invalid TCP data offset") return } dataStart := int(dataOffset) * 4 if dataStart > len(data) { tcpOffsetGreaterThanPacket.Inc(1) err = errors.New("TCP data offset greater than packet length") return } packet.Payload = data[dataStart:] return }
ãã®ãããªé¢æ°ã«ã€ããŠã¯ã ãã³ãããŒã¯ãæžã䟡å€ãåžžã«ãããŸãã ãä»åã¯ããªãè¯ãåçãåŸãããŸããã
Benchmark_DecodePacket-4 50000000 27.9 ns/op Benchmark_Gopacket-4 1000000 3351 ns/op
ã€ãŸãã100å以äžã®å éãåŸãããŸããã
ãã®é¢æ°ã®ã³ãŒãã®éèŠãªéšåã¯ãšã©ãŒåŠçã§ããããŸããŸãªã«ãŠã³ã¿ãŒã®å¢åã確èªããŠããããããšãŒãžã§ã³ããµãŒãã¹ã¡ããªãã¯ãäœæããã¹ããã¡ãŒãäœããã®çç±ã§æ©èœããªãçç±ãç°¡åã«ç解ã§ããŸãã ããšãã°ããã®ã¡ããªãã¯ã«ãã£ãŠIPv6ã®ãµããŒããè¿œå ããå¿ èŠæ§ã«ã€ããŠåŠç¿ããäºå®ã§ãã
ãŸããããŒã¿ã1ã€ã®ã€ãŒãµããããã¬ãŒã ã«åãŸããªãå Žåãç°ãªãããã±ãŒãžããã®tcpãã€ããŒããæ¥çããããšã¯ããŸããã
ãã®ãããªããã±ãŒãžãmongodbã®çãã§ããå ŽåãããããŒã«ã®ã¿é¢å¿ããããŸãããããšãã°ã倧ããªæ¿å
¥èŠæ±ã®å Žåãæåã®ããã±ãŒãžããèŠæ±éšåãååŸããã ãã§ãã
ããã±ãŒãžã®è€è£œ
ã¯ã©ã€ã¢ã³ããšãµãŒããŒãåããµãŒããŒäžã«ããå Žåãåãããã±ãŒãžã2åãã£ããããããšãå€æããŸããã
src ip + portãdest ip + portãããã³TCP seqã«åºã¥ããåçŽãªãã±ããéè€æé€æ©èœãäœæããå¿
èŠããããŸããã
åèš
- ãã®çµæããã³ãããŒã¯ã§ã¯ããšãŒãžã§ã³ãã¯ã³ã¢ã®70ïŒ ã§ã¯ãªãã5ïŒ ãæ¶è²»ãå§ããŸãã
- ããã«ã€ããŠã¯ãä»ã®ãšããæé©åããããããšã«ããŸããããããå°ãå éããæ¹æ³ãããã€ããããŸãã
- ã¯ã©ã€ã¢ã³ãã®å®éã®è² è·ã®äžã§ããšãŒãžã§ã³ãã¯ã»ãŒåãã€ã³ãžã±ãŒã¿ãŒã§åäœããŸãïŒãã³ãããŒã¯ã®å Žåãšåãããã«ãã±ããã®æ°ã«æ¯äŸããŠCPUãæ¶è²»ãããŸãïŒ