🚙

💨 💨 💨

×

  • Categories

  • Archives

  • Tags

  • About

W Duolingo Notes

Posted on 09-19-2021 | In Misc

hexo s
…

看图写句模板 (WIT 法) 60s

不需要写太多,简单概括一下人物的外貌、动作和背景就OK,这里三个开头句轮番用就行。
记忆技巧: 用每个模板的开头字母来记, WIT

🔹第一句 (开头)

  1. This chromatic (/krəˈmætɪk/ 彩色的) and vivacious (/vɪˈveɪʃəs/ 活泼的, 有生气的) image depicts (/dɪˈpɪkts/ 描绘, 描写) that … . The weather outside is sunny/cloudy/rainy.

    👉 举例: This chromatic and vivacious photograph depicts that a muscular (/ˈmʌskjələr/ 肌肉发达的) man is pushing two grizzled (/ˈɡrɪzld/ 头发斑白的) elderly people angrily. The weather outside is sunny.

  1. We can see in this intriguing (/ɪnˈtriːɡɪŋ/ 引人入胜的, 有趣的) and quaint (/kweɪnt/ 古色古香的, 奇特的) picture that … . This vivacious image portrays (/pɔːrˈtreɪz/ 描绘, 展现) a fabulous (/ˈfæbjələs/ 极好的, 极妙的) situation.

    👉 举例: We can see in this intriguing and quaint picture that two cunning (/ˈkʌnɪŋ/ 狡猾的; 可爱的) boys are playing basketball with each other. This vivacious image portrays a fabulous situation.

  1. It can be manifestly (/ˈmænɪfestli/ 明显地) seen in this fabulous snapshot that … . I reckon (/ˈrekən/ 认为, 估计) that he/she is feeling blissful (/ˈblɪsfl/ 幸福的, 极乐的) / discontented (/ˌdɪskənˈtentɪd/ 不满的) / contented (/kənˈtentɪd/ 满足的).

    👉 举例: It can be manifestly seen in this fabulous snapshot that a hysterical (/hɪˈsterɪkl/ 歇斯底里的) boy is crying in front of a wall. I reckon he is being kidnapped, because this chromatic image portrays a dangerous scene.


🔹第二句 (人物外貌 + 状态)

  • 男人: personable (/ˈpɜːrsənəbl/ 品貌兼优的, 英俊的), muscular (/ˈmʌskjələr/ 肌肉发达的, 强壮的)
  • 女人: winsome (/ˈwɪnsəm/ 迷人的, 可爱的)
  • 妈妈: affectionate (/əˈfekʃənət/ 深情的, 有爱心的)
  • 小孩: cunning (/ˈkʌnɪŋ/ 狡猾的; <美>可爱的), lovable (/ˈlʌvəbl/ 可爱的), hysterical (/hɪˈsterɪkl/ 歇斯底里的)
  • 黑人: dark-skinned (/ˌdɑːrkˈskɪnd/ 深肤色的)
  • 工人: wholehearted (/ˌhoʊlˈhɑːrtɪd/ 全心全意的)
  • 老人: grizzled (/ˈɡrɪzld/ 头发斑白的), elderly (/ˈeldərli/ 上了年纪的)

🔹第三句 (景色背景)

  • The weather outside is …
  • It is tranquil (/ˈtræŋkwɪl/ 宁静的, 平静的) with serenity (/səˈrenəti/ 宁静, 安详).
  • The scenery is extremely fascinating (/ˈfæsɪneɪtɪŋ/ 极其迷人的), presenting a piece of enchanting (/ɪnˈtʃæntɪŋ/ 迷人的, 妩媚的) beauty.
  • This vivacious / black / white picture portrays (/pɔːrˈtreɪz/ 描绘, 展现) a fabulous (/ˈfæbjələs/ 极好的) situation.

👉 这样背诵时,你只要记住 WIT 三个首字母:

  • W = Weather + What (第一句:图像描述 + 天气)
  • I = Individual (第二句:人物外貌/动作)
  • T = Tranquil/Total scene (第三句:整体氛围/景色)

📘 看图写句单词本

🔹描述图像 (开头常用词)

  • chromatic /krəˈmætɪk/ adj. 彩色的
  • vivacious /vɪˈveɪʃəs/ adj. 活泼的;有生气的
  • depict /dɪˈpɪkt/ v. 描绘,描写
  • intriguing /ɪnˈtriːɡɪŋ/ adj. 引人入胜的;有趣的
  • quaint /kweɪnt/ adj. 古色古香的;奇特有趣的
  • portray /pɔːrˈtreɪ/ v. 描绘;展现
  • fabulous /ˈfæbjələs/ adj. 极好的;极妙的
  • manifestly /ˈmænɪfestli/ adv. 明显地;明白地
  • reckon /ˈrekən/ v. 认为;估计
  • blissful /ˈblɪsfl/ adj. 幸福的;极乐的
  • discontented /ˌdɪskənˈtentɪd/ adj. 不满的
  • contented /kənˈtentɪd/ adj. 满足的;心满意足的
  • snapshot /ˈsnæpʃɒt/ n. 快照,照片

🔹人物形容词 (外貌 + 状态)

  • personable /ˈpɜːrsənəbl/ adj. 英俊潇洒的;品貌兼优的
  • muscular /ˈmʌskjələr/ adj. 肌肉发达的;强壮的
  • winsome /ˈwɪnsəm/ adj. 迷人的;可爱的
  • affectionate /əˈfekʃənət/ adj. 深情的;充满爱意的
  • cunning /ˈkʌnɪŋ/ adj. 狡猾的;灵巧的;<美>可爱的
  • lovable /ˈlʌvəbl/ adj. 可爱的;讨人喜欢的
  • hysterical /hɪˈsterɪkl/ adj. 歇斯底里的;情绪失控的
  • dark-skinned /ˌdɑːrkˈskɪnd/ adj. 深肤色的
  • wholehearted /ˌhoʊlˈhɑːrtɪd/ adj. 全心全意的;真诚的
  • grizzled /ˈɡrɪzld/ adj. 头发斑白的;灰白的
  • elderly /ˈeldərli/ adj. 上了年纪的;年老的

🔹景色描写 (环境 + 氛围)

  • tranquil /ˈtræŋkwɪl/ adj. 宁静的;平静的
  • serenity /səˈrenəti/ n. 平静;宁静
  • fascinating /ˈfæsɪneɪtɪŋ/ adj. 极其迷人的;吸引人的
  • enchanting /ɪnˈtʃæntɪŋ/ adj. 迷人的;妩媚的

🎧 互动听力总结题 (75s)

参考: https://zhuanlan.zhihu.com/p/632128657

小tip💡前面的选择题做完之后页面会展示整个对话的文本,如果这时时间有空余可以先不点击下一步,把文本看完之后脑子里生成一个大概的总结内容

假设题目
You are a student in a Political Science class and you are in the process of writing a paper on environmental policy making. You have found some sources, but you need help finding additional sources to support your argument. You go to talk to your professor during their review session to get some help.

📌 模板

记忆技巧: 我的问题 → 他的身份 → 他的方案
(75秒内写完, 写不完的可以删减模板, 比如下方蓝色背景的部分)


I was a/an 「我的身份」, and I stumbled upon (/ˈstʌmbld əˌpʌn/ v. 偶然遇到,碰到) a difficulty with 「我需要咨询的问题或者想要寻求的帮助」. I solicited (/səˈlɪsɪt/ v. 请求,征求,索求) 「对方的身份」. Afterwards, he/she endowed (/ɪnˈdaʊd/ v. 赋予,给予) me with some recommendations (/ˌrekəmenˈdeɪʃnz/ n. 建议,推荐), which were that 「对方给我的建议或者解决方案」.


📌 举例

I was a student in a Political Science class, and I stumbled upon (/ˈstʌmbld əˌpʌn/ v. 偶然遇到,碰到) a difficulty with finding additional (/əˈdɪʃənl/ adj. 额外的,附加的) sources. I solicited (/səˈlɪsɪt/ v. 请求,征求,索求) my professor. Afterwards, she endowed (/ɪnˈdaʊd/ v. 赋予,给予) me with some recommendations (/ˌrekəmenˈdeɪʃnz/ n. 建议,推荐), which were that I should look at news articles and a book on the topic.


  • stumble upon /ˈstʌmbəl əˌpʌn/ v. 偶然遇到,碰到
  • solicit /səˈlɪsɪt/ v. 请求,征求,索求
  • endow /ɪnˈdaʊ/ v. 赋予,给予
  • recommendation /ˌrekəmenˈdeɪʃn/ n. 建议,推荐
  • additional /əˈdɪʃənl/ adj. 额外的,附加的

小作文 5min

我是废话王,基本上可以打到140字左右。没什么好说的,套模板就可以,背熟了之后会打得很快,这里每个句子都很长,大家练习的时候可以自行删减。
不需要斟酌观点;️想到什么就写什么,写作时间很短没有办法有太长的思考时间,千万千万不要纠结;️

📝 小作文模板(5min)
记忆技巧: 三段是总分总结构, 第一段是表明总体观点, 第二段是分批举例, 第三段是再总结观点
关键句子首字母为: AFFATT

假设题目
Some companies allow employees to choose their own working hours, while others stick to a traditional routine. What type of work schedule do you prefer, and why?

第一段模板

As for (就… 而言; 关于) this profound (/prəˈfaʊnd/ adj. (影响)深刻的,极大的;(感情)强烈的,深切的;(思想)深邃的,(见解)深刻的) and intriguing (/ɪnˈtruːɡɪŋ/ adj. 引人入胜的) / engrossing (/ɪnˈɡroʊsɪŋ/ adj. 引人入胜的) and intricate (/ˈɪntrɪkət/ adj. 错综复杂的;难理解的,难学会的) topic, there is an ongoing / contentious (/kənˈtenʃəs/ adj. 有争议的,引起争论的;爱争论的,好争吵的) dispute about / debate regarding (prep. 关于,至于) …
From my perspective, there are two momentous (/moʊˈmentəs/ adj. 重大的,重要的) factors that can explain why I do maintain the belief that …

  • 举例:
    As for this profound and intricate topic of work schedules, there is an ongoing debate regarding the merits (/ˈmerɪt/ n. 优点,长处;优秀品质,价值) of flexibility versus the structure of a traditional routine. From my perspective, there are two momentous factors that can explain why I do maintain the belief that the ability to choose one’s own working hours is a preferred choice.

第二段模板

First and foremost (adj. 最前的,最重要的), the manifest (/ˈmænɪfest/ adj. 明显的,显而易见的) sentiment (/ˈsentɪmənt/ n. 观点,看法,情绪;多愁善感,伤感情绪) that coincides with my standpoint (/ˈstændpɔɪnt/ n. 立场,观点,角度) is that …
Additionally, I utterly (/ˈʌtərli/ adv. 完全地,彻底地) concede that …

  • 举例:
    First and foremost, the manifest sentiment that coincides with my standpoint is the enhancement of work-life balance. With the flexibility to choose one’s own working hours, employees can better manage their personal responsibilities and commitments.
    Additionally, I utterly concede that the flexibility of working hours fosters (/ˈfɑːstər/ v. 促进,培养;领养,收养 adj. 代养的,寄养的) a sense of autonomy and responsibility among employees.

第三段模板

To encapsulate (/ɪnˈkæpsjuleɪt/ v. 简要描述,概括;封装) my argument, undoubtedly, we can arrive at the undeniable verdict (/ˈvɜːrdɪkt/ n. 裁定,判决;定论;决定) that it is indisputable (/ˌɪndɪˈspjuːtəbəl/ adj. 不容置疑的,无可争辩的) for us to …
This is emblematic (/ˌembləˈmætɪk/ adj. 象征的;可当标志的), integral (/ˈɪntɪɡrəl/ adj. 必需的,必要的;作为组成部分的;完整的), momentous, and imperative (/ɪmˈperətɪv/ adj. 极重要的,必要的;命令的;祈使的).

  • 举例:
    To encapsulate my argument, undoubtedly, we can arrive at the undeniable verdict that the ability to choose one’s own working hours is not just a perk (/pɜːrk/ n. 津贴,额外收入) but a necessity in today’s fast-paced world. This flexibility is emblematic, integral, momentous, and imperative for both employers and employees.

📘 单词表清单(按词性分类)


