From d42a09c9aa7f0c14372a8ecb52a1b03b04b2f12d Mon Sep 17 00:00:00 2001 From: Sumesh Premraj Date: Fri, 27 Nov 2020 10:45:31 +0530 Subject: [PATCH] Add Relational Databases course (#21) Looks good to me --- .gitignore | 3 + courses/databases_sql/concepts.md | 98 +++++++++ courses/databases_sql/conclusion.md | 13 ++ .../images/innodb_architecture.png | Bin 0 -> 36687 bytes .../images/mysql_architecture.png | Bin 0 -> 24824 bytes courses/databases_sql/innodb.md | 26 +++ courses/databases_sql/intro.md | 21 ++ courses/databases_sql/lab.md | 207 ++++++++++++++++++ courses/databases_sql/mysql.md | 38 ++++ courses/databases_sql/operations.md | 64 ++++++ courses/index.md | 4 +- mkdocs.yml | 10 +- 12 files changed, 481 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 courses/databases_sql/concepts.md create mode 100644 courses/databases_sql/conclusion.md create mode 100644 courses/databases_sql/images/innodb_architecture.png create mode 100644 courses/databases_sql/images/mysql_architecture.png create mode 100644 courses/databases_sql/innodb.md create mode 100644 courses/databases_sql/intro.md create mode 100644 courses/databases_sql/lab.md create mode 100644 courses/databases_sql/mysql.md create mode 100644 courses/databases_sql/operations.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58874ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +.venv +site/ diff --git a/courses/databases_sql/concepts.md b/courses/databases_sql/concepts.md new file mode 100644 index 0000000..b11d651 --- /dev/null +++ b/courses/databases_sql/concepts.md @@ -0,0 +1,98 @@ +* Relational DBs are used for data storage. Even a file can be used to store data, but relational DBs are designed with specific goals: + * Efficiency + * Ease of access and management + * Organized + * Handle relations between data (represented as tables) +* Transaction: a unit of work that can comprise multiple statements, executed together +* ACID properties + + Set of properties that guarantee data integrity of DB transactions + + * Atomicity: Each transaction is atomic (succeeds or fails completely) + * Consistency: Transactions only result in valid state (which includes rules, constraints, triggers etc.) + * Isolation: Each transaction is executed independently of others safely within a concurrent system + * Durability: Completed transactions will not be lost due to any later failures + + Let’s take some examples to illustrate the above properties. + + * Account A has a balance of ₹200 & B has ₹400. Account A is transferring ₹100 to Account B. This transaction has a deduction from sender and an addition into the recipient’s balance. If the first operation passes successfully while the second fails, A’s balance would be ₹100 while B would be having ₹400 instead of ₹500. **Atomicity** in a DB ensures this partially failed transaction is rolled back. + * If the second operation above fails, it leaves the DB inconsistent (sum of balance of accounts before and after the operation is not the same). **Consistency** ensures that this does not happen. + * There are three operations, one to calculate interest for A’s account, another to add that to A’s account, then transfer ₹100 from B to A. Without **isolation** guarantees, concurrent execution of these 3 operations may lead to a different outcome every time. + * What happens if the system crashes before the transactions are written to disk? **Durability** ensures that the changes are applied correctly during recovery. +* Relational data + * Tables represent relations + * Columns (fields) represent attributes + * Rows are individual records + * Schema describes the structure of DB +* SQL + + A query language to interact with and manage data. + + [CRUD operations](https://stackify.com/what-are-crud-operations/) - create, read, update, delete queries + + Management operations - create DBs/tables/indexes etc, backup, import/export, users, access controls + + Exercise: Classify the below queries into the four types - DDL (definition), DML(manipulation), DCL(control) and TCL(transactions) and explain in detail. + + insert, create, drop, delete, update, commit, rollback, truncate, alter, grant, revoke + + You can practise these in the [lab section](../lab.md). + + + +* Constraints + + Rules for data that can be stored. Query fails if you violate any of these defined on a table. + + + Primary key: one or more columns that contain UNIQUE values, and cannot contain NULL values. A table can have only ONE primary key. An index on it is created by default. + + Foreign key: links two tables together. Its value(s) match a primary key in a different table \ + Not null: Does not allow null values \ + Unique: Value of column must be unique across all rows \ + Default: Provides a default value for a column if none is specified during insert + + Check: Allows only particular values (like Balance >= 0) + + + +* [Indexes](https://datageek.blog/en/2018/06/05/rdbms-basics-indexes-and-clustered-indexes/) + + Most indexes use B+ tree structure. + + Why use them: Speeds up queries (in large tables that fetch only a few rows, min/max queries, by eliminating rows from consideration etc) + + Types of indexes: unique, primary key, fulltext, secondary + + Write-heavy loads, mostly full table scans or accessing large number of rows etc. do not benefit from indexes + + + +* [Joins](https://www.sqlservertutorial.net/sql-server-basics/sql-server-joins/) + + Allows you to fetch related data from multiple tables, linking them together with some common field. Powerful but also resource-intensive and makes scaling databases difficult. This is the cause of many slow performing queries when run at scale, and the solution is almost always to find ways to reduce the joins. + + + +* [Access control](https://dev.mysql.com/doc/refman/8.0/en/access-control.html) + + DBs have privileged accounts for admin tasks, and regular accounts for clients. There are finegrained controls on what actions(DDL, DML etc. discussed earlier )are allowed for these accounts. + + DB first verifies the user credentials (authentication), and then examines whether this user is permitted to perform the request (authorization) by looking up these information in some internal tables. + + Other controls include activity auditing that allows examining the history of actions done by a user, and resource limits which define the number of queries, connections etc. allowed. + + +### Popular databases + +Commercial, closed source - Oracle, Microsoft SQL Server, IBM DB2 + +Open source with optional paid support - MySQL, MariaDB, PostgreSQL + +Individuals and small companies have always preferred open source DBs because of the huge cost associated with commercial software. + +In recent times, even large organizations have moved away from commercial software to open source alternatives because of the flexibility and cost savings associated with it. + +Lack of support is no longer a concern because of the paid support available from the developer and third parties. + +MySQL is the most widely used open source DB, and it is widely supported by hosting providers, making it easy for anyone to use. It is part of the popular Linux-Apache-MySQL-PHP ([LAMP](https://en.wikipedia.org/wiki/LAMP_(software_bundle))) stack that became popular in the 2000s. We have many more choices for a programming language, but the rest of that stack is still widely used. \ No newline at end of file diff --git a/courses/databases_sql/conclusion.md b/courses/databases_sql/conclusion.md new file mode 100644 index 0000000..d9626aa --- /dev/null +++ b/courses/databases_sql/conclusion.md @@ -0,0 +1,13 @@ +# Conclusion +We have covered basic concepts of SQL databases. We have also covered some of the tasks that an SRE may be responsible for - there is so much more to learn and do. We hope this course gives you a good start and inspires you to explore further. + + +### Further reading + +* More practice with online resources like [this one](https://www.w3resource.com/sql-exercises/index.php) +* [Normalization](https://beginnersbook.com/2015/05/normalization-in-dbms/) +* [Routines](https://dev.mysql.com/doc/refman/8.0/en/stored-routines.html), [triggers](https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html) +* [Views](https://www.essentialsql.com/what-is-a-relational-database-view/) +* [Transaction isolation levels](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html) +* [Sharding](https://www.digitalocean.com/community/tutorials/understanding-database-sharding) +* [Setting up HA](https://severalnines.com/database-blog/introduction-database-high-availability-mysql-mariadb), [monitoring](https://blog.serverdensity.com/how-to-monitor-mysql/), [backups](https://dev.mysql.com/doc/refman/8.0/en/backup-methods.html) \ No newline at end of file diff --git a/courses/databases_sql/images/innodb_architecture.png b/courses/databases_sql/images/innodb_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..9e2804af137acf88fdd131b4fe2992d978c50383 GIT binary patch literal 36687 zcmeFZbyU>f*FQQl(k&<{-64piARr~(T?0ykgrp2Lh=@pu(v9@cNY`M3bj&a`DAFY{ zAbsa!!RLF|`mOb>d++ag*1EqxbTRKZXYYM>oxNY@9igqML`*7;o6`DHJwAqxOh#S$Lh`{yo`wX&`T007|t0H99*fJ4kB=n??neFFek zu>t@jk^ulJ_w**6+n5`m`)W!GfV1$}9ysO~zPqxCCjda5_Rl|{ig#!{<|3|_iiRTY zH^R$=eB{){xTBcS04fTy`j5ufTdPXedYZD9&x#)tzQ?kw{0Oaj>n_zq?JpQ7`!;5+ z$o0l&=w^1OopH#}t7j^jH$tp5tZ-5UkSbJ<@kzKEL##OOhl)C^l<^cJTbocTNL_>0 z{8Op@Ot<}(@CSQdY=s==8J2ms}r&w3(_AqEc{3lH$fl<5wjeHa7;0WvA^$N=AK z?mz+1LTnHg;P!PgCcvy?2mlDsBK)8EXn;IaW(I7D5u-?j8{8c4d+fjghC|68TS+I_ zKMDs@Fh7nSO_)K0k;QFitN_4`g_Hhou;57}C_K2xXTHCAA^`v(dlt-QHr2SE0XkNi zN5G%VfDonW;I&&Ro3Z-?2lzds z-g`1hZjU8H0Goxy+HdN%)Q&d2scL|v<2Y+8x|9ZDL5&$?e;-kL&ZEC%xM%5xn0%w7 z-D&Cu-&a}PpXin?8D|P(c7c!THkT)D&!yEb+3ui$yKB5dL3g106xz_RhTJVb)LrI- zgA5Uewx>jo+B3=6Z|I0xn6afUauMEJT zQ?f$OKMqQSTb7JP1q>7HSUT=+|Udn zRXb-nG$(s~n@oDL*|lY3JljU@I^{_ee#U+8On(E4A*U0637AmoxAkR)>~cOwL4FXQ z!p%)9Ex!u7Bsl{j?k00&WEUd)^V*Gt6jzTvy0N(dx?)=J>I^Co=55*=p6%pK(bQfC z#7Kp&T|FE=n4 z$@Tbp;ne!Z4f|&6(AiWQnJv_GrvS{*irS2exl>DW(B0bPf^_NX9D_@&MuzG>2lQuK zi<^AVTHmQh6PeSS`!Z(@z7`5H$`70JjcDH5a-YDtH0iwceGw|=SVb#lhOn-e{UH5Q zGyc2eo?XLLM({Vq5A=z2zcvE=ugV6dqg%_Bd&2-orIpPt!_{{RFKjtj;N}ZV?Uuof zo`$FGJ)pcq{pKc#9H%%^Ha?wbJ=T{I!mpyoHHp{lr>Rt|u?s~Fh?}d1YQtR*ymk}I zDs!7`DD6# zycN4aM9N^R?ahr+#%iRZlTvNzFp9DcY(g+4tDA-85F~b_P_wWJnTL-YkK5`isZYf< zb!8CV$dOQ&87UmEqs;C!wdgN06^l4@&^O{goD=qnaA9cx1fR?^>eCZh5cvucYFnb7 zE9y5vUg+yuP~E+mvYqRCFX0eD8O;fO9xgi3Pi_1e5~JrMyf+cX@CUj5Gx zZjtb|k0ZXMO6->t42<>(8|mo`?>}p!nUfatScA4m(s!ytWzK%!`tP=2HIy5(W(@VU z2vL9Op`yR1XyA!_&Oqmx434O`xU`&cJcV@aL7M0JB#w%f%XDC81IS_nxO# zf`s=XHK0@5;U7;4Xu~uXbeY$mJrCf}a8~0wS%OSnPr7yM#5vk2=u4zt2crc@tPav^ zPz?^-a0tKeea}k+I>eAHO?$#HB{=C4|K3r1TZ-fxYdie(U{fo+PXoGs7~3a&snAo1 z--EpHEfk@^s+_#)g67WR9pY<$w-!Wh)!J0(sR2FoZPa(sk*gO&8hFv1xu-xu#|L|) zYQIJ3eqg9|KXV$Xjt2aE2c-q=i z(tbZu0lcraqYz2+Nxlfi#kIFl0`#5oQ25N5*XeBn0U*a>Qu~R=-E}GPiQ)Vb6;G$; z`NLgNyF!J$Rm#(q_xUW!-KQQv0#=s4Cs@Ld-$84<3OrV$@b37op?c}71 z1j3i!agYcY?(zr+^dbo?`0H2O5*$mtQPVXR7|E#12a6(3Sfr`lW5b7~eVHx+PY=6e zNLgh*CDpDW3fxdy5cA7ct%m_cLOX;?tqq@>Ius1@o*pYFRiLoMQ=ONZu^M0_E8ht` zW{q-dPjZ}RE!+0Jw8?CuJ+?&@=-i(i|v??WBHMCt=1B% zm|HETZj%1{f)@DNK}%aj9}mQA%B9Uh0E|UOi&si!I8@+4&cMFfcWwOrjA3-16OKc5 zU#nRLxhu*ED@E$_wK8bzYp+}4R~`?{4YJE=npa{UlLiM^KMKGDo}w8=4YIASqUk^n zT-IsAms0`j*RR?Z-#4g2`m7Fe2ini{%IK@Czj@%jTF|xdWX(6%(!jSi6dzUc0aqxd z)^wj+y3Zu!3;83`r#1(TPF_1J&jB(%k9V6C#fSPcPn*rrpb5S5b#IRL@gJp?a}G=1 zNnL|?RTrK7$*o-GZxabugW(UttUNCVNaP0x?|(17k<_lX*pUH$e*4E$fWdmsB6kOJ zAhZ@B@bvBI^RksAn1v`uQ@3?ZJx+-pL9{ZmuQpkaHmT}nH?b=uy~fm%&nP$XzRZNs z*(fOBiQA~#SC%`EA96lp@ObU-q9&xdn+oux#mQA-N$U?+u#6yySQbuMS`vmk(QcJhNYaKmi+br z!lwcL{Ox`19y?IrD;VC!OzlD4(-mUI8%@>5+Fsnho2!Bn2w7D6vVuGs4maZ~^Scrw zO+So$ucB87dtf@wnqzkd#@M4L)8mv|HKH|d;0#Q4PdOMvSupv%QHz^AQ&X!XpZ(y) zw5t%h7qXp-VpJbjnE>yeReE~O@n#+K3+8>b&?tV@aR zxumSoN}2640@rQ7)<+@Aeb!_U0zaI@m%AL87xEWHuhI@CRjslWzSU32P*mEzlD%it}2xu`+w)6@Z_FPQwb;lp_B$qj1v zFwJLy5_U9~fS{xoC$FjTh^22L=+n0J%b|b}wfm4OB)6H#m}YUX@LDiQo9i8DJKg`U zfBJvlIf*UuJ6q0AX1dc(jOQ*12V1^gL??wmehdJLF}3%$oZ-1tNd4H_Xv_Qc1TVbi z44iwHZXl4341_fa1=P#!JvyR45!+kUUS^sjkXl|EO*2K;_C=A|Vb z*=<(LAphsD|D_C$v3_7y)Z)?E=Td;*PCRI`q#Otim}~CWfMQnGXAX@ja0^d5uKt3L z-C%EVsionlxnCW+DYM5H#<)q>#?VZ~zf;#arz``#!Wza66@kUOj=Z|xnV@4PtfNt$JFjFNR7k{*9<=@#rjbm z1L>IgHp6pk(HF@4uUifKRNs*AV{c3cQ+3W)r!Mo)l8b}a%!PB^7i zl;w){2V=wZ724U4t&3EkD5j^R_xX4=y@|hrKwdAvMby&Is!GK%6B6PL$U<`N|6ux5 z{Z$_Y5Ma84yBzlXODJ{sb3F{_Pyl@%70>_Xmz_47M16RlB5~!sx>r?|QS(@Jmw*z= z0{TRZ7^eJDwVI_xhKzThgTQ}X1(VC!>}tI^1c0JQ&bji?LygomZ&PAXW0Pfv?#g}q zE*oV#ZwQsw>#WY0SyAOKd1#6?3WIl#ujPowv#$&5i=5~PAFfxG*-Vf6M_3yepo=W` z654gd25iHR)mo+QN+~+%Bqmoe9o);Tw`NUQmLV^-n5i;kzww3 zSyiXk$Z<=#Jg)@P=N|}eh(y;JvgJB`Tz39iFa*l0n@Jv&X=E#lmbm3VmCkdIlH>YG zkj7ETQ)^@s|6$>!a}Bxe^_VMvx!&Wb!@=CHevr~xQ{E1NLwu4}A))F;r<-{f*&`r? zu5bdrR{BW%FY(q2i|y~4nVL!kDuCA3wP}j$4bs^Ca??D#=x^(U#*Rl@FfU#Xi!dI4 zv~u5gGj-s|t3!L_J=Px1`Q+(fkiDr#%2lg30hlrRKBOIQy<=Zn&KCELSTba^_)(GlqL1`ys|^*As?CwQwEJ9%*a;m~S{&Gq*D zw9t)9{r1N$WZrd=NdUP)qJ;?v7|F0(RZLE zLxfk~;|eAw41=vsmhRpZ^*Si&Nph&y@boOxSO_9&oc3j zOvXg_FP}7g2m0W@#Vh^y`NRL(%pSU^ptiHOFZZ)tHM?YY2+w^a$;x;IimI;Vbo?y- znkf9-UTcFb{Ld~Qnw` z(f*%zC;R7D`-toQbvaW5>K4@%>mS7YH%P6FVRaTCH(VT==?-}LVb=u?WT7oXbMcnv zYV{c4m*2sCfq!h^wrZ^2g<%Om-x3%KFEDcztCLqs^C0Xz9OAt&f~0R>kS-Zh#jg1L z1s?woA^%4r|1CO_UJ4ZZ`Vi`Y`5e@@-KVg_O}Bx%uuD%#U5`ikOdbF!(Y zo|sU_fDlS)fw%wl=mg08)^|-{xtEyN`{j&s#}Qp><;xaQGhKAeO(Kl@PK6Ka3h(kc z?(OWwFIiEfV~^IO$6Q0YHuXUkE}rW1H$)F-pF(bot$VpH%o&7PgVXNQ^xvE)fp^4o zeS9DZf3)^PbdwNxm+YVDhhzn%)BgwrmTv&-@GYn~PF94*(x+kQ4ih)s7hWd;_bIJp z>DUjfM7tbf@26vnFTn-O(ty~bOMLmQ+~JQ;9h4+G)30zURLHm$=zS3`Fjmd*Z+)LW zCTd_(o5_eMmrS4O*bf7+KKl|X1I~<)X+7H~2I*V=D1{9rjPm5Pp~G}7Uej}I!_*aC zn?(<Uo!GU432Zxf8DMU&x+sg)I)t;Js;oaIobfv*=4uIe1Id1g5}ovkT1O!Cq&c zq{Ncf6?H@OilXBK;|Tr5w#zDGd?^N-wobfL+a-#1%YRuVn>_)=v*=~gd6JJDr@RQ= zQ;^?#y>D=&?8eiz!)wCZju2*7guQ>JYC%QGQUxki*Q*|pKKb^eE^9{;J2~~5TCM}c z4M8uj>Kow$R;|!GG+ud0%{Jdd*&T0gz0$!SDBLmi+iTlSIXXGq&j;$ zp}j@5!fsBATjCAldR8U(>@|gPX>1|JpSE5vDO=p%3S@4_`?*8e9>A}zBxeeoiP-i! zgH;Q7+J=n>nGa}gzh|H3Yp)WT@Vc9}z{-24=_$w1PBZi^)@b<~n`Xxc6WXL?)tRg! zK6|Jfu@sep*c9Zto##%)ZIRbv8GqM}y1VQBjb!KR?GP!60V^{_QxfEO>h}Ah!~+H^ z57n=T*%CWo-*>WE9<@+z+`~Mfu|W{xa5D<97$B;awC881k-1C!L_c89iWsXQjv9{0 z!ECW^6Fq73A=R!8AKw`ap#rIEhT@jcfvy!LWt;Jb5FY6W5mwf46J;@E9c`OoHT3D$ z^3d_#S+Dwpr%lz>AZ+jL?4DtwS5W3ezjr9{D17JWM;nFiH-)F)eQo)I_ap?96#u26?zvjFj+MG~yOzBo8N356`L9nT^96KR*cKZ&&@c zY)d*=AM3XBmwIe+rdltDQB|L&mi#z(q`2}#f6B<5aGp(=83>V)XTeWCuDs46N$bp4 z2HxHAu=)I?gT0bmWd2d7dSW@A_LcX>qgBDLX*+mKa6~i4+Ut|?bBs1MF>6g zE2h_k?%EL8a?`otRc2QW*i|yQs8(o2DZfK z-6dSj`3|Z7xAQ4k?1mui`t3I!j|R3Q`7Vq;FAvQiLoM3JsN?My_{RUv(&k)ud&}^JN^{_e-*sAL7Co_(9S3BZV;us=q|E$6I32!00G`^zd;o5wipeo%c( zH8-!;a^NQUkGX3DRrp{0mp^9x%oj;Jl8va;x=3S#W4nS?w5S-C>G7X^9bW8UZr3;p zy8a~RdVWUU^Tf|ZHsC=D;<`%YOtn)FgGEC3^PtM7uuO-F`9KrlJ?>{!#XQX$^Vy;T ztAlO)HhwMzx6eRPVn2Ceh+$SVP0$@zx!He1t=!P^wwhBS=PO~Q1$$3r*B`cadWM-p z{>sqM6M8zdnO#}U@UW7qLVNg6+u zN{lY1jK9~XkvgkHWL01lan2cBKD^oL@ujz+^mhLKGXQR}mU3Cz>WZ_b{p&m#uST_< z350Gaw^&75R}YoU@{^Fnb z+GSBs{Pvit#~}m2vRvzOoLz&vDd`)M6-F-wWlAB~1gYB3d{q1RHH1CsMNqWsN#UIZ z7!vApC_bdi_)F%R8OprLFNGs}9jlxB+Io!2cD!mO$WFw2Jz4En%4E$gDTbX7{_0C5 z=vt3QIHrkNEZqC`o}EdVgkYpIyCX724YW!*t4u{fVG%>*^R_&P%&xIo4LI~Pa1V`e z4(RIA`^Q}Sgm%no`CJkGt228(OV1D!Llxv@j~|-qp(YpBRZ~VU`;dsjfs6?^ zg(jjzL$~M1Hdqu-|A8&kUWpBS{Vrgn=Tgus!fjw&LMXxh~wZvmxIWhPe zfx;Tqvh=O0Z5I%0(P8m>vAj3U6KzK)k|y&98uUs;$0q6w40 zr3|-UP^6M7eKCUy{@mG3zZdLb|2}XPLceAPD%FySno|tlxcTD6r)x#bp$uZA7HJ(nA+&R)?Ws ztH2A2;_=Qq33VL^i^OqUmcclFPT5Zf0-jp2~R;(eDq3L4{ug>$pZ z%O{0)VMQdqyLntx6)UE`BTu@AHKD|n>KOFsH4PfCP}41I;MQg@pylRUyE-GQyyt}q@0lfcAHLY0z>oMML8_@bR zRC8setWA(rWf;`{>Ug@EWtHi+^IP6!Cz z+o~<*I7m#W*kx+B{$=f#1mnBA6rR--;%7TbYkp>u%w^_@fn?d^mW^5`0i%bNIUkb-Xe{0gyT@&wJ(bYpGuomOUz>+ zLH16q76jVytYujW9?nH*aSjRKo2bN^%~kvy!j}wXD!Jsa-FIp=6E_6j37ev;uk%(c z%R)Q)e)?G4`^uhUj%r+4?I-5{S!^*Fe5MbWI6MXj}w zX@q;RuCWj`Dct*k`we4ZOLvzuglx9zmq~_D>!!J>qFuyWA2pg#lp{q|6?JK_`*;>48(YWe!jE;k<}Gz0j-a&2`z6wjZrJ}xn6anIn?4w&m5LM3 z?h)iN+f=QffIXWG-gl2PzYz9Tj&xyO{4{6t>+K&l%yi{^{7`ZHN#W?~RlfhADhYIL z7kjlR-sWy$4Z<>~XO|vWkT7pvdKX$eRd0;1J>y;~vkJPWV+bDpYd54H8m=&%h*5Z3 zmq&>ZqhIO!aMMf;9u)&h&{mZGCe7s8tqY<_@7@6XYqdB`7yiBeQO=+T3W=*}o))BCEGk8L%F${mLkq zK8XNftCqvh-;~v=yU0($PZ0T*gxc#FB{ENKYUfpx4m*X}wu2x1=@?!6&DsR_Cnnd4B~4&Z(#jh@GKSuc`Y&EV%zvzs zUvR}dz~k1695gT`qdd&FyW^6(1pJSRxg}A1g>LMz(llNI8p;KD1AA&puNB||86)IbE~=ML5VwMYH0d#aeksq9h$`Lw zB7k#VM_v@hDRZ5!%~!@Dw_;IEeV<*sDOL;~vW}O#5{+0bE@UZgRxK@{{1!(VD|lW? zmMRof&8^n;VQYV&?a~r&=+oVMO8RPv;_=YUZ9*GF#N;6#iFhGKZ;q9s6K? zrAs;GzHjEOO>e1GY?e?r1*Ob^H-v)?^=)hA|9Xmkm#BEk!+{L?j2q5BQa?WKXo}Ze zF_@};wVpSg{KE43-owu7@yx&0;7OO;A7;_(02WrM@Ib7z#j)KoTQuq~aT;6Xurjx; zl{y!V#HZ0K_}5=dn+zh^+CP}n7P7V5Oo4hN<D?>^B7r#bsl~;Kk@OhugFFBn+1+Z`sn6;YZJWeD5>ui`2sAO3(1%ze^e0| z%ZJ7IyN`g8`KPF-GON z?JHbrztH6z1hL+RF{w)+ot8f$Mi` zyvP|2mJeQa+iCJV+8nqrDlez^`r^?9ul)41@A{c|z=g)kf34N^KL)Yz|A_3a!6!vI zsP_#qCHOj|1m~T%d#VBM7vLH1UMtU1p7-qLolMjd9WK2lek5>yvDV*$wfmY^&UgC@ zO?*tRP3CiOIpLRwozNxxMvC*Y&vGCH;sWTU4)k0WFaX-lKy(bwH#W|A%AguZeJ!d?!$qOi&$Apz7O;w+5 zg;MuvO%SLIfj93ksc6+Lo%UDo;061@hV%{aU<+y@> zt5jpzHgOPFH8eFKy_`6cEv-AZLa6ng7M1)hg;&%|M zc3knF5V$LhW8Phlds|v5XR{|!Q3fo!wSg=zA)UQ@#avH|lUVjmgOo0SE5z|CGm0#d zRzA_!&d`#NGxd18t1wd606gQyY$V3r5t}6%{t)?FRj}5?nYfViwkHXw#%&)q$hp4S zMeRBynqH3rAv6@RUG!*f`P5-2@1AyVc3xQFOTfONRRkSpWAE2D*CplLcobUK_+08M zDVe57d%7yFJ&JEyxOA?N1kCM?xuUnIW*dlt1U%f_Dco%*~P&VPt z@3_w9B1ACopFh9`13aMUf0&scEJ50eS1mi)YpA6Kf-L-ZB58!2o&yXzr2j z&gsQq`2(wSg)^8F{Ho`K%ljXUvlrqjKu($lC+b03kJkv#tra_kthS`9dSwAcd?KXZ zG(Upcm9>`6lNssX8`tQl<5_6sJfU`n9T|Koe<||x?Hca+tBO*ryQ4_NG+pl{=DwYy z2N$G03F=W0)eJrUBy93;PD_fgm2s{(dZ2{dWn%_LKR#{?CF}vE`ZMJ0n4EwiEL*-4pHIQB<_+O7; zu;$aP-8O%|O#uM`xCPa_ISlk+pW%?{KJd3gzyljvI~v}lXtYo*((Ij?|Dg>bz1H1N zHKM`ijeWc=v@84JFUqN@6q4UuTBORYFf>G$aoFs%dRs6uVl%MEXg`qs(oL63ryOVW zuVH?F$F?xkWs43PWViF1G`Bh29N}Ya+ZVK)+DFAwRrgFVu03KEgCGBxKfFT!7l7k# zA|fXct46c}Gde@H4H};85Bq;No~{KpZeQ{RZ2s{kc;<$yNM^#gCEu4{wssk$5-lro zuKT7L)tqho0ry}YDK1JxWsohvP4?X(v5ejQ`}IaoTUv9^<|O~ep_YP=@LG9x}{E4c$YZ>>7Wgdh#bYV#sP#PC^aE0BYjx%xC*L0^)aY%KxCc zu=shAeI;4~X~W_U7+mcI(R)Z8n+&p<22LIOZf>cNoliDkD`F$ZR{gS=tat-djF@Q* z5;gwePmXo%k3KL3m>7Hw*h?Df!_gy(&8L!vGhO?w{lM^Q+9sGq{oY~IdJ{&Fa*OAL z{(lSB{FQ*5jb?3X7If7TwZ&Zk#6haLw*oZ^`x}Wi`Y!kfsfpLhW(~oz?9;aHZWt`` zAJAwQvN}0jK5eeAj$-lVkGp^GFxev2Gz`3t^6I@U51&4?pbjLQG11TPqzPbU{A2wu zs#iWl1t^4I0G?U9Q@$2>_n15sLB`v%3Z0hvQi-0#Dj6M_yZNuOjDS;6CX?8rPXWWv=V_w^juq#QCp?9DY+s(Gkx!*4RgxJnZ{p2}5S&R_rgIbM+ajhXd*te7k*=KI39h z%MvkSgc0w^(rr33N%!qGk9+Ea`tMd)4V4pu^3aJoNmXg4pZ#@r&SxAHua_jyv6R`a zz`a$;#{}K*p zOe^n*bkMgP~ctx(wCY*JS2)mkD}y#>;q>C8+}Y56U%ReH&`M)IqjLZBFGOaS1y- z_lbYRE(s*j&5ZRH^YK!!BnOpla)Ebs@e;|qdCZhNnJt5GZ?u! z3KCn0@q?~O94 zfd75wnTP5V3+lrBd>h6sQyN$E_vyp|NMoh~!)42F_@#~BX7_qU_9dQG6>~w24(th6O_8;jB z0Z*edaYwyEy?iLecZdQCE8N&cNF*iAD#4kb)&(r#8CvXMHf>{`#-MZ1+;?%x!st?IdIm>tHo~v-Y^(+mI0M#?8O`ag z&u(^?6&yM6D63aL9SZ?hux&jdwKaem_>cD@x@+i?WYq*fc- z1#C2{_6I5!HdXA{1vPg?LK+gM`46PJ{%U|R{&$(kvoA7ztBfSw?C$qRI&;9pJCHUk z%pSj}Jfd62H`~jO1WoNPD*vUi)M>a_ahJHYnirmu(C^6N5Qkpy+1ytCZk+6rDiuQm znykYZ_*)g|oXpj;@r^XD9Pfp3hwN@}GmnCdC1`asLYUAY3u|(dljkR2H0C^&(8|F25B| z1-epyIx`li2HT2Io+wtS-7T_yR66{mjd{f^PIT_Ys+(LnoX+PaR&BT=DyJ$x4!swAWq#y4t=BeJ&QXmGn~+@_7Xoj6yRWu%NQiZZ~M{&^?Z=Y z+NWPy%;FD<-|`S5d65{YjDv<3P?+4c&RN`idN@f?d1bHbIqhY{#~)&6i@Df=0>Am0 zT*`R4uuzfsWgxoy8K)_~BLgLppB{5Ur|y@-v|?+QJq?FTTCk|Bj2L9Ta*^M$j$XOR z#fxjoFNd#9f-Ob!y1vxmv{bhrUw+PR@u_0*?mpuw68>K5&fNoX3MHKF#Ivl K?tkCNG0MdjfXQY7x;6xo?nMRchjViGZ7>Q}(SyiJt&&-s?`V8hV#tPRl}OET;!!qS>+ z=-c&+p!6&(?!7-V{R)m)TqWf^HqsS#B8ci0@71>4Gs?!GJFc#46PPNU>VkJLZ)@*G zi}@8SKUM#g+^=CUCsY0|>#sOY-Aw?RO#HPJ9c0<@MvI83Um(fz>n+#~v|F4qCV-QX zT=Z{vrtEq0@LSNoU1S)*w_kwsA8?PmSy250Oj!QF(Oz}E7q3c%l)R*e;Ib-I{uOG@ zX!#cOACE?gcjFq_?8tF7nbjM<6dS6T8+x4hIazGVFo7XYz|R%L42;R0VNdTIJX3=T z^FmOo?5~y%4w^Q3Pp-6Pt87nv^`P1P4Q2afc^S42&-FWD^zf?ws~eVWd&^kHCc7-; zE(P{2DBf%%h~SIjf=1aVDHi;V$#&v@z(_yowRc$Hc6xPsno$k_f@ged+Yg*lt=>2& z)I)ftgSfOdA4n&L$KKNY%U@v*a=rQC8tg-l)!q%VS#+R)8Fbi~YNzY6sxQm+aqj~a zp;rt19oydpl~n&m#AiM!$U3bBnzrTw!RWU+x!xH`F`uhS%;q}V=#Z9sQY9LA-w)f8 z_5nxn8+)JiBVD;(HO^UIwmR^Gkcf9Bar|A^53sj`X^F~(oHx~$CW`oncdyc^Stx2B zGd**4Tesp}mrd2Yb@95UC8l;ROv?83eGZ_+ugVVXecd~j{Y1xPIR#Z5BvY8!ebX@4 z4QZy!3Bs)DzAiMQH34Qe=%ayuOBNc3Nq>pLF9CfPPNxvciubV|nKrdm?-wt52OZAt zYCHQu2th4Xn@%TUZ+Jc!cUR|Qvn^GWzl6|7%&VjptVnD11ClM)ECyFWs_+m8j@sKj*ge;w}nog3jfGJLA8hz2Byjetr=c>TCKy@_%ymXB#kk{oT21Q_2J7?R^}wq zWn<2!4UWNw`_O-o!W>(jw!jSmRJ?(F-H{~BbLqomsd8KTptTXLfpq3Eyzh&V;RUb! z5m&!=YoTQCU$HV$HqoC*JWg?hUFGe~K<*rcf4IesL5EP1&3c1BL*wd!!LQCHsSE-@ zd7me=<^tTTI%)%>%`3KQVeMpA4H!<%4$yO9Yg>CtH7#-PErX6d!1`PGHz*v3wJf+7 zM1so)EY;un&`p8jeR2e904Cp9LBPc7=*{VV-{^*l_zr0SCfY!>g z%_)S~rDDCM$?Z&$t6fzFsmets;&ZBH!0dTQU(f+Ilgfz5Oz`E~wQ0@E)a;bjL)T=| z8Yo6?uxPNhOB-deAIcl_A3c~J!>d%&qMFvYS&_-1sK`Bj%a~`JvqGb~=C*M`tlNOm zxW^9`H~;wML;IX2<5we>wYcsWSP$9hisTKHlyriP+F}-7Wz&3Ic|u7r2uFryy4=sQ zKA@DDnE|sdtWWu!b?o;m85vj-dwcNTG03SNmK_}z8Pck1+U@L)vbz}}?^uI4bAA~pNgGQ4NAb$+O9)1q9UimClNeo*Ts%N>a%$i0Lv^|wWf zRCH^*kqK=w@>v5~JKzq`-eURApxTKd>;p>d{p8FgeI@4AF}D21%cDi9qRFAdo5^Du zJgru!(dB4bfzZAGq$!}%@8U0aTL&4-v`L3w_?Y3uSN ziN2y;8ut3f)P#vJ_jz1=(B#Wm)KMRZFHt71^+fji)-a~dr@#L>+&h)|4jA(-s41z8 z9O+L$lG?WX#DT%3aWN=iIW7sMJe@Fzu$)}ZqEV(;w zfbhBxF*CKQr+4AAKLUJ-c zF{mY^Df*v7V4+F|czd|_wbPQ&4wh69bXIu(K@OJj_28a_&ji}0yHVDk!#9brIwOy; zM^U>{BtfL^(s^Bc(#llu6`5UKUtP8^6Xffm?wL&2mt)T4B~R!;>A~>%8sQ%`g6p)j zh3v!XyuxmU^Po=tIPqAWwVstZwJi%8$A(-0?yH8jQQef6#F} zh&S`La6wHPU$s2gGI>Rh%~ecSEhxsJ^2DHU(Mz<^rD)i{beFEHYh8RPgvMsR#!;O@ z(PcO$qs>G$$;EMabs{kr%&uv2WB6rRc$J32QS_W=Gd%@xVZqxu*xqP9mjDv5`$^#Y z`xDRu0$ToS#|*QTT;fQ0OFq|AgTnHr>4Xxs*&V(B=5~bFaDfT19-j3th^=wH=S&8Q4iDP8VDquoj)*&sw*U|aprlog$UV)0U ztDR=y#8C>wnT$#!MQUej)JGjlS?rK4I`j2o=OQ4O>adV)7+oQ;iTf1bcR3B9Vt824 zLi6aRj);)d+k6_vqO=ELu0+K!rG50W+_yeVmx}HcBz_io??#5JU;-j_2qU?B2B@8T zv>uSMilp#&x0$kIa%z(XRv^0huTMX>^=$5Uc~M$r*zaw8o1_^|Gc3uYN}<1*E5uCx zhDg~y9F*dk6EkvI5VczpX-=!DvNR|a#_&}tX!XaWYli}~slw+NmCF5&a?cAZIoTM=oviR&-+Gcr<=-PU|Ll!%fztWu7K>;7Z6q{N8Oc zQ_>RAOF3bsQB*uqWD?GSfXbt?PPNbU*TvC$ zvY*Rcd$1p$_=~5>l7YS{27s=uPVR!o%^u7hCopv%%mS!}%g59oKpzmBE&sI9f`J2n>e4XMiL zPHe7^w*8OYFon;d{w%gjy^8Xoj1<@3&a1rmB;6<2OX|18JLMW=lb5m(u1LG{&9Jb( z(BwhE^zE_NoHe|O8!t}&F(5LgQT1=22d12!BEg~MPK;CD&14H9%YivrSQ|~5(<^jB zL$(aHw}ov7N3$Z@`g(=nUZTyg%AFOW+Ms%rnj~y}(LSi7u8Y^~gS_51^!KuV+!q#} z=uY%<Kt@}uRSd^9uWx$etBk&`;e4oz)uVB_dVwJ zk``W1h3Qk)pppmTa6-)2Junlc&RBugCWZlsD(5xNFNRPHG4%{TwSL;%!m#C@D}rE6 zj(q}8fuEmj`BmH`X1lWkjmn&hGqlJsGEZ3uGkF!{lM_hW%(G}~%GB=mo${W^-)(A_l>?rkq>Uyo zw4EzW3}q|b(g|O15Ht3sBL1lq1TcVidZ%3!UY0QWT$W(8xALc8^ zPM$zl?Gj0mF@*;ZHMT`Emu!uv(h2M6f+x~ix)t!A_LOF}raDZGL}_ZhTi{@87eDzx~2Tkv+Ms?dtV(E)%Wf@Gju7Uf|81abcfW4l!SzIqcljD^dJfb zh|(R>HAurCp&}^VJp)L044r3>h+lu_x%YYQKlh&dob&rj+_ToZ_S$>xwchx=pBKC8 z=9ya>kfggF`8;}PcV&9?u@N@f0G6GZjuDY+{(Ou5{^IUp%E{qH2)}wB#jD|YAJ&56 zAFc_fo06v?bdXiBUkPJIs zBn1Qz6>Pwj&eS-Lx?hNv-oJ##bfFHRQ1DCt}yGd~p---~Wp3d{%>H zCZ@$JOVyQp5d)32NHaVX0<{I3aw_U&G0Jbk-+QHX=cyA$t_+y&&+^EHm6A^=!0sI_ zB|z9a^kC@J7*EHxJRIRvoIQMu=ZZIhbQ{-_2=5+d_@eeL0HF~AkD>s(zxJY?b#=Pv z=tqV57=JlCn=hMG+j>d7jl+4nskT5>5hVN4-$M~L6=gIr#wi5^etPY`L5)txA!F~( zJ4`GlfH2RQS^2@H#*s>86A<1~nyUuk_5Sc@X|SQCh8}M~-}qY?Ny5UWe@BX{F0Ec= za+P@PImG)wb;CmlTX?!Df}*yeyc+4T&v4%+g(L2Sq4CrL^$bM!<5z?xES;bWu2Qe> zH5yi(TbGPDU|SqzyW1Zc{#mvBtL35p_P+T3({}+`O!}n5iXLv>0AK^8ehve<~*~ z`Z(sLN}$hnb#F;Y=}^zRXsXiBU6xrnPSc&WL4mXCM+0q|SIUPc;q8zhURa*wTQAJ0YFcvK z)w`b7Vy<>v$yjTFTnF~Yv+x&ol{YaW&DagdGffP6i*tKrd8nVF#@`p8-Jaqpfc*Ux zCjf(2X#%sXxP`|0a188mhPe13N>W1biq&Xu7pZ!$nzfGA;RVab2sW}vM2hQ%>39-j z2@2_lGCUO1(Z%^&O1iGBwNfZTtiP3?k-vtgmT#5-xjyLe>3LUWv$J?XW!hYwg7>Z> z*Ajo{zL}uiHaZ}(Y0M>0P(Y~pWY9B9)kUtbOw@6Es{0Sv3P`ObMHpmyT-tSSZk3U6#IanJRp2j_V*F-vzcTrd zTL{;Hz%l1x`OIm0qm~AZHDA6na#4d$;n7@EvdH0`7ax*)mwy5Sxe5BC2UdY}yx%Te z;QVX;&S_sI?}Dd1a?)lW??A+g<>p@EUt$Igjrzz3;g6ujt~9_+XnyQ1embcD%Z?N| z4q=beR-u*^?aO{+b?j+LTUhA&3EdeOrP=W7`~+eFq!TC}PAb8kvNG2AHph9y%(mI{ z%;&lz3q;m09qy^XW+L{}B{Fj@{WP7a90)&_D9SD{e{1Bmo_9SMGuX7S5E*tA!Glbn z&WuAaAt}-cL!PEENMCYk*4V4==U+ED98ad4!$<}Fw$3~oq`;2(zKw{yFpx75AJcAL z?_|7ce8PbV6%v6_*QMf^Y@4lf)WxsQ>%lBg9&tlytZzWpb`c6iOAKvK$@zp(&T*_; z`gRH=lH!a!IlI12XEj!y9@Kxl?+ht zD{wY-z(;z~>*Q9x#1=#)3&)zaG(sCYC!#!T`p0dY*n6;xSYUgAd}gok+SkyPV3nSS zcJ)7ZET;NngevzfwnzaG$>y<+e6Fj%h$~AolS)a`Cv`=)73GWELR&8 zZetE_{2kl2wxM}a>>51bo_>E;EyM9=@U<7dT5)=X7KZbSU;k4N)x`+Sh!OLyd!$OR z&{bAmUm@JynO>`5fvtFi&vcZ;<-eY#*yC8;jNZb_(m0*Xm5+y#o$QWx_!pZm9Ha$j zw5QUDLdYOvpkJR#YVWnJcaAmUaTEJ{%EWg3@q#-?0~s_Ny78Ar5$bZCaloWOz&QqF zlj{Ij4JtgHd0MpQsK<7Q33Pl*Q5x`Pga7=DO^uHv=Rf_5xNLU(uFRUof^-n;-AJNuAL zK3)8l2oT;^4^yJvD6V>OY{S!mOFds-b6N7{P+4^x` zb!%vvQ;AC%Mk`|FXt&cGmK45jQF{4M^XpJ_|JaW8Yl~RGq4dIVT-%PnOfk1JGOXTS$RSRoAbVknuUL$lT{>O+b?=lkWo=R1 z1vh@8@7>b8W$%;M`-qvWwR{PkV)`W(j`z^UYeO-Iz-AutT&8)p$1`k*#KY=J z3`SIoay(zM+t%=QyI6IH^preV{&HjgTaT;#=45)oj{Mrp%uI6%%HO}3YnSVke6@YF zhBa?|_sO-O@x^u2ZkvM`guQVyQz8_PFWab(BjYa4r&!cVXVv&Tf4gA^hEwB%Q4>+b-^)n$5{^8K1url+=uIz(89OgS zG*)Udx@f3fao7o3dRVE@wPK=-ksTi8dR>*pz4#h7gG%+5?N)j*`!ifhKBkz^NJtKOk1gD*obNUBX1D8h z#y7BYY6Rx$+OhCe>D+kX`o6PFDXXhlCrI@Zc-z|#Yv5Un$;o^Ebp=}qFIT7X>N(Mq zY_i${7qdMcK@A}O9lvptW_3qOL~fI_(y234DPQ}e-)!T(($6$Nx0FF z?!pb$niaJR`t;zj%yWpwY<_U=U`%xjkRvQIRj$FXt-54Syj?<>*Z3AojNoIIQ(!3I zrg)h_lg}V;MG`6li{ZnqY7L&dCW=q`)g(D6kbwIt4wAXGJ!*2HNtZkwG?xwCY7j3S zDZEb6o4_pE(yK^$*_Y{VsaImo>{U1GZ)30haHkh5V0*Ksj)|>{jk4g!H?|(3D;VpY z@IQ)|@3oaadOwb}yl4>b#O-RU*wP{~!mCkT*17X`(cGxL6Ouqe1tc)%?#{bK%(dU8 z;#V&fJJN?o4>~4>t?(>MiA{;2TWt_A@H@yK%Og$^ppDyO@6V*GzILsflZ$!XP1MEs z)#jCx#eNl;REIjM*OR~V&XQ%?3VXnd6P(nmQqsjTu-%y$WcAqlTXS}be(9tW++3li zbeq8-2fpcXf5Jv>34rG$n_XKwPU6Ji2;3AG2H<$& zeP0=F-8XF3#!ea^P(HeG^ZY&nLBaO@IcfG^hUT!Q?^}VHB7CJqSn$Ln0rDp6W>`nW z%(816km7eI9F#->*#%Atwub7cLGqS>+|`{T;?OwK#~-wAV($sdbpSEEd%tjzI$-#$ zP=WPM(c^7?W6QY;d0@1$n&>9AbD%UNDr#Xr^C~E-SO9%bwOP*2;jjW(rUVXBe#h~j z8fvnI+f!Qc+>Y-L5IGE%-nUnJ&cE8bl+F-J%dnycYa9GJ^aW{aeFm?EKOQrf(S{za zZxG&B3 z6!`A6^eO0JBR^@a<*|KpCsOX>JEx=6u!jT8n z!6CAKUHnf=LHe1^kJzoB8l+n)ENoRx$T9>bOB}ps0j0C&*Y=3EtTUcrtl^J=DhGRXoX-#1eOXWS7eU0iTy4|Z67;D!P;UH zXMnby*dwi=hg6H6=xHn>n6!bGcP5bZMuov|V`TUFtLm>U@B5QYa2F9c1YOW)t-by6 z{+srd`ll^6zY@bSnCwo?5H<)uI)JpVhbnF!jiTgFId*J_Gim+0TTjJh)K@gg9QHi6Y{6N7rD!OWuuUhK2~d?!#hkS z?Eg9kJa8T^U8oBjuq?K0-eU=Dvx;w-F9HMxa^GFOAHcAp`;9Q!+87*sa{e_s?EAh{ z*5=yJ`XJO~GmX5N(Wo4wmaIND83B5h_^iG+FM>LQ*REkWc`^A>z-o{nR5k4D8B@(4 zHaU=nGTyVRm-M&NYT;W7X5J2(cZ&KK@wW;O_(w8RmI+mSk;IGQh}H7+#2SR*z%!e? z%)#`cB^ue!(&~Rb*S|P@R}Han{-yx_i4ssfM=_0rqXGs`LE}1qn&fjV&^$f9xE}gf zqezq*rjg!1jUFt}xB!jX|7sMCasnFd76m}gIoF!D-Xc!$_L?YfpR-okDX+nn{9$W_ z|1?r-p#=c>XAH&p>5=02fACatEZ(FS&T$KGAMOB9`QPMMG?~ki0BZD%6--27IMDG7 zlMT?%M3&%9p5w0;Z<}BIa*lK8|34Y1%7f$uN8?k!&%ipyaJhL8st(-?nWLb_(C6Xl z>i=97xKcqnwT}b5^|9}g1JPC9|8%HyeqPR4R~+<({DWBiL>FMoz4Etv&?9{SWNkJ#`ZSlxi!Xdc>51?g1N7Tu|1FM4T zoI2J%xrhMNGYK&mN$sEV<6FvTHfVmu2$mWbj(DLCMwl+?z!K8FNJ6hX7jx)|b^^p- zlOd-2B!i*m9zpF11Gg7K|C?Rg&Y_BK95^H~%kCHZ_9)eAv&WV5RSx93?gSlf(BPbZ z5W@ZCwUd56a^QbH?SG5-FMSrD`pdHx;dM}r{_H~%O;J}(z6S?T)Fv*F2KAeO{i0jp z`-jKcua`b7J>PpWr-iSkc`q~d^m182gWLIw-c%iAEv&-BiG5u*PpNwk=5w|a0ZXC= z^uj8}?8e>X^UZEX8-{E?guMYB_6Pey>&ZSChZjSZ5j;w|0ZV_{sKXw1k<`9XDFm?{ zC|%LBGpCo<6Hs{S6;|pE3-n`KSbgao>-CN1d|8PRa$k~%Piv<5+hFV5&W*|?jT@F= zA$!jQR=<<-`aI!A+%zi_|alULt^tu^2ojT0M-JM9?vI;I3N zcs}!%bdbwIx{#M>8W5$r@b(#}cOh}FEkr}Ri{|}H(Nzd-rjgU1XT0{+1Wu^>y2n0# z@$}NSt6=xXX$KBRae%Dw} zjT3>vkfKSr5!-WGVwWW{VT0dKHg=)T{thvcMrcTC=GNFgkeI)}k-a-UKJEczp%FMs zgy<4|G+I5JDWwmlO1g2N{Id_mM@|XZ!or@Xm(?!3=Kf}TKTtb<4eGM3E8)(R19}yO zRnKx)jaWUwh#XEn5sN>pB6YJdmG0Ee>MMq`HNmfFrwjC4#vx@K?KU^_$9=v@Gzw0t->YHl_ z9G$K}kZ3vRO;Nuk3{hBu1cEs}T;)i*Dx=C#PLJ{pU6(dzLfL^(f^WA#*Ix8eRB^YS0L zEsOovtpl7 znGk89c#Ahk-j|UjPLsjNd`6^}=uq5`9f|N>il3R+Ry=MSyR(#1|7+DJebd2?jU(67 zUT7UijQ2>g{>&06z|dszp+ZI_f32&Pg;BpB-T$PT+@w~_%b0zt*IBgTNeLvfwF>F( zr%}%Y-pP6(Y-EcN16;LG;@R*bJs3Y(Y2BRs#c^{SOn&1VCj_3AncS9PHXZf+a)#r< zO?9`=2wJNAI$i#xM4KFrnnG-0p>b#QlK_-iKl%~fo!FGIyji(z???2Em9mqxMzAoF z=NE8yr$hn6r1Tman3B$I*m;H@s%|o?mo3D{*YrU-#;&BF56ukj5bO^_X3M72;S6yG z%&mMgXVAh-KbYo@syr2lCGFU{iN>)XY%yiZ z5Q9z$4}F+y(kt@RlP5Z}CX`gghzg5`S|`0zoY#IGvA`1I#JN4|&xs=({~30VcxS+* zsFEcVp%+(#qr>5iA~&%h7E)bPus)Edr190E=V4dnSx?IM4^S#j(oJ{64XrqBEkGUFD)zlu8Wvg83V}(O1f{0cv;xN8Q_iNdEaGj zTa+?s_1%}IkS^={Hnz)%g+=ye8R5!@0T*!i!fhElIlT``)2x%T>|{m1rSoO~Ob1wq zZ5Q4re>(h`X7XG1l$gIYRC}j*g-apIwV#XfM_Ytb?cpMoXgxJz zc8enmIa@T%LY9pY*ktNy0PDA;TXXlBP{wTHPafqsiywZ`R__JC;i7}(a9he_F7da$ zn#iGr+_nRFW(_=#ArwvN)n#hmFJx}*r@FPXeKeIxJUA%R&pDca6s4TdI5u9SOwAAi zpeZZrw-&#mq&+l#pH6~mfJZA{+Q-Qh+Eg-94qwp6x%XC4?qTMd)O?LQ{R*eoYA9%% zGtVcxe~0pBTVmC>vZ`=*A%_#98eRhj+*`juGF~v(dNd{wN{%> zgrEg|E~Px*j1D-9z8#OX_5)jP=W#knRBxg3hpWE^BUx>&WUI>YVl4bX1|}7gD}Dmi zlc=R?lFlv7ClmFsvB?%jKmDahhJ^M-wNYKzpK$f#+AZ8tX}r&24v+_P)U>GG>;Wug z727bcw}%P^qeCs0f%Y0LeBchOm}QM_udc&X4+=!-_@lqngmO#6()bVdnYysPMBBDl z`S`nYEsP%}q2@YCp`6ARxulgi!qA0?xPatubIo{Te~!y!w-smQZ!N?sTq;7daD2xc zLYB;{6JFsmhun{M<3Rn46_Myw^wHIh7D9vbBRXGvX~f2?#p`;yrs5hhY}R!n#Nh%3OQVMa^KcnY%7e{Hw4BI$zFP&M&uJ ziGd`JG>YBtQ6V+q{?oS-zRhnm#y&ncbV_O(mGEuZn%7R3-&8JaEgsM3@Sc_zN4EE1w7U`iV+L2n?Z(8!j04v6TxCmB&C(5khYpRNo$&$TJMuw|FKnn6!mEt@ z5Es7uV@y|~6~-Bw>|S~AJs20-{o69HL@zZH^$%1+Kg@(0tZz=mpg%lz^V?4ra+t%q z3lm+3*UJ~9zka1*HkymsJ+s5B@G#_5`W5KK^xDtM7#shW!M-~Q5L4MUeyTmJ9`9H^ z;uUwO!`Sfwa{rZHn4zTcl(n?M)2a(h(%YD-e83GqR`3CVA^z(k^*UDwfC_!L3Ob4m z$s-s?U{+W_1uSfoiJMVJ9HcR zG_7_J1MqRvShl*S|=_?3jBxzXbZB{gW{>}~=&My1&QC-MY@L?b5hf1;?n~C)0wn;n_}twcbrFbXbApq50zlP& zt|@{q82WFl4pY4W)%1@d9pgHVhJjKQOHKQ|kG4mA{~lc#_JH;I{gEFitjG^+mv5F> z3^(FCw&6TlC5wJmkrVA5)Zo=e%qNo!@1gKe>!85ZlyK#g~SIeB0*Z7x3cy_u+Nr46P zLZe?kzLc87yov0&RR9z~S&?N#Jfe(TkwrG;Yb_`Z`0hYokNCNwS@nQTLaU$|mP#R~M%3&)Cpr1j#-i)2Vr~cSN>F|B$hZfb7~o z6Yg?i_n%BK|Dwa6i zK8f+qbtS$uPG}6`#tBB$m64px%9? zczr0Txgnt_FOwCj;rK3$^Kl~XvE_DlVNDTEPPuy+;GdXd3M(OuM4jy0Z4r#Sk|om8 zRRMg`^tdFMj8+By8^b7L!IP$%dNop$`J){}O_I?FC%_H_+zxN9WfSx&#;Z*W8WVmD z1R*n;4Y|)$Q(zRJAWd)*D~oh|?~Ef?Yw>*^PWAuU7KRa5|WWSGIt5V zuj#G8N3DG@*{cD!6ue-pWz^$+N<&}ow~1Q;0wZ2Jk(o%9;`I7c^*?F-t~M)~FS!`k ztpZV!9(W+7&>6JoDJaKh>F9P)GzK|t%W#Gy%g%>QhFS2FZ)QM#Px~RWj3Zd{kId1p`KX!Fpy!RPP=dJ+S5G^Yuq?lwVIIsxN|r1E^?@rVr)!3KG6Nu`Fg+zE9~b zkTM1U#77@qY5j85%RXXqcBnC%^v0cauKG_i|4cHa$*g*-JtIwbEK}d&4QM7;RBDBB zGcQkqu#1>iu@k$BIQER2|Wp8t(9=4dnfE^ppCq?aFwwhb{HnKfd zpPdC{TrZg};6!^VSF;qa)O7m{#D)zd-3T?*R8w#hDA;9zqOb~{j3Bik21SE9h1smM z+us~q%&FU42-fLN_Wa|BaL=5)P4o?)#i)xoJ>)eCzq@jMq~0nSZC8M~mWw^?yf4{m z*ISXRE2iY}$g27(zp4e0k~T*K9=Tehh_G4RevM2rDz3RQoD+8I`dUkHSjlEMO3x2^ z0wgnXV__B=C7#vlbhn+j?#zg1(tVABp2Jh>)f(Sc+E}`M_U!Hc&jpi zEX^}`lnSC0eHg;O`;q;!sWBzgu#0r@wUMQ{AydA*XkbiK%$C-J`jnH+C1wW`BFwj$ z&n(I7D=E4N4=&?R>CQ1bzT5027^~pXCAg^hAnfV%E#2NvcKROT;-fdi-4Qn%)KyAD zEmy_fR?~*2r(j>-I+T|{#ayH_pJA-k?Cmu5JDw(a0{Fr%nJ$xak+>szi*1#d;qKbw zu?dcrmp{0#ENA+83WR=jjVE=p8a-ieu;J9a`ki;^X*S%*D{3ft5{?j&pX|n|;y)If z{k>quc@VX;#B~vAEkB+H()d;BOVENkAvNEBtxM4$2e9%BgbnS;^ekc7#n~yaaFX%) z3*MsnS_9gC7Wz}6d9A`n36UYmZtq$aJmX27&eK;Is_U>ClRpaKzhis< zAH_L^%Rs+Lv8G>yA&4$;HVnqZREqbao!Zi1AkWG4Rfq+|#~+C9)EcAw@~2U|iuR{R z&Oe8-t6U66skq!IxGzt1#u)nV^l3J>b{>b1LhBQs3W8S+26vhikvMJG_g7A0DwF(T z{MBik*O>6R4TuHZ>>R}U4I6-3(|(`2;`|Ry`5ZLr0p5bg8n92EBH&eRpszj>XAE1o zc1f?A0xC8p69D@94LEmqn≷%h%sYzx-FI({bb(oC8y`T&QVV{S3k} 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 literal 0 HcmV?d00001 diff --git a/courses/databases_sql/innodb.md b/courses/databases_sql/innodb.md new file mode 100644 index 0000000..a42ae9a --- /dev/null +++ b/courses/databases_sql/innodb.md @@ -0,0 +1,26 @@ +### Why should you use this? + +General purpose, row level locking, ACID support, transactions, crash recovery and multi-version concurrency control etc. + + +### Architecture + +![alt_text](images/innodb_architecture.png "InnoDB components") + + +### Key components: + +* Memory: + * Buffer pool: LRU cache of frequently used data(table and index) to be processed directly from memory, which speeds up processing. Important for tuning performance. + * Change buffer: Caches changes to secondary index pages when those pages are not in the buffer pool and merges it when they are fetched. Merging may take a long time and impact live queries. It also takes up part of the buffer pool. Avoids the extra I/O to read secondary indexes in. + * Adaptive hash index: Supplements InnoDB’s B-Tree indexes with fast hash lookup tables like a cache. Slight performance penalty for misses, also adds maintenance overhead of updating it. Hash collisions cause AHI rebuilding for large DBs. + * Log buffer: Holds log data before flush to disk. + + Size of each above memory is configurable, and impacts performance a lot. Requires careful analysis of workload, available resources, benchmarking and tuning for optimal performance. + +* Disk: + * Tables: Stores data within rows and columns. + * Indexes: Helps find rows with specific column values quickly, avoids full table scans. + * Redo Logs: all transactions are written to them, and after a crash, the recovery process corrects data written by incomplete transactions and replays any pending ones. + * Undo Logs: Records associated with a single transaction that contains information about how to undo the latest change by a transaction. + diff --git a/courses/databases_sql/intro.md b/courses/databases_sql/intro.md new file mode 100644 index 0000000..010736b --- /dev/null +++ b/courses/databases_sql/intro.md @@ -0,0 +1,21 @@ +# Relational Databases + +### What to expect from this training +You will have an understanding of what relational databases are, their advantages, and some MySQL specific concepts. + +### What is not covered under this course +* In depth implementation details + +* Advanced topics like normalization, sharding + +* Specific tools for administration + +### Introduction +The main purpose of database systems is to manage data. This includes storage, adding new data, deleting unused data, updating existing data, retrieving data within a reasonable response time, other maintenance tasks to keep the system running etc. + +### Prerequisites +* Complete [Linux course](/linux_basics/intro/) +* Install Docker (for lab section) + +### Pre-reads +[RDBMS Concepts](https://beginnersbook.com/2015/04/rdbms-concepts/) \ No newline at end of file diff --git a/courses/databases_sql/lab.md b/courses/databases_sql/lab.md new file mode 100644 index 0000000..c946d41 --- /dev/null +++ b/courses/databases_sql/lab.md @@ -0,0 +1,207 @@ +**Prerequisites** + +Install Docker + + +**Setup** + +Create a working directory named sos or something similar, and cd into it. + +Enter the following into a file named my.cnf under a directory named custom. + + +``` +sos $ cat custom/my.cnf +[mysqld] +# These settings apply to MySQL server +# You can set port, socket path, buffer size etc. +# Below, we are configuring slow query settings +slow_query_log=1 +slow_query_log_file=/var/log/mysqlslow.log +long_query_time=0.1 +``` + + +Start a container and enable slow query log with the following: + + +``` +sos $ docker run --name db -v custom:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=realsecret -d mysql:8 +sos $ docker cp custom/mysqld.cnf $(docker ps -qf "name=db"):/etc/mysql/conf.d/custom.cnf +sos $ docker restart $(docker ps -qf "name=db") +``` + + +Import a sample database + + +``` +sos $ git clone git@github.com:datacharmer/test_db.git +sos $ docker cp test_db $(docker ps -qf "name=db"):/home/test_db/ +sos $ docker exec -it $(docker ps -qf "name=db") bash +root@3ab5b18b0c7d:/# cd /home/test_db/ +root@3ab5b18b0c7d:/# mysql -uroot -prealsecret mysql < employees.sql +root@3ab5b18b0c7d:/etc# touch /var/log/mysqlslow.log +root@3ab5b18b0c7d:/etc# chown mysql:mysql /var/log/mysqlslow.log +``` + + +_Workshop 1: Run some sample queries_ +Run the following +``` +$ mysql -uroot -prealsecret mysql +mysql> + +# inspect DBs and tables +# the last 4 are MySQL internal DBs + +mysql> show databases; ++--------------------+ +| Database | ++--------------------+ +| employees | +| information_schema | +| mysql | +| performance_schema | +| sys | ++--------------------+ + +> use employees; +mysql> show tables; ++----------------------+ +| Tables_in_employees | ++----------------------+ +| current_dept_emp | +| departments | +| dept_emp | +| dept_emp_latest_date | +| dept_manager | +| employees | +| salaries | +| titles | ++----------------------+ + +# read a few rows +mysql> select * from employees limit 5; + +# filter data by conditions +mysql> select count(*) from employees where gender = 'M' limit 5; + +# find count of particular data +mysql> select count(*) from employees where first_name = 'Sachin'; +``` + +_Workshop 2: Use explain and explain analyze to profile a query, identify and add indexes required for improving performance_ +``` +# View all indexes on table +#(\G is to output horizontally, replace it with a ; to get table output) +mysql> show index from employees from employees\G +*************************** 1. row *************************** + Table: employees + Non_unique: 0 + Key_name: PRIMARY + Seq_in_index: 1 + Column_name: emp_no + Collation: A + Cardinality: 299113 + Sub_part: NULL + Packed: NULL + Null: + Index_type: BTREE + Comment: +Index_comment: + Visible: YES + Expression: NULL + +# This query uses an index, idenitfied by 'key' field +# By prefixing explain keyword to the command, +# we get query plan (including key used) +mysql> explain select * from employees where emp_no < 10005\G +*************************** 1. row *************************** + id: 1 + select_type: SIMPLE + table: employees + partitions: NULL + type: range +possible_keys: PRIMARY + key: PRIMARY + key_len: 4 + ref: NULL + rows: 4 + filtered: 100.00 + Extra: Using where + +# Compare that to the next query which does not utilize any index +mysql> explain select first_name, last_name from employees where first_name = 'Sachin'\G +*************************** 1. row *************************** + id: 1 + select_type: SIMPLE + table: employees + partitions: NULL + type: ALL +possible_keys: NULL + key: NULL + key_len: NULL + ref: NULL + rows: 299113 + filtered: 10.00 + Extra: Using where + +# Let's see how much time this query takes +mysql> explain analyze select first_name, last_name from employees where first_name = 'Sachin'\G +*************************** 1. row *************************** +EXPLAIN: -> Filter: (employees.first_name = 'Sachin') (cost=30143.55 rows=29911) (actual time=28.284..3952.428 rows=232 loops=1) + -> Table scan on employees (cost=30143.55 rows=299113) (actual time=0.095..1996.092 rows=300024 loops=1) + + +# Cost(estimated by query planner) is 30143.55 +# actual time=28.284ms for first row, 3952.428 for all rows +# Now lets try adding an index and running the query again +mysql> create index idx_firstname on employees(first_name); +Query OK, 0 rows affected (1.25 sec) +Records: 0 Duplicates: 0 Warnings: 0 + +mysql> explain analyze select first_name, last_name from employees where first_name = 'Sachin'; ++--------------------------------------------------------------------------------------------------------------------------------------------+ +| EXPLAIN | ++--------------------------------------------------------------------------------------------------------------------------------------------+ +| -> Index lookup on employees using idx_firstname (first_name='Sachin') (cost=81.20 rows=232) (actual time=0.551..2.934 rows=232 loops=1) + | ++--------------------------------------------------------------------------------------------------------------------------------------------+ +1 row in set (0.01 sec) + +# Actual time=0.551ms for first row +# 2.934ms for all rows. A huge improvement! +# Also notice that the query involves only an index lookup, +# and no table scan (reading all rows of table) +# ..which vastly reduces load on the DB. +``` + +_Workshop 3: Identify slow queries on a MySQL server_ +``` +# Run the command below in two terminal tabs to open two shells into the container. +docker exec -it $(docker ps -qf "name=db") bash + +# Open a mysql prompt in one of them and execute this command +# We have configured to log queries that take longer than 1s, +# so this sleep(3) will be logged +mysql -uroot -prealsecret mysql +mysql> sleep(3); + +# Now, in the other terminal, tail the slow log to find details about the query +root@62c92c89234d:/etc# tail -f /var/log/mysqlslow.log +/usr/sbin/mysqld, Version: 8.0.21 (MySQL Community Server - GPL). started with: +Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock +Time Id Command Argument +# Time: 2020-11-26T14:53:44.822348Z +# User@Host: root[root] @ localhost [] Id: 9 +# Query_time: 5.404938 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 1 +use employees; +# Time: 2020-11-26T14:53:58.015736Z +# User@Host: root[root] @ localhost [] Id: 9 +# Query_time: 10.000225 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 1 +SET timestamp=1606402428; +select sleep(3); +``` + +These were simulated examples with minimal complexity. In real life, the queries would be much more complex and the explain/analyze and slow query logs would have more details. \ No newline at end of file diff --git a/courses/databases_sql/mysql.md b/courses/databases_sql/mysql.md new file mode 100644 index 0000000..6e12789 --- /dev/null +++ b/courses/databases_sql/mysql.md @@ -0,0 +1,38 @@ +### MySQL architecture + +![alt_text](images/mysql_architecture.png "MySQL architecture diagram") + +MySQL architecture enables you to select the right storage engine for your needs, and abstracts away all implementation details from the end users (application engineers and [DBA](https://en.wikipedia.org/wiki/Database_administrator)) who only need to know a consistent stable API. + +Application layer: + +* Connection handling - each client gets its own connection which is cached for the duration of access) +* Authentication - server checks (username,password,host) info of client and allows/rejects connection +* Security: server determines whether the client has privileges to execute each query (check with _show privileges_ command) + +Server layer: + + + +* Services and utilities - backup/restore, replication, cluster etc +* SQL interface - clients run queries for data access and manipulation +* SQL parser - creates a parse tree from the query (lexical/syntactic/semantic analysis and code generation) +* Optimizer - optimizes queries using various algorithms and data available to it(table level stats), modifies queries, order of scanning, indexes to use etc. (check with explain command) +* Caches and buffers - cache stores query results, buffer pool(InnoDB) stores table and index data in [LRU](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)) fashion + +Storage engine options: + + + +* InnoDB: most widely used, transaction support, ACID compliant, supports row-level locking, crash recovery and multi-version concurrency control. Default since MySQL 5.5+. +* MyISAM: fast, does not support transactions, provides table-level locking, great for read-heavy workloads, mostly in web and data warehousing. Default upto MySQL 5.1. +* Archive: optimised for high speed inserts, compresses data as it is inserted, does not support transactions, ideal for storing and retrieving large amounts of seldom referenced historical, archived data +* Memory: tables in memory. Fastest engine, supports table-level locking, does not support transactions, ideal for creating temporary tables or quick lookups, data is lost after a shutdown +* CSV: stores data in CSV files, great for integrating into other applications that use this format +* … etc. + +It is possible to migrate from one storage engine to another. But this migration locks tables for all operations and is not online, as it changes the physical layout of the data. It takes a long time and is generally not recommended. Hence, choosing the right storage engine at the beginning is important. + +General guideline is to use InnoDB unless you have a specific need for one of the other storage engines. + +Running `mysql> SHOW ENGINES; `shows you the supported engines on your MySQL server. \ No newline at end of file diff --git a/courses/databases_sql/operations.md b/courses/databases_sql/operations.md new file mode 100644 index 0000000..a96c2fb --- /dev/null +++ b/courses/databases_sql/operations.md @@ -0,0 +1,64 @@ +* Explain and explain+analyze + + EXPLAIN <query> analyzes query plans from the optimizer, including how tables are joined, which tables/rows are scanned etc. + + Explain analyze shows the above and additional info like execution cost, number of rows returned, time taken etc. + + This knowledge is useful to tweak queries and add indexes. + + Watch this performance tuning [tutorial video](https://www.youtube.com/watch?v=pjRTLPeUOug). + + Checkout the [lab section](../lab.md) for a hands-on about indexes. + +* [Slow query logs](https://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html) + + Used to identify slow queries (configurable threshold), enabled in config or dynamically with a query + + Checkout the [lab section](../lab.md) about identifying slow queries. + +* User management + + This includes creation and changes to users, like managing privileges, changing password etc. + + + +* Backup and restore strategies, pros and cons + + Logical backup using mysqldump - slower but can be done online + + Physical backup (copy data directory or use xtrabackup) - quick backup/recovery. Copying data directory requires locking or shut down. xtrabackup is an improvement because it supports backups without shutting down (hot backup). + + Others - PITR, snapshots etc. + + +* Crash recovery process using redo logs + + After a crash, when you restart server it reads redo logs and replays modifications to recover + + +* Monitoring MySQL + + Key MySQL metrics: reads, writes, query runtime, errors, slow queries, connections, running threads, InnoDB metrics + + Key OS metrics: CPU, load, memory, disk I/O, network + + +* Replication + + Copies data from one instance to one or more instances. Helps in horizontal scaling, data protection, analytics and performance. Binlog dump thread on primary, replication I/O and SQL threads on secondary. Strategies include the standard async, semi async or group replication. + +* High Availability + + Ability to cope with failure at software, hardware and network level. Essential for anyone who needs 99.9%+ uptime. Can be implemented with replication or clustering solutions from MySQL, Percona, Oracle etc. Requires expertise to setup and maintain. Failover can be manual, scripted or using tools like Orchestrator. + +* [Data directory](https://dev.mysql.com/doc/refman/8.0/en/data-directory.html) + + Data is stored in a particular directory, with nested directories for the data contained in each database. There are also MySQL log files, InnoDB log files, server process ID file and some other configs. The data directory is configurable. + +* [MySQL configuration](https://dev.mysql.com/doc/refman/5.7/en/server-configuration.html) + + This can be done by passing [parameters during startup](https://dev.mysql.com/doc/refman/5.7/en/server-options.html), or in a [file](https://dev.mysql.com/doc/refman/8.0/en/option-files.html). There are a few [standard paths](https://dev.mysql.com/doc/refman/8.0/en/option-files.html#option-file-order) where MySQL looks for config files, `/etc/my.cnf` is one of the commonly used paths. These options are organized under headers (mysqld for server and mysql for client), you can explore them more in the lab that follows. + +* [Logs](https://dev.mysql.com/doc/refman/5.7/en/server-logs.html) + + MySQL has logs for various purposes - general query log, errors, binary logs (for replication), slow query log. Only error log is enabled by default (to reduce I/O and storage requirement), the others can be enabled when required - by specifying config parameters at startup or running commands at runtime. [Log destination](https://dev.mysql.com/doc/refman/5.7/en/log-destinations.html) can also be tweaked with config parameters. diff --git a/courses/index.md b/courses/index.md index b33b234..c7d62ab 100644 --- a/courses/index.md +++ b/courses/index.md @@ -16,7 +16,7 @@ In this course we are focusing on building strong foundational skills. The cours - [Linux Networking](https://linkedin.github.io/school-of-sre/linux_networking/intro/) - [Python and Web](https://linkedin.github.io/school-of-sre/python_web/intro/) - Data - - Relational databases (MySQL) + - [Relational databases(MySQL)](https://linkedin.github.io/school-of-sre/databases_sql/intro/) - [NoSQL concepts](https://linkedin.github.io/school-of-sre/databases_nosql/intro/) - [Big Data](https://linkedin.github.io/school-of-sre/big_data/intro/) - [Systems Design](https://linkedin.github.io/school-of-sre/systems_design/intro/) @@ -24,4 +24,4 @@ In this course we are focusing on building strong foundational skills. The cours We believe continuous learning will help in acquiring deeper knowledge and competencies in order to expand your skill sets, every module has added reference which could be a guide for further learning. Our hope is that by going through these modules we should be able to build the essential skills required for a Site Reliability Engineer. -At Linkedin, we are using this curriculum for onboarding our non-traditional hires and new college grads to the SRE role. We had multiple rounds of successful onboarding experience with the new members and helped them to be productive in a very short period of time. This motivated us to opensource these contents for helping other organizations onboarding new engineers to the role and individuals to get into the role. We realize that the initial content we created is just a starting point and we hope that the community can help in the journey of refining and extending the contents. +At Linkedin, we are using this curriculum for onboarding our non-traditional hires and new college grads to the SRE role. We had multiple rounds of successful onboarding experience with the new members and helped them to be productive in a very short period of time. This motivated us to opensource these contents for helping other organizations onboarding new engineers to the role and individuals to get into the role. We realize that the initial content we created is just a starting point and we hope that the community can help in the journey of refining and extending the contents. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index a056995..a6794f5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,14 @@ nav: - The URL Shortening App: python_web/url-shorten-app.md - Conclusion: python_web/sre-conclusion.md - Data: + - Relational Databases: + - Introduction: databases_sql/intro.md + - Key Concepts: databases_sql/concepts.md + - MySQL: databases_sql/mysql.md + - InnoDB: databases_sql/innodb.md + - Operational Concepts: databases_sql/operations.md + - Lab: databases_sql/lab.md + - Further Reading: databases_sql/reading.md - NoSQL: - Introduction: databases_nosql/intro.md - Key Concepts: databases_nosql/key_concepts.md @@ -54,6 +62,6 @@ nav: - Fundamentals of Security: security/fundamentals.md - Network Security: security/network_security.md - Threat, Attacks & Defences: security/threats_attacks_defences.md - - Writing Secure code: security/writing_secure_code.md + - Writing Secure code: security/writing_secure_code.md - Conclusion: security/conclusion.md - Contribute: CONTRIBUTING.md