🔹 Adjectives (形容词)

  • profound /prəˈfaʊnd/ adj. (影响)深刻的,极大的;(感情)强烈的,深切的;(思想)深邃的,(见解)深刻的
  • intriguing /ɪnˈtriːɡɪŋ/ adj. 引人入胜的
  • intricate /ˈɪntrɪkət/ adj. 错综复杂的;难理解的,难学会的
  • contentious /kənˈtenʃəs/ adj. 有争议的,引起争论的;爱争论的,好争吵的
  • momentous /moʊˈmentəs/ adj. 重大的,重要的
  • manifest /ˈmænɪfest/ adj. 明显的,显而易见的
  • indisputable /ˌɪndɪˈspjuːtəb(ə)l/ adj. 不容置疑的,无可争辩的
  • emblematic /ˌembləˈmætɪk/ adj. 象征的;可当标志的
  • integral /ˈɪntɪɡrəl/ adj. 必需的,必要的;作为组成部分的;完整的;整的,积分的
  • imperative /ɪmˈperətɪv/ adj. 极重要的,必要的;命令的,强制的;祈使的

🔹 Nouns (名词)

  • merit /ˈmerɪt/ n. 优秀品质,价值;优点,长处
  • sentiment /ˈsentɪmənt/ n. 观点,看法,情绪;多愁善感,伤感情绪
  • standpoint /ˈstændpɔɪnt/ n. 立场,观点,角度;观景点,观看位
  • verdict /ˈvɜːrdɪkt/ n. (法庭的)裁定,判决;(权威的)评判,定论;意见,决定
  • perk /pɜːrk/ n. (工资以外的)额外收入,津贴

🔹 Verbs (动词)

  • coincide /ˌkoʊɪnˈsaɪd/ v. (观点或看法)一致,相符;同时发生
  • concede /kənˈsiːd/ v. 承认(某事属实);允许,让与;认输
  • foster /ˈfɑːstər/ v. 促进,培养;领养,收养

🔹 Adverbs (副词)

  • utterly /ˈʌtərli/ adv. 完全地,彻底地

🔹 Verb Phrase (动词短语)

  • encapsulate /ɪnˈkæpsjuleɪt/ vt. 简要描述,概括;用胶囊(或囊状物)装,封装

互动写作-模板1 (5min, 适用于互动写作的第1个题)

首字母: OCBAFFT(蓝色背景的可酌情删减)

On the subject of [topic], diverse (/daɪˈvɜːrs/ adj. 多样的,不同的) individuals harbor (/ˈhɑːrbər/ v. 怀有(想法、感情)) diametrically (/ˌdaɪəˈmetrɪkli/ adv. 完全地;截然相反地) opposing viewpoints. Consequently, a heated discussion persists (/pərˈsɪst/ v. 持续存在,坚持) surrounding…

Before this topic, I used to think that (题目) is of little significance (/sɪɡˈnɪfɪkəns/ n. 重要性,意义). Therefore, I haven’t thought about it seriously until I realized that it exerts (/ɪɡˈzɜːrt/ v. 施加(影响、力量)) a profound (/prəˈfaʊnd/ adj. (影响)深刻的;(思想)深邃的) impact on people’s lives.

Apparently, this controversy (/ˈkɑːntrəvɜːrsi/ n. 争议,争论) about … could be vitally (/ˈvaɪtəli/ adv. 极其,紧要地) problematic.

From my vantage point (/ˈvæntɪdʒ pɔɪnt/ n. 视角,有利位置), I believe that [opinion], which is supported by two pivotal (/ˈpɪvət(ə)l/ adj. 中枢的,关键的) factors. There are several quintessential (/ˌkwɪntɪˈsenʃl/ adj. 典型的,完美的;本质的) instances and reasons involved thereof (/ˌðerˈʌv/ adv. 在其中,由此).

Furthermore, proponents (/prəˈpoʊnənts/ n. 支持者,建议者) fervently (/ˈfɜːrvəntli/ adv. 热心地,热诚地) assert that […]

The unequivocal (/ˌʌnɪˈkwɪvək(ə)l/ adj. 明确的,不含糊的) sentiment that aligns with my position is […]


互动写作模板1词汇表

  • diverse /daɪˈvɜːrs/ adj. 多样的,不同的
  • harbor /ˈhɑːrbər/ v. 怀有(想法、感情)
  • diametrically /ˌdaɪəˈmetrɪkli/ adv. 完全地,截然相反地
  • persist /pərˈsɪst/ v. 持续存在,坚持
  • significance /sɪɡˈnɪfɪkəns/ n. 重要性,意义
  • exert /ɪɡˈzɜːrt/ v. 施加(影响、力量)
  • profound /prəˈfaʊnd/ adj. (影响)深刻的;(思想)深邃的
  • controversy /ˈkɑːntrəvɜːrsi/ n. 争议,争论
  • vitally /ˈvaɪtəli/ adv. 极其,紧要地
  • vantage point /ˈvæntɪdʒ pɔɪnt/ n. 视角,有利位置
  • pivotal /ˈpɪvət(ə)l/ adj. 中枢的,关键的
  • quintessential /ˌkwɪntɪˈsenʃl/ adj. 典型的,完美的;本质的
  • thereof /ˌðerˈʌv/ adv. 在其中,由此
  • proponents /prəˈpoʊnənts/ n. 支持者,建议者
  • fervently /ˈfɜːrvəntli/ adv. 热心地,热诚地
  • assert /əˈsɜːrt/ v. 断言,声称
  • unequivocal /ˌʌnɪˈkwɪvək(ə)l/ adj. 明确的,不含糊的
  • equivocal /ɪˈkwɪvək(ə)l/ adj. 模棱两可的

互动写作模板2 (3min, 适用于互动写作的第2个题)

首字母: AFAT

About [题目], different (/ˈdɪfərənt/ adj. 不同的,有差异的) people have entirely dissimilar (/dɪˈsɪmələr/ adj. 不同的,迥异的) opinions. Hence, there is an ongoing debate (/dɪˈbeɪt/ n. 辩论,争论) about…

From my perspective, elaborated (/ɪˈlæbərət/ adj. 复杂的,详尽的;v. 详细说明,详尽阐述;精心制作) by two momentous (/moʊˈmentəs/ adj. 重大的,重要的) factors, I deem (/diːm/ v. 认为,相信) that [观点].

Additionally, advocates (/ˈædvəkeɪt/ v. 拥护,提倡;n. 拥护者,提倡者;辩护律师) utterly (/ˈʌtərli/ adv. 完全地,彻底地) concede (/kənˈsiːd/ v. 承认,让步) that […]

The explicit (/ɪkˈsplɪsɪt/ adj. 明确的,清楚的) sentiment that coincides (/koʊˈɪnsaɪdz/ v. 与…一致;同时发生) with my standpoint (/ˈstændpɔɪnt/ n. 立场,观点) is that […]


互动写作模板2词汇表(加粗版)

  • entirely /ɪnˈtaɪərli/ adv. 完全地,彻底地
  • dissimilar /dɪˈsɪmələr/ adj. 不同的,不相似的
  • ongoing /ˈɑːnˌɡoʊɪŋ/ adj. 持续的,进行中的
  • debate /dɪˈbeɪt/ n. 辩论,讨论 v. 辩论,讨论
  • elaborate /ɪˈlæbərət/ adj. 复杂的,详尽的;v. 详细说明,详尽阐述;精心制作
  • momentous /moʊˈmentəs/ adj. 重大的,重要的
  • deem /diːm/ v. 认为,相信
  • advocate /ˈædvəkeɪt/ n. 拥护者,提倡者;v. 拥护,提倡
  • utterly /ˈʌtərli/ adv. 完全地,彻底地
  • concede /kənˈsiːd/ v. 承认,让步
  • explicit /ɪkˈsplɪsɪt/ adj. 明确的,直言的
  • coincide /ˌkoʊɪnˈsaɪd/ v. 与…一致,同时发生
  • standpoint /ˈstændpɔɪnt/ n. 立场,观点,角度

📖 看图演讲 (90s) 模板

💡 记忆方法: 总人物背总 → TWAGI

  • Total(总体)
  • Who(人物)
  • Articles(物体)
  • Ground(背景)
  • In a nutshell(总结)

●总体 (Total)

This picture depicts [dɪˈpɪkts v. 描绘,描述] a fabulous [ˈfæbjələs adj. 极好的,惊人的] situation outdoors/indoors, in a (street / playground / the top of the mountain / studio).
And the weather outside is sunny/cloudy/rainy.

举例:
This picture depicts a serene [/səˈriːn/ adj. 平静的,安详的] scene[/siːn/ n. 景色,景象] indoors, in a cozy [ˈkoʊzi adj. 舒适的,惬意的] room with a large window.


●人物 (Who)

What can be seen from the photograph is that a/an + 人物 + who is doing sth.
He/She/They has/have [short/long/curly + black/white/red/blonde] hair.
Plus, … is/are wearing a [black/white/red/… + shirt/dress/skirt/hoody (/ˈhʊdi/ n. 套头衫;年轻小混混)] with [a hat/scarf].

According to their facial expression/behaviors, I speculate [ˈspekjuleɪt v. 猜测] / reckon [ˈrekən v. 认为,猜想] that they are pretty blissful [ˈblɪsf(ə)l adj. 极乐的;幸福的], indigent [ˈɪndɪdʒənt adj. 贫困的], or exhausted [ɪɡˈzɔːstɪd adj. 精疲力竭的].

举例:
What can be seen from the photograph is that a woman is sitting next to the window.
She has long, curly black hair that falls gracefully [ˈɡreɪsfəli adv. 优雅地,得体地] over her shoulders.
She is wearing a white dress with a delicate [ˈdelɪkət adj. 柔和的,精致的] scarf wrapped [/ræpt/ v. 包,裹] gracefully around her neck.

According to her facial [ˈfeɪʃ(ə)l adj. 面部的] expression [ɪkˈspreʃ(ə)n n. 神情], I speculate[/ˈspekjuleɪt/ v. 猜测]. that she is feeling peaceful/blissful and contented [kənˈtentɪd adj. 满足的].


●物体 (Articles)

Additionally[əˈdɪʃənəli/另外], there are several delicate [ˈdelɪkət adj. 精美的;纤细的] and gorgeous [ˈɡɔːrdʒəs adj. 华丽的;美丽动人的] items involved [ɪnˈvɑːlvd v. 包含] there.
For example, we can see there is a breathtaking [ˈbreθˌteɪkɪŋ adj. 令人屏息的,惊人的] … which is probably made in the 19th century.

举例:
Additionally, there are several delicate and gorgeous items involved there.
For instance, we can see a breathtaking window which is probably made in the 19th century.


●背景 (Ground)

Given the background that there is/isn’t sunlight, this photograph is probably taken in the daytime/noon/night.
In the background/foreground, we can see that the scenery is extremely fascinating [ˈfæsɪneɪtɪŋ adj. 极有吸引力的,迷人的].

举例:
Given the background that there is plenty of sunlight streaming through the window, this photograph is probably taken in the daytime.
In the background/foreground, we can see that the scenery[/ˈsiːnəri/ n. 风景,景色]. is extremely[/ɪkˈstriːmli/adv. 极度,非常] fascinating[/ˈfæsɪneɪtɪŋ/adj. 极有吸引力的,迷人的].


●总结 (In a nutshell)

In a nutshell [ˈnʌtʃel idiom 总而言之,简而言之], this picture is not only very informative [ɪnˈfɔːrmətɪv adj. 提供有用信息的;教育性的] but also provides me with lots of profound [prəˈfaʊnd adj. 深刻的;意义深远的] information and helps me get a better understanding of this thing.


📌 看题演讲 (90s)

记忆方法: 念题目 → 以前的想法别人做什么 → 现在的我开始做什么
首字母: STUB

模板:
Speaking of (题目的中心), I feel like I have to talk about (题目), which is popular on the internet.

To be honest, before this topic, I used to think that (否定题目现象). Therefore, I didn’t know why people were keen to do that, which was the reason why I had never paid attention to this topic before.

Unexpectedly, by chance, I seriously thought about it again some days ago. From that day on, I have been (doing xxx) every day. Admittedly, it sounds a little bit exaggerated (/ɪɡˈzædʒəreɪtɪd/ adj. 夸张的,夸大的) or even eccentric (/ɪkˈsentrɪk/ adj. 古怪的,异乎寻常的;不同圆心的,不正圆的), but it is the truth.

Because there was one day that I didn’t do that before sleep which made me feel wretched (/ˈretʃɪd/ adj. 可怜的,悲惨的), frustrated (/ˈfrʌstreɪtɪd/ adj. 懊恼的,沮丧的), and disappointed. So it becomes necessary for me to (do xxx) regularly.

带注释模板

Speaking of (题目的中心), I feel like I have to talk about (题目), which is popular (/ˈpɒpjələr/ adj. 受欢迎的) on the internet.

To be honest, before this topic, I used to think that (否定题目现象). Therefore, I didn’t know why people were keen (/kiːn/ adj. 热衷的,渴望的) to do that, which was the reason why I had never paid attention to this topic before.

Unexpectedly, by chance, I seriously thought about it again some days ago. From that day on, I have been (doing xxx) every day. Admittedly (/ədˈmɪtɪdli/ adv. 公认地,诚然), it sounds a little bit exaggerated (/ɪɡˈzædʒəreɪtɪd/ adj. 夸张的,夸大的) or even eccentric (/ɪkˈsentrɪk/ adj. 古怪的,异乎寻常的;不同圆心的,不正圆的), but it is the truth.

Because there was one day that I didn’t do that before sleep which made me feel wretched (/ˈretʃɪd/ adj. 可怜的,悲惨的), frustrated (/ˈfrʌstreɪtɪd/ adj. 懊恼的,沮丧的), and disappointed (/ˌdɪsəˈpɔɪntɪd/ adj. 失望的). So it becomes necessary (/ˈnesəsəri/ adj. 必要的) for me to (do xxx) regularly (/ˈreɡjələli/ adv. 定期地,经常地).

📖 生词表(B1及以上)

形容词 (Adjectives)

  • popular /ˈpɒpjələr/ adj. 受欢迎的
  • keen /kiːn/ adj. 热衷的,渴望的
  • exaggerated /ɪɡˈzædʒəreɪtɪd/ adj. 夸张的,夸大的
  • eccentric /ɪkˈsentrɪk/ adj. 古怪的,异乎寻常的;不同圆心的
  • wretched /ˈretʃɪd/ adj. 可怜的,悲惨的
  • frustrated /ˈfrʌstreɪtɪd/ adj. 懊恼的,沮丧的
  • disappointed /ˌdɪsəˈpɔɪntɪd/ adj. 失望的
  • necessary /ˈnesəseri/ adj. 必要的

副词 (Adverbs)

  • admittedly /ədˈmɪtɪdli/ adv. 公认地,诚然
  • regularly /ˈreɡjələli/ adv. 定期地,经常地

📌 听题演讲 (90s)

记忆方法: 念题目 → 以前的想法 → 现在的想法 → 转变让我明白了什么的意义 → 介绍事件(念题目)
首字母: AHTTIT

模板:

About (题目), different people have entirely dissimilar opinions. Hence, there is an ongoing dispute about (题目) on the internet, which could be vitally problematic. However, I deem that …. for the following reasons and instances.

To be honest, before this topic, I used to think that (题目) is of little significance. Therefore, I haven’t thought about it seriously until I realized that it exerts a profound impact on people’s lives.

It provided me an opportunity to expose myself to understand the meaning of [题目], which helps me broaden my horizon. This enables me to change my point of view on certain things, thereby exerting a profound impact on cultivating my xx skills. Had it not been for this experience, I wouldn't have achieved the success I enjoy today.

📖 英中逐句对照

  1. About (题目), different people have entirely dissimilar opinions.
    关于(题目),不同的人有完全不同的观点。

  2. Hence, there is an ongoing dispute about (题目) on the internet, which could be vitally problematic.
    因此,在互联网上关于(题目)一直存在争议,这可能会带来很大的问题。

  3. However, I deem that …. for the following reasons and instances.
    不过,我认为……,原因和例子如下。

  4. To be honest, before this topic, I used to think that (题目) is of little significance.
    老实说,在接触这个话题之前,我曾认为(题目)并不重要。

  5. Therefore, I haven’t thought about it seriously until I realized that it exerts a profound impact on people’s lives.
    因此,我从未认真考虑过这个问题,直到我意识到它对人们的生活有深远的影响。

  6. It provided me an opportunity to expose myself to understand the meaning of [题目], which helps me broaden my horizon.
    它给了我一个机会去接触并理解(题目)的意义,这帮助我拓宽了视野。

  7. This enables me to change my point of view on certain things, thereby exerting a profound impact on cultivating my xx skills.
    这使我能够改变自己对某些事情的看法,从而对培养我的 xx 技能产生深远的影响。

  8. Had it not been for this experience, I wouldn’t have achieved the success I enjoy today.
    如果没有这段经历,我也不可能取得今天享有的成功。


带注释模板

About (题目), different people have entirely (/ɪnˈtaɪərli/adv. 完全地,完整地;仅仅,只)dissimilar (/dɪˈsɪmələr/ adj. 不同的,不相似的) opinions. Hence, there is an ongoing dispute (/dɪˈspjuːt/ n. 争论,纠纷) about (题目) on the internet, which could be vitally (/ˈvaɪtəli/ adv. 极其,非常) problematic (/ˌprɒbləˈmætɪk/ adj. 成问题的,棘手的). However, I deem (/diːm/ v. 认为,视作) that …. for the following reasons and instances.

To be honest, before this topic, I used to think that (题目) is of little significance (/sɪɡˈnɪfɪkəns/ n. 重要性,意义). Therefore, I haven’t thought about it seriously until I realized that it exerts (/ɪɡˈzɜːts/ v. 施加,产生) a profound (/prəˈfaʊnd/ adj. 深刻的,意义深远的) impact (/ˈɪmpækt/ n. 影响,冲击) on people’s lives.

It provided me an opportunity to expose (/ɪkˈspəʊz/ v. 接触,暴露) myself to understand the meaning of [题目], which helps me broaden (/ˈbrɔːdn/ v. 扩大,拓宽) my horizon (/həˈraɪzn/ n. 视野,眼界). This enables(/ɪˈneɪb(ə)l/v. 使能够,使可能;激活,启动;准许,授权) me to change my point of view on certain things, thereby exerting a profound impact on cultivating (/ˈkʌltɪveɪtɪŋ/ v. 培养,培育) my xx skills. Had it not been for this experience, I wouldn’t have achieved the success I enjoy (/ɪnˈdʒɔɪ/ v. 享有) today.

📖 生词表(B1及以上)

形容词 (Adjectives)

  • dissimilar /dɪˈsɪmələr/ adj. 不同的,不相似的
  • problematic /ˌprɒbləˈmætɪk/ adj. 成问题的,棘手的
  • significant /sɪɡˈnɪfɪkənt/ adj. 重要的,有意义的
  • profound /prəˈfaʊnd/ adj. 深刻的,意义深远的

名词 (Nouns)

  • dispute /dɪˈspjuːt/ n. 争论,纠纷
  • significance /sɪɡˈnɪfɪkəns/ n. 重要性,意义
  • impact /ˈɪmpækt/ n. 影响,冲击
  • horizon /həˈraɪzn/ n. 视野,眼界

动词 (Verbs)

  • deem /diːm/ v. 认为,视作
  • exert /ɪɡˈzɜːt/ v. 施加,运用
  • expose /ɪkˈspəʊz/ v. 接触,使暴露
  • broaden /ˈbrɔːdn/ v. 扩大,拓宽
  • cultivate /ˈkʌltɪveɪt/ v. 培养,培育
  • enjoy /ɪnˈdʒɔɪ/ v. 享有,拥有

副词 (Adverbs)

  • vitally /ˈvaɪtəli/ adv. 极其,非常

📌 口语样本 (3min)

记忆方法: 念题目 → 以前的想法 → 现在的想法 → 转变让我明白了什么的意义 → 介绍事件(念题目)
首字母: IBUIT

假设题目: Discuss a recent upsetting experience.

模板:

I’d like to talk about [题目], which is such a fantastic/terrible topic.  
- 举例: I'd like to talk about a recent upsetting experience, which is such a terrible topic.

Before this topic, I used to think that … That was why I had never paid my attention to this topic before.
- 举例: Before this incident, I used to think that our friendship was invincible, unbreakable. That was why I had never paid my attention to this topic before.

Unexpectedly, I seriously thought about it again several months ago, which changed my mind. Nowadays, a considerable amount of people are beginning to believe that … is significant. It provided me an opportunity to expose myself to understand the meaning of … and changed my point of view on certain things that help me expand my horizon.
- 举例: Unexpectedly, I received the news of my friend's passing a few months ago, which changed my mind. Nowadays, a considerable amount of people are beginning to believe that a valuable life is significant. It provided me an opportunity to expose myself to understand the meaning of life and changed my point of view on certain things that help me expand my horizon.

Therefore, that’s why I’m very glad to introduce it to you. If you have some free time, you should think about it twice. I guess you will get something unexpected.
- 举例: Therefore, that's why I'm sharing this upsetting experience with you. If you have some free time, you can try it. I guess you will get something unexpected.

带注释模板

I’d like to talk about [题目], which is such a fantastic (/fænˈtæstɪk/ adj. 极好的,奇妙的)/terrible (/ˈterəbl/ adj. 糟糕的,极差的) topic.

  • 举例: I’d like to talk about a recent upsetting experience, which is such a terrible topic.

Before this topic, I used to think that … That was why I had never paid my attention to this topic before.

  • 举例: Before this incident (/ˈɪnsɪdənt/ n. 事件), I used to think that our friendship was invincible (/ɪnˈvɪnsəbl/ adj. 不可战胜的) and unbreakable (/ʌnˈbreɪkəbl/ adj. 不可破坏的). That was why I had never paid my attention to this topic before.

Unexpectedly (/ʌnɪkˈspektɪdli/ adv. 出乎意料地), I seriously thought about it again several months ago, which changed my mind. Nowadays, a considerable (/kənˈsɪdərəbl/ adj. 相当多的) amount of people are beginning to believe that … is significant (/sɪɡˈnɪfɪkənt/ adj. 重要的,有意义的). It provided me an opportunity to expose (/ɪkˈspəʊz/ v. 接触,使暴露) myself to understand the meaning of … and changed my point of view on certain things that help me expand (/ɪkˈspænd/ v. 扩展,扩大) my horizon (/həˈraɪzn/ n. 视野,眼界).

  • 举例: Unexpectedly, I received the news of my friend’s passing a few months ago, which changed my mind. Nowadays, a considerable(/kənˈsɪdərəb(ə)l/adj. 相当大的,相当重要的;) amount of people are beginning to believe that a valuable life is significant(/sɪɡˈnɪfɪkənt/adj. 显著的,相当数量的;重要的,意义重大的; 别有含义的,意味深长的). It provided me an opportunity to expose myself to understand the meaning of life and changed my point of view on certain things that help me expand my horizon.

Therefore, that’s why I’m very glad to introduce it to you. If you have some free time, you should think about it twice. I guess you will get something unexpected.

  • 举例: Therefore, that’s why I’m sharing this upsetting experience with you. If you have some free time, you can try it. I guess you will get something unexpected.

📖 生词表(B1及以上)

形容词 (Adjectives)

  • fantastic /fænˈtæstɪk/ adj. 极好的,奇妙的
  • terrible /ˈterəbl/ adj. 糟糕的,极差的
  • invincible /ɪnˈvɪnsəbl/ adj. 不可战胜的
  • unbreakable /ʌnˈbreɪkəbl/ adj. 不可破坏的
  • considerable /kənˈsɪdərəbl/ adj. 相当多的,可观的
  • significant /sɡˈnɪfɪkənt/ adj. 重要的,有意义的

名词 (Nouns)

  • incident /ˈɪnsɪdənt/ n. 事件
  • horizon /həˈraɪzn/ n. 视野,眼界

动词 (Verbs)

  • expose /ɪkˈspəʊz/ v. 接触,使暴露
  • expand /ɪkˈspænd/ v. 扩展,扩大

副词 (Adverbs)

  • unexpectedly /ʌnɪkˈspektɪdli/ adv. 出乎意料地

重点词汇总表(精华背诵版)

描述图片开头(WIT 模版)

  • chromatic [krəˈmætɪk] adj. 彩色的;易染色的
  • vivacious [vɪˈveɪʃəs] adj. 活泼的;快活的
  • intriguing [ɪnˈtriːɡɪŋ] adj. 引人入胜的
  • quaint [kweɪnt] adj. 古色古香的;奇特有趣的
  • manifestly [ˈmænɪfestli] adv. 显然地;明白地
  • reckon [ˈrekən] v. 认为;猜测
  • blissful [ˈblɪsf(ə)l] adj. 极乐的;幸福的
  • discontented [ˌdɪskənˈtentɪd] adj. 不满的;不快的
  • contented [kənˈtentɪd] adj. 满足的;心满意足的
  • hysterical [hɪˈsterɪkl] adj. 歇斯底里的

人物描写

  • personable [ˈpɜːrsənəbl] adj. 英俊潇洒的;和蔼可亲的
  • muscular [ˈmʌskjələr] adj. 肌肉发达的;强壮的
  • winsome [ˈwɪnsəm] adj. 迷人的;可爱的
  • affectionate [əˈfekʃənət] adj. 充满深情的;有爱的
  • cunning [ˈkʌnɪŋ] adj. 狡猾的;灵巧的;可爱的
  • lovable [ˈlʌvəbl] adj. 可爱的;讨人喜欢的
  • dark-skinned [ˌdɑːrk ˈskɪnd] adj. 深肤色的
  • wholehearted [ˌhoʊlˈhɑːrtɪd] adj. 全心全意的
  • grizzled [ˈɡrɪzld] adj. 头发斑白的
  • elderly [ˈeldərli] adj. 上了年纪的

景色描写

  • tranquil [ˈtræŋkwɪl] adj. 宁静的;安静的
  • serenity [səˈrenəti] n. 宁静;安详
  • fascinating [ˈfæsɪneɪtɪŋ] adj. 极有吸引力的
  • enchanting [ɪnˈtʃæntɪŋ] adj. 迷人的;妩媚的

听力总结模版

  • solicit [səˈlɪsɪt] v. 请求;征求
  • endow sb. with [ɪnˈdaʊ] v. 赋予某人(能力/品质)

写作模版

  • profound [prəˈfaʊnd] adj. 深刻的;极大的
  • intriguing [ɪnˈtriːɡɪŋ] adj. 引人入胜的
  • engrossing [ɪnˈɡroʊsɪŋ] adj. 使人专注的
  • intricate [ˈɪntrɪkət] adj. 错综复杂的
  • contentious [kənˈtenʃəs] adj. 有争议的;好争论的
  • momentous [moʊˈmentəs] adj. 重大的;重要的
  • merit [ˈmerɪt] n. 优点;价值
  • foremost [ˈfɔːrmoʊst] adj. 首要的
  • manifest [ˈmænɪfest] adj. 明显的
  • sentiment [ˈsentɪmənt] n. 观点;情绪
  • standpoint [ˈstændpɔɪnt] n. 立场;观点
  • utterly [ˈʌtərli] adv. 完全地
  • concede [kənˈsiːd] v. 承认;让步
  • foster [ˈfɑːstər] v. 培养;促进
  • encapsulate [ɪnˈkæpsjuleɪt] v. 概括;简要描述
  • undoubtedly [ʌnˈdaʊtɪdli] adv. 毫无疑问地
  • verdict [ˈvɜːrdɪkt] n. 裁定;判决;意见
  • indisputable [ˌɪndɪˈspjuːtəbl] adj. 不容置疑的
  • emblematic [ˌembləˈmætɪk] adj. 象征性的
  • integral [ˈɪntɪɡrəl] adj. 必需的;整体的
  • imperative [ɪmˈperətɪv] adj. 极重要的;必要的
  • perk [pɜːrk] n. 额外福利

📌 看图写句 (WIT)

●第一句
○This chromatic [krəˈmætɪk] adj. 彩色的;易染色的 and vivacious [vɪˈveɪʃəs] adj. 活泼的;快活的;有生气的 image depicts that … . The weather outside is sunny/cloudy/rainy.
■举例: This chromatic and vivacious photograph depicts that a muscular [ˈmʌskjələr] adj. 肌肉发达的;强壮的 man is pushing two grizzled [ˈɡrɪzld] adj. 头发斑白的 elderly people angrily. The weather outside is sunny.

○We can see in this intriguing [ɪnˈtriːɡɪŋ] adj. 引人入胜的;非常有趣的 and quaint [kweɪnt] adj. 奇特有趣的;古色古香的 picture that … .This vivacious image portrays a fabulous [ˈfæbjələs] adj. 极好的;惊人的 situation.
■举例: We can see in this intriguing and quaint picture that two cunning [ˈkʌnɪŋ] adj. 狡猾的;灵巧的;可爱的 boys are playing basketball with each other. This vivacious image portrays a fabulous situation.

○It can be manifestly [ˈmænɪfestli] adv. 显然地;明白地 seen in this fabulous snapshot that … . I reckon [ˈrekən] v. 认为;猜想 that he/she is feeling blissful [ˈblɪsf(ə)l] adj. 极乐的;幸福的;无忧无虑的 / discontented [ˌdɪskənˈtentɪd] adj. 不满的;/ contented [kənˈtentɪd] adj. 满足的.
■举例: It can be manifestly seen in this fabulous snapshot that a hysterical [hɪˈsterɪkl] adj. 歇斯底里的 boy is crying in front of a wall. I reckon he is being kidnapped, because this chromatic image portrays a dangerous scene.


●第二句强调人物状态
○男人: personable [ˈpɜːrsənəbl] adj. 英俊潇洒的;品貌兼优的;和蔼可亲的, muscular [ˈmʌskjələr] adj. 肌肉的;强壮的
○女人: winsome [ˈwɪnsəm] adj. 迷人的;可爱的;引人注目的
○妈妈: affectionate [əˈfekʃənət] adj. 表达爱意的;深情的
○小孩: cunning [ˈkʌnɪŋ] adj. 狡猾的;灵巧的;<美>可爱的, lovable [ˈlʌvəbl] adj. 可爱的;讨人喜欢的, hysterical [hɪˈsterɪkl] adj. 歇斯底里的
○黑人: dark-skinned [ˌdɑːrk ˈskɪnd] adj. 深肤色的
○工人: wholehearted [ˌhoʊlˈhɑːrtɪd] adj. 全心全意的
○老人: grizzled [ˈɡrɪzld] adj. 头发斑白的, elderly [ˈeldərli] adj. 上了年纪的


●强调景色
○The weather outside is …
○It is tranquil [ˈtræŋkwɪl] adj. 宁静的;安详的 with serenity [səˈrenəti] n. 平静;宁静
○The scenery is extremely fascinating [ˈfæsɪneɪtɪŋ] adj. 极有吸引力的, presenting a piece of enchanting [ɪnˈtʃæntɪŋ] adj. 迷人的;妩媚的 beauty.
○This vivacious/black/white picture portrays [pɔːrˈtreɪz] v. 描绘;描写 a fabulous situation


🎧 互动听力总结题 (ILT)

●开头 (Introduction)

  • From the dialogue between A and B, I learned that …
  • There are several important points to discuss.
  • The conversation mainly revolves around the topic of …

●主体 (Main Content)

  1. Firstly, A mentioned that …

    • He/She highlighted [ˈhaɪlaɪtɪd] v. 强调;突出 the importance of …
    • He/She also expressed [ɪkˈsprest] v. 表达;陈述 his/her concern [kənˈsɜːrn] n. 担忧;关切 about …
  2. Secondly, B responded by saying that …

    • B emphasized [ˈemfəsaɪzd] v. 强调 that …
    • He/She also proposed [prəˈpoʊzd] v. 建议;提出 a solution to deal with the issue.
  3. Thirdly, they both agreed/disagreed on …

    • They had a consensus [kənˈsensəs] n. 共识 about …
    • Or they had a conflict [ˈkɑːnflɪkt] n. 冲突;分歧 regarding …

●背景/语气 (Tone & Attitude)

  • Judging from their tone of voice,
    I speculate [ˈspekjəˌleɪt] v. 推测;猜想 that they were quite enthusiastic [ɪnˌθuːziˈæstɪk] adj. 热情的;积极的 /
    indifferent [ɪnˈdɪfərənt] adj. 漠不关心的 /
    frustrated [ˈfrʌstreɪtɪd] adj. 沮丧的;受挫的.

●总结 (Conclusion)

  • In a nutshell [ɪn ə ˈnʌtʃel] idiom 总而言之,
  • This conversation is quite informative [ɪnˈfɔːrmətɪv] adj. 信息量大的;有教育意义的,
  • and it gives me a profound [prəˈfaʊnd] adj. 深刻的;意义深远的 understanding of this issue.

📖 阅读总结模版 (RT)

●开头 (Introduction)

  • From the passage/article, I learned that …
  • The author mainly illustrates [ˈɪləˌstreɪts] v. 阐明;说明 the importance of …
  • The text mainly focuses on [ˈfoʊkəs ɑːn] 短语:集中于;关注 …

●主体 (Main Content)

  1. Firstly, the author points out that …

    • He/She emphasizes [ˈemfəsaɪzɪz] v. 强调 the significance of …
    • He/She also elaborates [ɪˈlæbəˌreɪts] v. 详细说明 on the reasons why …
  2. Secondly, the passage provides several examples [ɪɡˈzæmpəlz] n. 例子 to support [səˈpɔːrt] v. 支持 this idea.

    • For instance, …
    • These examples demonstrate [ˈdemənˌstreɪt] v. 证明;说明 that …
  3. Thirdly, the author contrasts [ˈkɑːnˌtræsts] v. 对比;对照 different viewpoints.

    • He/She acknowledges [əkˈnɑːlɪdʒɪz] v. 承认;认可 the challenges/drawbacks.
    • But still argues [ˈɑːrɡjuːz] v. 争论;主张 that …

●态度/语气 (Tone & Attitude)

  • Judging from the writing style, I speculate that the author’s attitude is
    optimistic [ˌɑːptɪˈmɪstɪk] adj. 乐观的 /
    pessimistic [ˌpesəˈmɪstɪk] adj. 悲观的 /
    objective [əbˈdʒektɪv] adj. 客观的 /
    critical [ˈkrɪtɪkl] adj. 批判的.

●总结 (Conclusion)

  • In a nutshell [ɪn ə ˈnʌtʃel] idiom 总而言之;简而言之,
  • This passage is quite enlightening [ɪnˈlaɪtnɪŋ] adj. 启发性的;有教育意义的,
  • and it gives me a more comprehensive [ˌkɑːmprɪˈhensɪv] adj. 全面的;综合的 understanding of this topic.

Actor模型 vs. CSP模型

Posted on 07-21-2021 | In Misc

Akka/Erlang 的 actor 模型与 Go 语言的协程 Goroutine 与通道 Channel 代表的 CSP(Communicating Sequential Processes) 模型有什么区别呢?

首先这两者都是并发模型的解决方案,我们看看 Actor 和 Channel 这两个方案的不同:

Actor 模型

在 Actor 模型中,主角是 Actor,类似一种 worker,Actor 彼此之间直接发送消息,不需要经过什么中介,消息是异步发送和处理的:

Actor 模型描述了一组为了避免并发编程的常见问题的公理:

  1. 所有 Actor 状态是 Actor 本地的,外部无法访问。
  2. Actor 必须只有通过消息传递进行通信。  
  3. 一个 Actor 可以响应消息: 推出新 Actor, 改变其内部状态, 或将消息发送到一个或多个其他参与者。
  4. Actor 可能会堵塞自己, 但 Actor 不应该堵塞它运行的线程。

更多可见 Actor 模型专题

. . .

服务器开发自我修养专栏-etcd与raft要点

Posted on 05-17-2021 | In Self-cultivation

etcd

  • etcd 参考 https://wingsxdu.com/post/database/etcd/#gsc.tab=0
  • raft 参考 https://www.jianshu.com/p/5aed73b288f7
  • 重点参考 https://segmentfault.com/a/1190000022248118

  • etcd 是一个 Go 语言编写的分布式、高可用的强一致性键值存储系统,用于提供可靠的分布式键值(key-value)存储、配置共享和服务发现等功能。 etcd可以用于存储关键数据和实现分布式调度,它在现代化的集群运行中能够起到关键性的作用。

  • Raft用于保证分布式数据的一致性。动画演示Raft

Raft选主过程



动画演示Raft选主
前提知识:

  • Election timeout选举周期: The election timeout is the amount of time a follower waits until becoming a candidate.
  • heartbeat timeout心跳时间间隔

. . .

python的reload对于func_closure的处理踩坑

Posted on 05-08-2021 | In Misc

带着问题学习动力是较强的, 直接上例子

. . .

服务器开发自我修养专栏-Redis实现原理

Posted on 04-20-2021 | In Self-cultivation

Redis

redis 数据结构有哪些?分别怎么实现的?

  • String:
    • 全是整数的时候用整数编码int
    • 当有字符串的时候用简单动态字符串sds编码
  • HashTable:
    • 元素比较少或者元素比较短的时候用压缩表ziplist(key1|val1|key2|val2|…这样存储),
    • 其他时候就用字典ht
  • Set:
    • 元素全是整数的时候用整数集合编码(一种特殊的编码, 会使用各种规则来利用位空间, 来节省内存),
    • 其他时候用字典ht编码(键为Set的元素, 值都为Null)
  • List:
    • 元素比较少或者元素比较短的时候用压缩表ziplist,
    • 其他时候就用双端列表LinkedList编码
  • ZSet:
    • 参考 http://redisbook.com/preview/object/sorted_set.html
    • 参考 https://redisbook.readthedocs.io/en/latest/datatype/sorted_set.html
    • 元素比较少或者元素比较短的时候用压缩表ziplist(member1|score1|member2|score2|…, 按照score从小到大排列),
    • 其他时候就用跳跃表SkipList编码, 这个编码里包含一个字典结构和一个跳表结构, 但这两种数据结构都会通过指针来共享相同元素的成员和分值, 所以同时使用跳跃表和字典来保存集合元素不会产生任何重复成员或者分值, 也不会因此而浪费额外的内存:
      • 字典用于快速查找, 如ZScore查询member成员的 score 值, 或者快速确定是否有某个member
      • 跳表用于zrank/zrange等

zset各种问题

为什么zset用跳表不用红黑树

现在我们看看,对于这个问题,Redis的作者 @antirez 是怎么说的:

There are a few reasons:

  • They are not very memory intensive. It’s up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.
  • A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.
  • They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.

可参考: 本博客文章跳表
总结:

  • 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
  • 从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。
  • 从算法实现难度上来比较,skiplist比平衡树要简单得多。

zset是怎么支持查询排名的

跳表怎么支持查询排名的

延时队列用redis怎么做

用zset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者轮询zset用zrangebyscore指令获取N秒之前的数据轮询进行处理。

ZSET做排行榜时要实现分数相同时按时间顺序排序怎么实现

说了一个将 score 拆成高 32 位和低 32 位,高 32 位存分数,低 32 位存时间的方法。

哈希表渐进式rehash

  • 当以下条件中的任意一个被满足时, 程序会自动开始对哈希表执行扩展操作:

    • 服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 1 ;
    • 服务器目前正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 5 ;

      根据 BGSAVE 命令或 BGREWRITEAOF 命令是否正在执行, 服务器执行扩展操作所需的负载因子并不相同, 这是因为在执行 BGSAVE 命令或 BGREWRITEAOF 命令的过程中, Redis 需要创建当前服务器进程的子进程, 所以在子进程存在期间, 服务器会提高执行扩展操作所需的负载因子, 从而尽可能地避免在子进程存在期间进行哈希表扩展操作, 这可以避免不必要的内存写入操作, 最大限度地节约内存。

  • 另一方面, 当哈希表的负载因子小于 0.1 时, 程序自动开始对哈希表执行收缩操作。

以下是哈希表渐进式 rehash 的详细步骤:

  1. 为 ht[1] 分配空间, 让字典同时持有 ht[0] 和 ht[1] 两个哈希表。
  2. 在字典中维持一个索引计数器变量 rehashidx , 并将它的值设置为 0 , 表示 rehash 工作正式开始。
  3. 在 rehash 进行期间, 每次对字典执行删除、查找或者更新操作时, 程序除了执行指定的操作以外, 还会顺带将 ht[0] 哈希表在 rehashidx 索引上的所有键值对 rehash 到 ht[1] , 当 rehash 工作完成之后, 程序将 rehashidx 属性的值增一。
  4. 随着字典操作的不断执行, 最终在某个时间点上, ht[0] 的所有键值对都会被 rehash 至 ht[1] , 这时程序将 rehashidx 属性的值设为 -1 , 表示 rehash 操作已完成。

渐进式 rehash 的好处在于它采取分而治之的方式, 将 rehash 键值对所需的计算工作均滩到对字典的每个添加、删除、查找和更新操作上, 从而避免了集中式 rehash 而带来的庞大计算量。

因为在进行渐进式 rehash 的过程中, 字典会同时使用 ht[0] 和 ht[1] 两个哈希表, 所以在渐进式 rehash 进行期间, 字典的删除(delete)、查找(find)、更新(update)等操作会在两个哈希表上进行: 比如说, 要在字典里面查找一个键的话, 程序会先在 ht[0] 里面进行查找, 如果没找到的话, 就会继续到 ht[1] 里面进行查找, 诸如此类。

另外, 在渐进式 rehash 执行期间, 新添加到字典的键值对一律会被保存到 ht[1] 里面, 而 ht[0] 则不再进行任何添加操作: 这一措施保证了 ht[0] 包含的键值对数量会只减不增, 并随着 rehash 操作的执行而最终变成空表。

redis 持久化有哪几种方式,怎么选?

  • 混合持久化
    • 原因: 重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。如果使用 AOF 日志重放,性能则相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动的时候需要花费很长的时间。
    • 原理: 混合持久化同样也是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将aof_rewrite_buf重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。
    • 简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,
  • rdb
    • 优势:
      • RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
      • 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
      • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
    • 劣势:
      • 当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。
  • aof
    • 优势:
      • AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台刷盘线程执行一次fsync操作(这种一秒刷盘一次的策略, 可能会造成追加阻塞: 当硬盘资源繁忙时,即主线程发现距离上次fsync时间超过2秒, 为了数据安全性, 主线程会阻塞直到后台刷盘线程执行fsync操作完成),保证最多丢失1秒钟的数据。所以这也是redis重启优先加载aof的理由
      • AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
      • AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
      • AOF日志文件的命令通过可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据
    • 劣势:
      • 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
      • AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的

bgsave流程说一下

子进程创建RDB文件, 根据父进程内存生成临时快照文件, 完成后对原有RDB文件进行原子替换. 然后子进程发送信号给父进程表示完成

aof流程说一下以及aof追加阻塞是啥

追加阻塞:

AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台刷盘线程执行一次fsync操作(这种一秒刷盘一次的策略, 可能会造成追加阻塞: 当硬盘资源繁忙时,即主线程发现距离上次fsync时间超过2秒, 为了数据安全性, 主线程会阻塞直到后台刷盘线程执行fsync操作完成),保证最多丢失1秒钟的数据。

AOF重写的实现

  • 所谓的“重写”其实是一个有歧义的词语, AOF重写并不需要对原有AOF文件进行任何的读取,写入,分析等操作,这个功能是通过读取服务器当前的数据库状态来实现的。
  • 如当前列表键list在数据库中的值就为["C", "D", "E", "F", "G"]。要使用尽量少的命令来记录list键的状态,最简单的方式不是去读取和分析现有AOF文件的内容,,而是直接读取list键在数据库中的当前值,然后用一条RPUSH list "C" "D" "E" "F" "G"代替前面的6条命令

AOF 重写程序可以很好地完成创建一个新 AOF 文件的任务, 但是, 在执行这个程序的时候, 调用者线程会被阻塞。很明显, 作为一种辅佐性的维护手段, Redis 不希望 AOF 重写造成服务器无法处理请求, 所以 Redis 决定将 AOF 重写程序放到(后台)子进程里执行, 这样处理的最大好处是:

  • 子进程进行 AOF 重写期间,主进程可以继续处理命令请求。
  • 子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。

不过, 使用子进程也有一个问题需要解决: 因为子进程在进行 AOF 重写期间, 主进程还需要继续处理命令, 而新的命令可能对现有的数据进行修改, 这会让当前数据库的数据和重写后的 AOF 文件中的数据不一致。为了解决这个问题, Redis 增加了一个 AOF 重写缓存, 这个缓存在 fork 出子进程之后开始启用, Redis 主进程在接到新的写命令之后, 除了会将这个写命令的协议内容追加到现有的 AOF 文件之外, 还会追加到这个重写缓存中, 换言之, 当子进程在执行 AOF 重写时, 主进程需要执行以下三个工作:

  1. 处理命令请求。
  2. 将写命令追加到现有的 AOF 文件中。
  3. 将写命令追加到 AOF 重写缓存中。

当子进程完成 AOF 重写之后, 它会向父进程发送一个完成信号, 父进程在接到完成信号之后, 会调用一个信号处理函数, 并完成以下工作:

  1. 将 AOF 重写缓存中的内容全部写入到新 AOF 文件中。
  2. 对新的 AOF 文件进行改名rename,覆盖原有的 AOF 文件。这就是aof的原子替换.

在整个 AOF 后台重写过程中, 只有最后的写入缓存和改名操作会造成主进程阻塞, 在其他时候, AOF 后台重写都不会对主进程造成阻塞, 这将 AOF 重写对性能造成的影响降到了最低。以上就是 AOF 后台重写, 也即是 BGREWRITEAOF 命令的工作原理。

如何做rdb和aof的原子替换的

比如想要将temp文件原子替换origin文件, 则直接rename tmp文件到origin文件即可实现.
rename通过来说, 直接修改 file system metadata, 如inode信息. 在posix标准里, rename实现是原子的, 即:

  • rename成功, 原文件名 指向 temp 文件; 原文件内容被删除.
  • rename失败, 原文件名 仍指向原来的文件内容.

redis 主从同步是怎样的过程?

  1. 从redis发出sync要求
  2. 主redis开始bgsave(并且一边开启指令buffer来存储bgsave过程中的写指令们记为cmd)
  3. 主redis把bgsave生成的rdb发给从redis
  4. 把cmd发送给从redis
  5. 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令

总结:
主从刚刚连接的时候,进行全量同步;全量同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

redis key 的过期策略

Redis键的过期策略,是有定期删除+惰性删除两种。

  • 定期好理解,默认100ms就 随机 抽一些设置了过期时间的key,去检查是否过期,过期了就删了。
  • 惰性删除,查询时再判断是否过期,过期就删除键不返回值。

内存淘汰机制

当新增数据发现内存达到限制时,Redis触发内存淘汰机制。

  • lru
  • lfu(least frequency used, redis 4新增)
  • random
  • ttl

redis的LRU算法说一下

  • 普通的LRU算法:
    一般是用哈希表+双向链表来实现的:

    基于 HashMap 和 双向链表实现 LRU 的整体的设计思路是,可以使用 HashMap 存储 key,这样可以做到 save 和 get key的时间都是 O(1),而 HashMap 的 Value 指向双向链表实现的 LRU 的 Node 节点. 其核心操作的步骤是:
    • save(key, value):
      首先在 HashMap 找到 Key 对应的节点,如果节点存在,更新节点的值,并把这个节点移动队头。如果不存在,需要构造新的节点,并且尝试把节点塞到队头,如果LRU空间不足,则通过 tail 淘汰掉队尾的节点,同时在 HashMap 中移除 Key。
    • get(key):
      通过 HashMap 找到 LRU 链表节点,因为根据LRU 原理,这个节点是最新访问的,所以要把节点插入到队头,然后返回缓存的值。
  • Redis的LRU实现:
    • 如果按照HashMap和双向链表实现,需要额外的存储存放 next 和 prev 指针,牺牲比较大的存储空间,显然是不划算的。所以Redis采用了一个近似的做法,就是定时每隔一段时间就随机取出若干个key,然后按照访问时间排序后,淘汰掉最不经常使用的.
    • Redis 3.0之后又改善了算法的性能,会提供一个待淘汰候选key的pool,里面默认有16个key,按照空闲时间排好序。更新时从Redis键空间随机选择N个key,分别计算它们的空闲时间 idle,key只会在pool不满或者空闲时间大于pool里最小的时,才会进入pool,然后从pool中选择空闲时间最大的key淘汰掉。

redis哨兵

  • Redis Sentinel是Redis的高可用实现方案:故障发现、故障自动转移、配置中心 客户端通知。
  • Redis Sentinel从Redis 2.8版本开始才正式生产可用,之前版本生产不可用。
  • 尽可能在不同物理机上部署Redis Sentinel所有节点。
  • Redis Sentinel中的Sentinel节点个数应该为大于等于3且最好为奇数。
  • Redis Sentinel中的数据节点与普通数据节点没有区别。
  • 哨兵是一个配置提供者,而不是代理。在引入哨兵之后,客户端会先连接哨兵,再获取到主节点之后,客户端会和主节点直接通信。如果发生了故障转移,哨兵会通知到客户端。所以这也需要客户端的实现对哨兵的显式支持。
  • Redis Sentinel通过三个定时任务实现了Sentinel节点对于主节点、从节点、其余 Sentinel节点的监控。
  • Redis Sentinel在对节点做失败判定时分为主观下线和客观下线。
  • Redis Sentinel实现读写分离高可用可以依赖Sentinel节点的消息通知,获取Redis 数据节点的状态变化。

用文字描述一下故障切换(failover)的过程:

  • 假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。
  • 当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,就对这个主节点故障达成一致, 这个过程称为客观下线。
  • 这样对于客户端而言,一切都是透明的。然后通过raft算法从哨兵中选出一个哨兵来执行故障转移

redis集群

redis集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。
集群模式有以下几个特点:

  • 由多个Redis服务器组成的分布式网络服务集群;
  • 集群之中有多个Master主节点,每一个主节点都可读可写;
  • 节点之间会互相通信,两两相连, 采用gossip协议来通信;
  • Redis集群无中心节点。
  • 集群的伸缩本质是: 槽数据在节点中的移动

优点

在哨兵模式中,仍然只有一个Master节点。当并发写请求较大时,哨兵模式并不能缓解写压力。 我们知道只有主节点才具有写能力,那如果在一个集群中,能够配置多个主节点,缓解写压力,redis-cluster集群模式能达到此类要求。

在Redis-Cluster集群中,可以给每一个主节点添加从节点,主节点和从节点直接遵循主从模型的特性。
当用户需要处理更多读请求的时候,添加从节点开启read-only来读写分离可以扩展系统的读性能。

缺点

  • Redis 集群不支持那些需要同时处理多个键的 Redis 命令, 因为执行这些命令需要在多个 Redis 节点之间移动数据, 并且在高负载的情况下, 这些命令将降低 Redis 集群的性能, 并导致不可预测的行为。
  • 不能用redis事务机制(不过就算不用redis集群一般也不推荐用redis的事务, 毕竟假事务无法回滚嘛, 比如multi之后那些在 EXEC 命令执行之后所产生的错误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行。)。因为一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。

故障转移

Redis集群的主节点内置了类似Redis Sentinel的节点故障检测和自动故障转移功能,当集群中的某个主节点下线时,集群中的其他在线主节点会注意到这一点,并对已下线的主节点进行故障转移。
集群进行故障转移的方法和Redis Sentinel进行故障转移的方法基本一样(也有主观下线和客观下线),不同的是,在集群里面,故障转移的过程是:

  1. 在集群内广播选举消息
  2. 集群中其他在线的持有槽的主节点投票到故障主节点的从节点们
  3. 被选出来的从节点变成主节点

所以集群不必另外使用Redis Sentinel。

集群分片策略

常见的集群分片算法有:

  • 一般哈希算法
  • 一致性哈希算法
  • Hash Slot算法

Redis采用的是Hash Slot

一般哈希算法

计算方式:hash(key)%N
缺点:如果增加一个redis,映射公式变成了 hash(key)%(N+1)
​ 如果一个redis宕机了,映射公式变成了 hash(key)%(N-1)
​ 在以上两种情况下,几乎所有的缓存都失效了。

一致性哈希算法

先构造出一个长度为2^32整数环,根据节点名称的hash值(分布在[0,2^32-1])放到这个环上。现在要存放资源,根据资源的Key的Hash值(也是分布在[0,2^32-1]),在环上顺时针的找到离它最近的一个节点,就建立了资源和节点的映射关系。

  • 优点:一个节点宕机时,上面的数据转移到顺时针的下一个节点中,新增一个节点时,也只需要将部分数据迁移到这个节点中,对其他节点的影响很小
  • 缺点:由于数据在环上分布不均,可能存在某个节点存储的数据比较多,那么当他宕机的时候,会导致大量数据涌入下一个节点中,把另一个节点打挂了,然后所有节点都挂了
  • 改进:引进了虚拟节点的概念,想象在这个环上有很多“虚拟节点”,数据的存储是沿着环的顺时针方向找一个虚拟节点,每个虚拟节点都会关联到一个真实节点

HashSlot算法

Redis采用的是Hash Slot分片算法,用来计算key存储位置的。集群将整个数据库分为16384个槽位slot,所有key-value数据都存储在这些slot中的某一个上。一个slot槽位可以存放多个数据,key的槽位计算公式为:slot_number=CRC16(key)%16384,其中CRC16为16位的循环冗余校验和函数。
客户端可能会挑选任意一个redis实例去发送命令,每个redis实例接收到命令,都会计算key对应的hash slot,如果在本地就在本地处理,否则返回moved给客户端,让客户端进行重定向到对应的节点执行命令(实现得好一点的smart客户端会缓存键-slot-节点的映射关系来获得性能提升).

那为什么是16384个槽呢?

ps:CRC16算法产生的hash值有16bit,该算法可以产生2^16-=65536个值。换句话说,值是分布在0~65535之间。那作者在做mod运算的时候,为什么不mod65536,而选择mod16384?作者解答

在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384=16k,在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 2K),也就是说使用2k的空间创建了16k的槽数。

虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) = 8K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。

Cache和DB如何一致

详细的请参考: https://segmentfault.com/a/1190000015804406
本博客也有一份: Cache和DB一致性

总结:

  • 使用cache aside pattern
    • 对于读请求

      先读 cache,再读 db
      如果,cache hit,则直接返回数据
      如果,cache miss,则访问 db,并将数据 set 回缓存
    • 对于写请求

      先操作数据库,再淘汰缓存(淘汰缓存,而不是更新缓存, 如果更新缓存,在并发写时,可能出现数据不一致。)
  • Cache Aside Pattern 方案存在什么问题?
    • 问题1: 如果先写数据库,再淘汰缓存,在原子性被破坏时:
      1. 修改数据库成功了
      2. 淘汰缓存失败了
        导致,数据库与缓存的数据不一致。
      • 如何解决问题1?
        • 在淘汰缓存的时候,如果失败,则重试一定的次数。如果失败一定次数还不行,那就是其他原因了。比如说 redis 故障、内网出了问题。
    • 问题2: 主从同步延迟导致的缓存和数据不一致问题
      • 问题: 发生写请求后(不管是先操作 DB,还是先淘汰 Cache),在主从数据库同步完成之前,如果有读请求,都可能发生读 Cache Miss,读从库把旧数据存入缓存的情况。此时怎么办呢?
      • 解决思路: 在主从时延的时间段内,读取修改过的数据的话,强制读主,并且更新缓存,这样子缓存内的数据就是最新。在主从时延过后,这部分数据继续读从库,从而继续利用从库提高读取能力。
      • 具体解决方案:
        • 写请求发生的时候: 将哪个库,哪个表,哪个主键三个信息拼装一个 key 设置到 cache 里,这条记录的超时时间,设置为 “主从同步时延”, PS:key 的格式为 “db:table:PK”,假设主从延时为 1s,这个 key 的 cache 超时时间也为 1s。
        • 当读请求发生时:这是要读哪个库,哪个表,哪个主键的数据呢,也将这三个信息拼装一个 key,到 cache 里去查询,如果,
          • (1)cache 里有这个 key,说明 1s 内刚发生过写请求,数据库主从同步可能还没有完成,此时就应该去主库查询。并且把主库的数据 set 到缓存中,防止下一次 cache miss。
          • (2)cache 里没有这个 key,说明最近没有发生过写请求,此时就可以去从库查询

缓存雪崩是啥?咋处理?

是指大面积的缓存失效,打崩了DB.

如果缓存挂掉,所有的请求会压到数据库,如果未提前做容量预估,可能会把数据库压垮(在缓存恢复之前,数据库可能一直都起不来),导致系统整体不可服务。
又或者打个比方, 如果所有首页的Key失效时间都是12小时,中午12点刷新的,我零点有个秒杀活动大量用户涌入,假设当时每秒 6000 个请求,本来缓存在可以扛住每秒 5000 个请求,但是缓存当时所有的Key都失效了。此时 1 秒 6000 个请求全部落数据库,数据库必然扛不住.

处理方案:

  • key随机过期
  • key永不过期, 比如开个单独线程去定时更新缓存
  • 高可用, 如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题
  • 隔离服务, 限流降级

缓存穿透是啥?咋处理?

是指缓存和数据库中都没有的数据,而用户不断发起请求,严重会击垮数据库

我们数据库的 id 都是1开始自增上去的,如发起为id值为 -1 的数据或 id 为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据库。

处理方案:

  • 缓存穿透我会在接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return,比如:id 做基础校验,id <=0的直接拦截等。
  • 布隆过滤器, 把存在的key提前存放好在布隆过滤器中, 当查询的时候快速判断出你这个Key是否在数据库中存在, 不存在则直接return

缓存击穿是啥?咋处理?

是指持续的大并发的访问一个热点数据, 当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库

这个跟缓存雪崩有点像,但是又有一点不一样,缓存雪崩是因为大面积的缓存失效,打崩了DB,而缓存击穿不同的是缓存击穿是指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。

处理方案:

  • key永不过期, 比如开个单独线程去定时更新缓存
  • 互斥锁, 在key失效的瞬间, 只允许一个查询操作的线程A去查询数据库并重建缓存并上互斥锁, 其他的查询操作线程全部等待线程A操作完了再从缓存里取数据

服务器开发自我修养专栏-编码知识

Posted on 04-18-2021 | In Self-cultivation

编码知识

Base64 的原理?编码后比编码前是大了还是小了。

结论:

大了. 因为Base64 编码本质上是一种将二进制数据转成文本数据的方案。对于非二进制数据,是先将其转换成二进制形式,然后每连续 6 比特(2 的 6 次方 = 64)计算其十进制值,根据该值在上面的索引表中找到对应的字符,最终得到一个文本字符串。也就是说, 每 3 个原始字符编码成 4 个字符,如果原始字符串长度不能被 3 整除,那怎么办?使用 0 值来补充原始字符串。

base64的原理

Base64 编码之所以称为 Base64,是因为其使用 64 个字符来对任意数据进行编码,同理有 Base32、Base16 编码。标准 Base64 编码使用的 64 个字符为:

这 64 个字符是各种字符编码(比如 ASCII 编码)所使用字符的子集,基本,并且可打印。唯一有点特殊的是最后两个字符,因对最后两个字符的选择不同,Base64 编码又有很多变种,比如 Base64 URL 编码。

Base64 编码本质上是一种将二进制数据转成文本数据的方案。对于非二进制数据,是先将其转换成二进制形式,然后每连续 6 比特(2 的 6 次方 = 64)计算其十进制值,根据该值在上面的索引表中找到对应的字符,最终得到一个文本字符串。

假设我们要对 Hello! 进行 Base64 编码,按照 ASCII 表,其转换过程如下图所示:

可知 Hello! 的 Base64 编码结果为 SGVsbG8h ,原始字符串长度为 6 个字符,编码后长度为 8 个字符,每 3 个原始字符经 Base64 编码成 4 个字符,编码前后长度比 4/3,这个长度比很重要 - 比原始字符串长度短,则需要使用更大的编码字符集,这并不我们想要的;长度比越大,则需要传输越多的字符,传输时间越长。Base64 应用广泛的原因是在字符集大小与长度比之间取得一个较好的平衡,适用于各种场景。

是不是觉得 Base64 编码原理很简单?

但这里需要注意一个点:Base64 编码是每 3 个原始字符编码成 4 个字符,如果原始字符串长度不能被 3 整除,那怎么办?使用 0 值来补充原始字符串。

以 Hello!! 为例,其转换过程为:

注:图表中蓝色背景的二进制 0 值是额外补充的。

Hello!! Base64 编码的结果为 SGVsbG8hIQAA 。最后 2 个零值只是为了 Base64 编码而补充的,在原始字符中并没有对应的字符,那么 Base64 编码结果中的最后两个字符 AA 实际不带有效信息,所以需要特殊处理,以免解码错误。

标准 Base64 编码通常用 = 字符来替换最后的 A,即编码结果为 SGVsbG8hIQ==。因为 = 字符并不在 Base64 编码索引表中,其意义在于结束符号,在 Base64 解码时遇到 = 时即可知道一个 Base64 编码字符串结束。

如果 Base64 编码字符串不会相互拼接再传输,那么最后的 = 也可以省略,解码时如果发现 Base64 编码字符串长度不能被 4 整除,则先补充 = 字符,再解码即可。

解码是对编码的逆向操作,但注意一点:对于最后的两个 = 字符,转换成两个 A 字符,再转成对应的两个 6 比特二进制 0 值,接着转成原始字符之前,需要将最后的两个 6 比特二进制 0 值丢弃,因为它们实际上不携带有效信息。

utf8编码和unicode字符集

总结:

  • unicode是个字符集, 只是一个符号对应表, 它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储
  • utf8是unicode符号具体的编码方式, 规定了该怎么存储

说到utf8,就不得不说一下unicode了。 Unicode是一个很大的集合,每一个unicode对应一个符号,不管是中文的汉字,英文字符,日文,韩文等等。现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母 Ain,U+0041表示英语的大写字母A,U+4E25表示汉字“严”。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表。

需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

比如,汉字“严”的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

这里就有两个严重的问题,第一个问题是:如何才能区别unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是:我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。

它们造成的结果是:

1)出现了unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示unicode。

2)unicode在很长一段时间内无法推广,直到互联网的出现。

UTF-8

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8的编码规则很简单,只有二条:

  • 1)对于单字节的符号,字节的第一位(字节的最高位)设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

  • 2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

下表总结了编码规则,字母x表示可用编码的位。

Unicode符号范围 UTF-8编码方式(十六进制) | (二进制)

—————+———————————————————————
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

下面,还是以汉字“严”为例,演示如何实现UTF-8编码:
已知“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。

rpclib源码阅读笔记之浅谈cpp func traits

Posted on 04-18-2021 | In Misc

原文出处


0. 导语

大家好,我是光城,欢迎关注公众号:guangcity。在 STL 编程中,容器和算法是独立设计的,即数据结构和算法是独立设计的,连接容器和算法的桥梁就是迭代器了,迭代器使其独立设计成为可能。如下图所示:

上图给出了 STL 的目标就是要把数据和算法分开,分别对其进行设计,之后通过一种名为 iterator 的东西,把这二者再粘接到一起。

设计模式中,关于 iterator 的描述为:一种能够顺序访问容器中每个元素的方法,使用该方法不能暴露容器内部的表达方式。而类型萃取技术就是为了要解决和 iterator 有关的问题的。

它将范型算法 (find, count, find_if) 用于某个容器中, 最重要的是要给算法提供一个访问容器元素的工具,iterator 就扮演着这个重要的角色。

而在算法中我们可能会定义简单的中间变量或者设定算法的返回变量类型,这时候需要知道迭代器所指元素的类型是什么,但是由于没有 typeof 这类判断类型的函数, 我们无法直接获取,那该如何是好?本文就来具体阐述。

对于迭代器来说就是一种智能指针,因此,它也就拥有了一般指针的所有特点——能够对其进行 * 和 -> 操作。但是在遍历容器的时候,不可避免的要对遍历的容器内部有所了解,所以,干脆把迭代器的开发工作交给容器的设计者好了,如此以来,所有实现细节反而得以封装起来不被使用者看到,这正是为什么每一种 STL 容器都提供有专属迭代器的缘故。

而 Traits 在bits/stl_iterator_base_types.h中:

template<class _Tp>
struct iterator_traits<_Tp*>
{
typedef ptrdiff_t difference_type;
typedef typename _Tp::value_type value_type;
typedef typename _Tp::pointer pointer;
typedef typename _Tp::reference reference;
typedef typename _Tp::iterator_category iterator_category;
};

看的一脸懵逼吧,没事,看完本节,入门 STL,哈哈~

1.template 参数推导

首先,在算法中运用迭代器时,很可能会用到其相应型别(associated type)(迭代器所指之物的型别)。假设算法中有必要声明一个变量,以 “迭代器所指对象的型别” 为型别,该怎么办呢?

解决方法是:利用 function template 的参数推导机制。

例如:

如果 T 是某个指向特定对象的指针,那么在 func 中需要指针所指向对象的型别的时候,怎么办呢?这个还比较容易,模板的参数推导机制可以完成任务,

template <class I>
inline
void func(I iter) {
func_impl(iter, *iter); // 传入iter和iter所指的值,class自动推导
}

通过模板的推导机制,我们轻而易举的或得了指针所指向的对象的类型。

template <class I, class T>
void func_impl(I iter, T t) {
T tmp; // 这里就是迭代器所指物的类别
// ... 功能实现
}

int main() {
int i;
func(&i);
}

但是,函数的 “template 参数推导机制” 推导的只是参数,无法推导函数的返回值类型。万一需要推导函数的传回值,就无能为力了。因此,我们引出下面的方法。

2. 声明内嵌型别

迭代器所指对象的型别,称之为迭代器的 value type。

尽管在 func_impl 中我们可以把 T 作为函数的返回值,但是问题是用户需要调用的是 func。

template <class I, class T>
T func_impl(I iter, T t) {
T tmp; // 这里就是迭代器所指物的类别
// ... 功能实现
}
template <class T>
(*T) func(T t) { // !!!Wrong code
return func_impl(t, *t); // forward the task to func_impl
}
int main() {
int i =10;
cout<<func(&i)<<endl; // !!! Can’t pass compile
}

如果去编译上述代码,编译失败!

这个问题解决起来也不难,声明内嵌型别似乎是个好主意,这样我们就可以直接获取。只要做一个 iterator,然后在定义的时候为其指向的对象类型制定一个别名,就好了,像下面这样:

template <class T>
struct MyIter {
typedef T value_type; // 内嵌型别声明
T* ptr;
MyIter(T* p = 0) : ptr(p) {}
T& operator*() const { return *ptr; }
};

template <class I>
typename I::value_type
func(I ite) {
std::cout << "class version" << std::endl;
return *ite;
}
int main() {
// ...
MyIter<int> ite(new int(8));
cout << func(ite); // 输出8
}

很漂亮的解决方案,看上去一切都很完美。但是,实际上还是有问题,因为 func 如果是一个泛型算法,那么它也绝对要接受一个原生指针作为迭代器,但是显然,你无法让下面的代码编译通过:

int *p = new int(5);
cout<<func(p)<<endl; // error

我们的 func 无法支持原生指针,这显然是不能接受的。此时,template partial specialization 就派上了用场。

3. 救世主 Traits

前面也提到了,如果直接使用typename I::value_type,算法就无法接收原生指针,因为原生指针根本就没有 value_type 这个内嵌类型。

因此,我们还需要加入一个中间层对其进行判断,看它是不是原生指针,注意,这就是 traits 技法的妙处所在。

如果我们只使用上面的做法,也就是内嵌 value_type,那么对于没有 value_type 的指针,我们只能对其进行偏特化,这种偏特化是针对可调用函数 func 的偏特化,假如 func 有 100 万行行代码,那么就会造成极大的视觉污染。

(1)函数偏特化

函数偏特化:

template <class T>
struct MyIter {
typedef T value_type; // 内嵌型别声明
T* ptr;
MyIter(T* p = 0) : ptr(p) {}
T& operator*() const { return *ptr; }
};

template <class I>
typename I::value_type
func(I ite) {
std::cout << "class version" << std::endl;
return *ite;
}
template <class I>
I
func(I* ite) {
std::cout << "pointer version" << std::endl;
return *ite;
}
template <class I>
I func(const I* ite) {
std::cout << "const pointer version" << std::endl;
return *ite;
}
int main() {
// ...
MyIter<int> ite(new int(8));
cout << func(ite)<<endl;
int *p = new int(52);
cout<<func(p)<<endl;
const int k = 3;
cout<<func(&k)<<endl;
}

输出:

class version
8
pointer version
52
const pointer version
3

(2)加入中间层

在 STL 中 Traits 是什么呢?看下图:

利用一个中间层iterator_traits固定了func的形式,使得重复的代码大量减少,唯一要做的就是稍稍特化一下 iterator_tartis 使其支持 pointer 和 const pointer:)

#include <iostream>

template <class T>
struct MyIter {
typedef T value_type; // 内嵌型别声明
T* ptr;
MyIter(T* p = 0) : ptr(p) {}
T& operator*() const { return *ptr; }
};
// class type
template <class T>
struct iterator_traits {
typedef typename T::value_type value_type;
};
// 偏特化1
template <class T>
struct iterator_traits<T*> {
typedef T value_type;
};
// 偏特化2
template <class T>
struct iterator_traits<const T*> {
typedef T value_type;
};

template <class I>
typename iterator_traits<I>::value_type
// 首先询问iterator_traits<I>::value_type,如果传递的I为指针,则进入特化版本,iterator_traits直接回答;如果传递进来的I为class type,就去询问T::value_type.
func(I ite) {
std::cout << "normal version" << std::endl;
return *ite;
}
int main() {
// ...
MyIter<int> ite(new int(8));
std::cout << func(ite)<<std::endl;
int *p = new int(52);
std::cout<<func(p)<<std::endl;
const int k = 3;
std::cout<<func(&k)<<std::endl;
}

上述的过程是首先询问iterator_traits<I>::value_type,如果传递的 I 为指针, 则进入特化版本,iterator_traits直接回答T; 如果传递进来的I为class type, 就去询问T::value_type.

上述的通俗解释为算法 (func) 问 iterator_traits(我),但是 iterator_traits(我)发现手上是指针的时候,就由我来替它回答。如果是 class type,iterator_traits(我)就继续问(他 —T::value_type)。

总结:通过定义内嵌类型,我们获得了知晓 iterator 所指元素类型的方法,通过 traits 技法,我们将函数模板对于原生指针和自定义 iterator 的定义都统一起来,我们使用 traits 技法主要是为了解决原生指针和自定义 iterator 之间的不同所造成的代码冗余,这就是 traits 技法的妙处所在。

学习书籍:

侯捷《 STL 源码剖析》

学习文章:

https://juejin.im/post/5b1a43fb51882513bf1795c6
https://www.cnblogs.com/mangoyuan/p/6446046.html
http://www.cppblog.com/nacci/archive/2005/11/03/911.aspx

服务器开发自我修养专栏-Python精要

Posted on 03-27-2021 | In Self-cultivation

python

  • mro问题
  • 怎么实现一个协程库?
  • mock是啥: https://zhuanlan.zhihu.com/p/30380243

import流程

  • 当Python的解释器遇到import语句或者其他上述导入语句时,它会先去查看sys.modules中是否已经有同名模块被导入了,
  • 如果有就直接取来用;没有就去查阅sys.path里面所有已经储存的目录.
    • sys.path这个列表初始化的时候,通常包含一些来自外部的库(external libraries)或者是来自操作系统的一些库,当然也会有一些类似于dist-package的标准库在里面.这些目录通常是被按照顺序或者是直接去搜索想要的–如果说他们当中的一个包含有期望的package或者是module,这个package或者是module将会在整个过程结束的时候被直接提取出来保存在sys.modules中(sys.modules是一个模块名:模块对象的字典结构).
    • 当然,这个 sys.path 是可以修改的(正如上文提到的一种解决办法)。值得注意的是,如果当前目录包含有和标准库同名的模块,会直接使用当前目录的模块而不是标准模块。
    • 当在这些个地址中实在是找不着时,它就会抛出一个ModuleNotFoundError错误.
  • 当我们要导入一个模块(比如 foo )时,解释器首先会根据命名查找内置模块,如果没有找到,它就会去查找 sys.path 列表中的目录,看目录中是否有 foo.py 。sys.path 的初始值来自于:
    • 运行脚本所在的目录(如果打开的是交互式解释器则是当前目录)
    • PYTHONPATH 环境变量(类似于 PATH 变量,也是一组目录名组成)
    • Python 安装时的默认设置

为啥字符串join比加号连接快

字符串是不可变对象,当用操作符+连接字符串的时候,每执行一次+都会申请一块新的内存,然后复制上一个+操作的结果和本次操作的右操作符到这块内存空间,因此用+连接字符串的时候会涉及好几次内存申请和复制。而join在连接字符串的时候,会先计算需要多大的内存存放结果,然后一次性申请所需内存并将字符串复制过去,这是为什么join的性能优于+的原因。所以在连接字符串数组的时候,我们应考虑优先使用join。

is和==的区别

官方文档中说 is 表示的是对象标示符(object identity),而 == 表示的是相等(equality)。is 的作用是用来检查对象的标示符是否一致,也就是比较两个对象在内存中的地址是否一样,而 == 是用来检查两个对象是否相等。

我们在检查 a is b 的时候,其实相当于检查 id(a) == id(b)。而检查 a == b 的时候,实际是调用了对象 a 的 eq() 方法,a == b 相当于 a.eq(b)。

一般情况下,如果 a is b 返回True的话,即 a 和 b 指向同一块内存地址的话,a == b 也返回True,即 a 和 b 的值也相等。

元类

参考: https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072

python元类的使用场景, 比如orm框架, ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。

type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:

\>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>

要创建一个 class 对象,type()函数依次传入 3 个参数:

  1. class 的名称;
  2. 继承的父类集合,注意 Python 支持多重继承,如果只有一个父类,别忘了 tuple 的单元素写法;
  3. class 的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

通过type()函数创建的类和直接写 class 是完全一样的,因为 Python 解释器遇到 class 定义时,仅仅是扫描一下 class 定义的语法,然后调用type()函数创建出 class。

正常情况下,我们都用class Xxx...来定义类,但是,type()函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。

metaclass

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用 metaclass。
metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据 metaclass 创建出类,所以:先定义 metaclass,然后创建类。
连接起来就是:先定义 metaclass,就可以创建类,最后创建实例。
所以,metaclass 允许你创建类或者修改类。换句话说,你可以把类看成是 metaclass 创建出来的 “实例”。
我们先看一个简单的例子,这个 metaclass 可以给我们自定义的 MyList 增加一个add方法:
定义ListMetaclass,按照默认习惯,metaclass 的类名总是以 Metaclass 结尾,以便清楚地表示这是一个 metaclass:

class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs\['add'\] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)

有了 ListMetaclass,我们在定义类的时候还要指示使用 ListMetaclass 来定制类,传入关键字参数metaclass:

class MyList(list, metaclass=ListMetaclass):
pass

当我们传入关键字参数metaclass时,魔术就生效了,它指示 Python 解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

__new__()方法接收到的参数依次是:

  1. 当前准备创建的类的对象;
  2. 类的名字;
  3. 类继承的父类集合;
  4. 类的方法集合。

测试一下MyList是否可以调用add()方法:

\>>> L = MyList()
>>> L.add(1)
>> L
\[1\]

而普通的list没有add()方法:

\>>> L2 = list()
>>> L2.add(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'add'

装饰器

def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

@log
def now():
print('2015-3-25')

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

>>> now()
call now():
2015-3-25

把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(args, *kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator

这个3层嵌套的decorator用法如下:

@log('execute')
def now():
print('2015-3-25')

执行结果如下:

>>> now()
execute now():
2015-3-25

和两层嵌套的decorator相比,3层嵌套的效果是这样的:
now = log('execute')(now)
我们来剖析上面的语句,首先执行log(‘execute’),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

python命令行参数

  • -u参数的使用:python命令加上-u(unbuffered)参数后会强制其标准输出也同标准错误一样不通过缓存直接打印到屏幕。
  • -c参数,支持执行单行命令/脚本。如: python -c "import os;print('hello'),print('world')"

python -m test_folder/test.py与python test_folder/test有什么不同

桌面的test_folder文件夹下有个test.py

test.py
import sys
print(sys.path)

运行看看:

hulinhong@GIH-D-14531 MINGW64 ~/Desktop
$ python test_folder/test.py
['C:\\Users\\hulinhong\\Desktop\\test_folder', 'C:\\Program Files\\Python37\\python37.zip', 'C:\\Program Files\\Python37\\DLLs', 'C:\\Program Files\\Python37\\lib', 'C:\\Program Files\\Python37', 'C:\\Program Files\\Python37\\lib\\site-packages', 'C:\\Program Files\\Python37\\lib\\site-packages\\redis_py_cluster-2.1.0-py3.7.egg']

hulinhong@GIH-D-14531 MINGW64 ~/Desktop
$ python -m test_folder.test
['C:\\Users\\hulinhong\\Desktop', 'C:\\Program Files\\Python37\\python37.zip', 'C:\\Program Files\\Python37\\DLLs', 'C:\\Program Files\\Python37\\lib', 'C:\\Program Files\\Python37', 'C:\\Program Files\\Python37\\lib\\site-packages', 'C:\\Program Files\\Python37\\lib\\site-packages\\redis_py_cluster-2.1.0-py3.7.egg']

细心的同学会发现,区别就是在第一个路径:

  • python直接启动是把test.py文件所在的目录放到了sys.path属性中。
  • 模块启动是把你输入命令的目录(也就是当前路径),放到了sys.path属性中

所以就会有下面的情况:

目录结构如下

package/
__init__.py
mod1.py
package2/
__init__.py
run.py

run.py 内容如下

import sys
from package import mod1
print(sys.path)

如何才能启动run.py文件?

  • 直接启动(失败)

    ➜  test_import_project git:(master) ✗ python package2/run.py
    Traceback (most recent call last):
    File "package2/run.py", line 2, in <module>
    from package import mod1
    ImportError: No module named package
  • 以模块方式启动(成功)

    ➜  test_import_project git:(master) ✗ python -m package2.run
    ['C:\\Users\\hulinhong\\Desktop',
    '/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',
    ...]

当需要启动的py文件引用了一个模块。你需要注意:在启动的时候需要考虑sys.path中有没有你import的模块的路径!
这个时候,到底是使用直接启动,还是以模块的启动?目的就是把import的那个模块的路径放到sys.path中。你是不是明白了呢?

官方文档参考: http://www.pythondoc.com/pythontutorial3/modules.html

导入一个叫 mod1 的模块时,解释器先在当前目录中搜索名为 mod1.py 的文件。如果没有找到的话,接着会到 sys.path 变量中给出的目录列表中查找。 sys.path 变量的初始值来自如下:

输入脚本的目录(当前目录)。

  • 环境变量 PYTHONPATH 表示的目录列表中搜索(这和 shell 变量 PATH 具有一样的语法,即一系列目录名的列表)。
  • Python 默认安装路径中搜索。
  • 实际上,解释器由 sys.path 变量指定的路径目录搜索模块,该变量初始化时默认包含了输入脚本(或者当前目录), PYTHONPATH 和安装目录。这样就允许 Python程序了解如何修改或替换模块搜索目录。

在python程序中调用cpp的库创建的线程是否受制于GIL?

首先要理解什么是GIL.
Python 的多线程是真的多线程,只不过在任意时刻,它们中只有一个线程能够取得 GIL 从而被允许执行 Python 代码。其它线程要么等着,要么干别的和 Python 无关的事情(比如等待系统 I/O,或者算点什么东西)。

那如果是通过CPP扩展创建出来的线程,可以摆脱这个限制么?
很简单,不访问 Python 的数据和方法,就和 GIL 没任何关系。如果需要访问 Python,还是需要先取得 GIL.

GIL 是为了保护 Python 数据不被并发访问破坏,所以当你不访问 Python 的数据的时候自然就可以释放(或者不取得)GIL。反过来,如果需要访问 Python 的数据,就一定要取得 GIL 再访问。PyObject 等不是线程安全的。多线程访问任何非线程安全的数据都需要先取得对应的锁。Python 所有的 PyObject 什么的都共享一个锁,它就叫 GIL。

__new__ 与 __del__ 与 __init__

先来看一个单例模式的实现

class Demo:
__isinstance = False
def __new__(cls, *args, **kwargs):
if not cls.__isinstance: # 如果被实例化了
cls.__isinstance = object.__new__(cls) # 否则实例化
return cls.__isinstance # 返回实例化的对象

def __init__(self, name):
self.name = name
print('my name is %s'%(name))

def __del__(self):
print('886, %s'%(self.name))


d1 = Demo('Alice')
d2 = Demo('Anew')
print(d1)
print(d2)

打印:

my name is Alice
my name is Anew
<__main__.Demo object at 0x000001446604D3C8>
<__main__.Demo object at 0x000001446604D3C8>
886, Anew

__new__ 是负责对当前类进行实例化,并将实例返回,并传给__init__方法,__init__方法中的self就是指代__new__传过来的对象,所以再次强调,__init__是实例化后调用的第一个方法。

__del__在对象销毁时被调用,往往用于清除数据或还原环境等操作,比如在类中的其他普通方法中实现了插入数据库的语句,当对象被销毁时我们需要将数据还原,那么这时可以在__del__方法中实现还原数据库数据的功能。__del__被成为析构方法,同样和C++中的析构方法类似。

python垃圾回收

总体来说,在Python中,主要通过引用计数进行垃圾回收;通过 “标记-清除” 解决容器对象可能产生的循环引用问题;通过 “分代回收” 以空间换时间的方法提高垃圾回收效率。

  • 引用计数
  • 标记清除(Mark and Sweep)
  • 分代回收

标记清除咋弄的

参考: https://zhuanlan.zhihu.com/p/83251959

Python 采用了 “标记-清除”(Mark and Sweep) 算法,解决容器对象可能产生的循环引用问题。(注意,只有容器对象才会产生循环引用的情况,比如列表、字典、用户自定义类的对象、元组等。而像数字,字符串这类简单类型不会出现循环引用。作为一种优化策略,对于只包含简单类型的元组也不在标记清除算法的考虑之列)

跟其名称一样,该算法在进行垃圾回收时分成了两步,分别是:

  • A)标记阶段,遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达;
  • B)清除阶段,再次遍历对象,如果发现某个对象没有标记为可达,则就将其回收。

如下图所示,在标记清除算法中,为了追踪容器对象,需要每个容器对象维护两个额外的指针,用来将容器对象组成一个双端链表,指针分别指向前后两个容器对象,方便插入和删除操作。python 解释器 (Cpython) 维护了两个这样的双端链表,一个链表存放着需要被扫描的容器对象,另一个链表存放着临时不可达对象。在图中,这两个链表分别被命名为”Object to Scan”和”Unreachable”。图中例子是这么一个情况:link1,link2,link3 组成了一个引用环,同时 link1 还被一个变量 A(其实这里称为名称 A 更好)引用。link4 自引用,也构成了一个引用环。从图中我们还可以看到,每一个节点除了有一个记录当前引用计数的变量 ref_count 还有一个 gc_ref 变量,这个 gc_ref 是 ref_count 的一个副本,所以初始值为 ref_count 的大小。

gc 启动的时候,会逐个遍历”Object to Scan” 链表中的容器对象,并且将当前对象所引用的所有对象的 gc_ref 减一。(扫描到 link1 的时候,由于 link1 引用了 link2, 所以会将 link2 的 gc_ref 减一,接着扫描 link2, 由于 link2 引用了 link3, 所以会将 link3 的 gc_ref 减一…..) 像这样将”Objects to Scan” 链表中的所有对象考察一遍之后,两个链表中的对象的 ref_count 和 gc_ref 的情况如下图所示。这一步操作就相当于解除了循环引用对引用计数的影响。

接着,gc 会再次扫描所有的容器对象,如果对象的 gc_ref 值为 0,那么这个对象就被标记为 GC_TENTATIVELY_UNREACHABLE,并且被移至”Unreachable” 链表中。下图中的 link3 和 link4 就是这样一种情况。

如果对象的 gc_ref 不为 0,那么这个对象就会被标记为 GC_REACHABLE。同时当 gc 发现有一个节点是可达的,那么他会递归式的将从该节点出发可以到达的所有节点标记为 GC_REACHABLE, 这就是下图中 link2 和 link3 所碰到的情形。

除了将所有可达节点标记为 GC_REACHABLE 之外,如果该节点当前在”Unreachable” 链表中的话,还需要将其移回到”Object to Scan” 链表中,下图就是 link3 移回之后的情形。

第二次遍历的所有对象都遍历完成之后,存在于”Unreachable” 链表中的对象就是真正需要被释放的对象。如上图所示,此时 link4 存在于 Unreachable 链表中,gc 随即释放之。

上面描述的垃圾回收的阶段,会暂停整个应用程序,等待标记清除结束后才会恢复应用程序的运行。

为啥标记清除回收无法回收重写了__del__方法的类对象

Circular references which are garbage are detected when the option cycle detector is enabled (it’s on by default), but can only be cleaned up if there are no Python-level __del__() methods involved.

官方文档中表明启用周期检测器时会检测到垃圾的循环引用(默认情况下它是打开的),但只有在没有涉及 Python __del__() 方法的情况下才能清除。Python 不知道破坏彼此保持循环引用的对象的安全顺序,因此它则不会为这些方法调用析构函数。简而言之,如果定义了 __del__ 函数,那么在循环引用中Python解释器无法判断析构对象的顺序,因此就不做处理。

分代回收

在循环引用对象的回收中,整个应用程序会被暂停,为了减少应用程序暂停的时间,Python 通过“分代回收”(Generational Collection)以空间换时间的方法提高垃圾回收效率。

分代回收是基于这样的一个统计事实,对于程序,存在一定比例的内存块的生存周期比较短;而剩下的内存块,生存周期会比较长,甚至会从程序开始一直持续到程序结束。生存期较短对象的比例通常在 80%~90% 之间,这种思想简单点说就是:对象存在时间越长,越可能不是垃圾,应该越少去收集。这样在执行标记-清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度。

python gc给对象定义了三种世代(0,1,2),每一个新生对象在generation zero中,如果它在一轮gc扫描中活了下来,那么它将被移至generation one,在那里他将较少的被扫描,如果它又活过了一轮gc,它又将被移至generation two,在那里它被扫描的次数将会更少。

gc的扫描在什么时候会被触发呢?答案是当某一世代中被分配的对象与被释放的对象之差达到某一阈值的时候,就会触发gc对某一世代的扫描。值得注意的是当某一世代的扫描被触发的时候,比该世代年轻的世代也会被扫描。也就是说如果世代2的gc扫描被触发了,那么世代0,世代1也将被扫描,如果世代1的gc扫描被触发,世代0也会被扫描。

该阈值可以通过下面两个函数查看和调整:

gc.get_threshold() # (threshold0, threshold1, threshold2).
gc.set_threshold(threshold0[, threshold1[, threshold2]])

下面对set_threshold()中的三个参数threshold0, threshold1, threshold2进行介绍。gc会记录自从上次收集以来新分配的对象数量与释放的对象数量,当两者之差超过threshold0的值时,gc的扫描就会启动,初始的时候只有世代0被检查。如果自从世代1最近一次被检查以来,世代0被检查超过threshold1次,那么对世代1的检查将被触发。相同的,如果自从世代2最近一次被检查以来,世代1被检查超过threshold2次,那么对世代2的检查将被触发。get_threshold()是获取三者的值,默认值为(700,10,10).

1234567891011121314151617…37
Mike

Mike

🚙 🚗 💨 💨 If you want to create a blog like this, just follow my open-source project, "hexo-theme-neo", click the GitHub button below and check it out ^_^ . It is recommended to use Chrome, Safari, or Edge to read this blog since this blog was developed on Edge (Chromium kernel version) and tested on Safari.

11 categories
296 posts
111 tags
about
GitHub Spotify
© 2013 - 2025 Mike