diff --git a/1079203-20190304104037017-1495719611.png b/1079203-20190304104037017-1495719611.png new file mode 100644 index 0000000..9ba9f68 Binary files /dev/null and b/1079203-20190304104037017-1495719611.png differ diff --git a/1438655-20210220083347391-59878081.png b/1438655-20210220083347391-59878081.png new file mode 100644 index 0000000..40ea2ec Binary files /dev/null and b/1438655-20210220083347391-59878081.png differ diff --git a/1742867-20191104115215072-2033982939 1.jpg b/1742867-20191104115215072-2033982939 1.jpg new file mode 100644 index 0000000..577e4bc Binary files /dev/null and b/1742867-20191104115215072-2033982939 1.jpg differ diff --git a/1742867-20191104115215072-2033982939 2.jpg b/1742867-20191104115215072-2033982939 2.jpg new file mode 100644 index 0000000..e0e9293 Binary files /dev/null and b/1742867-20191104115215072-2033982939 2.jpg differ diff --git a/1742867-20191104115215072-2033982939 3.jpg b/1742867-20191104115215072-2033982939 3.jpg new file mode 100644 index 0000000..270e035 Binary files /dev/null and b/1742867-20191104115215072-2033982939 3.jpg differ diff --git a/1742867-20191104115215072-2033982939 4.jpg b/1742867-20191104115215072-2033982939 4.jpg new file mode 100644 index 0000000..51591e0 Binary files /dev/null and b/1742867-20191104115215072-2033982939 4.jpg differ diff --git a/1742867-20191104115215072-2033982939.jpg b/1742867-20191104115215072-2033982939.jpg new file mode 100644 index 0000000..d867534 Binary files /dev/null and b/1742867-20191104115215072-2033982939.jpg differ diff --git a/1745215-20201120153019722-1747791924.png b/1745215-20201120153019722-1747791924.png new file mode 100644 index 0000000..3e4d9eb Binary files /dev/null and b/1745215-20201120153019722-1747791924.png differ diff --git a/1745215-20201204225139159-906801326 1.png b/1745215-20201204225139159-906801326 1.png new file mode 100644 index 0000000..271d45d Binary files /dev/null and b/1745215-20201204225139159-906801326 1.png differ diff --git a/1745215-20201204225139159-906801326.png b/1745215-20201204225139159-906801326.png new file mode 100644 index 0000000..271d45d Binary files /dev/null and b/1745215-20201204225139159-906801326.png differ diff --git a/1fd40497a31465f77367692a09a223c9 1.png b/1fd40497a31465f77367692a09a223c9 1.png new file mode 100644 index 0000000..b247f9c Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 1.png differ diff --git a/1fd40497a31465f77367692a09a223c9 10.png b/1fd40497a31465f77367692a09a223c9 10.png new file mode 100644 index 0000000..d30f9b7 Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 10.png differ diff --git a/1fd40497a31465f77367692a09a223c9 11.png b/1fd40497a31465f77367692a09a223c9 11.png new file mode 100644 index 0000000..8f54f22 Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 11.png differ diff --git a/1fd40497a31465f77367692a09a223c9 2.png b/1fd40497a31465f77367692a09a223c9 2.png new file mode 100644 index 0000000..e08b65c Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 2.png differ diff --git a/1fd40497a31465f77367692a09a223c9 3.png b/1fd40497a31465f77367692a09a223c9 3.png new file mode 100644 index 0000000..ad952fc Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 3.png differ diff --git a/1fd40497a31465f77367692a09a223c9 4.png b/1fd40497a31465f77367692a09a223c9 4.png new file mode 100644 index 0000000..120318e Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 4.png differ diff --git a/1fd40497a31465f77367692a09a223c9 5.png b/1fd40497a31465f77367692a09a223c9 5.png new file mode 100644 index 0000000..1adb256 Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 5.png differ diff --git a/1fd40497a31465f77367692a09a223c9 6.png b/1fd40497a31465f77367692a09a223c9 6.png new file mode 100644 index 0000000..3a460ac Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 6.png differ diff --git a/1fd40497a31465f77367692a09a223c9 7.png b/1fd40497a31465f77367692a09a223c9 7.png new file mode 100644 index 0000000..e6929b7 Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 7.png differ diff --git a/1fd40497a31465f77367692a09a223c9 8.png b/1fd40497a31465f77367692a09a223c9 8.png new file mode 100644 index 0000000..c01c3e5 Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 8.png differ diff --git a/1fd40497a31465f77367692a09a223c9 9.png b/1fd40497a31465f77367692a09a223c9 9.png new file mode 100644 index 0000000..538bc89 Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9 9.png differ diff --git a/1fd40497a31465f77367692a09a223c9.png b/1fd40497a31465f77367692a09a223c9.png new file mode 100644 index 0000000..133ff41 Binary files /dev/null and b/1fd40497a31465f77367692a09a223c9.png differ diff --git a/27605d483e8935da683a93be015713f331378 1.png b/27605d483e8935da683a93be015713f331378 1.png new file mode 100644 index 0000000..0e91d09 Binary files /dev/null and b/27605d483e8935da683a93be015713f331378 1.png differ diff --git a/27605d483e8935da683a93be015713f331378.png b/27605d483e8935da683a93be015713f331378.png new file mode 100644 index 0000000..65bb447 Binary files /dev/null and b/27605d483e8935da683a93be015713f331378.png differ diff --git a/326517643.png b/326517643.png new file mode 100644 index 0000000..0b1aaa1 Binary files /dev/null and b/326517643.png differ diff --git a/3669053-3b340bc054bdb1e0.png b/3669053-3b340bc054bdb1e0.png new file mode 100644 index 0000000..3f0de38 Binary files /dev/null and b/3669053-3b340bc054bdb1e0.png differ diff --git a/5128967-24284b0c0ed22be3.png b/5128967-24284b0c0ed22be3.png new file mode 100644 index 0000000..5c9b1e0 Binary files /dev/null and b/5128967-24284b0c0ed22be3.png differ diff --git a/5630287-0a3d0b3926366d63 1.jpg b/5630287-0a3d0b3926366d63 1.jpg new file mode 100644 index 0000000..be81890 Binary files /dev/null and b/5630287-0a3d0b3926366d63 1.jpg differ diff --git a/5630287-0a3d0b3926366d63.jpg b/5630287-0a3d0b3926366d63.jpg new file mode 100644 index 0000000..25964bc Binary files /dev/null and b/5630287-0a3d0b3926366d63.jpg differ diff --git a/5630287-350bc3c474eef0e8 1.jpg b/5630287-350bc3c474eef0e8 1.jpg new file mode 100644 index 0000000..c9058d6 Binary files /dev/null and b/5630287-350bc3c474eef0e8 1.jpg differ diff --git a/5630287-350bc3c474eef0e8 2.jpg b/5630287-350bc3c474eef0e8 2.jpg new file mode 100644 index 0000000..948fee8 Binary files /dev/null and b/5630287-350bc3c474eef0e8 2.jpg differ diff --git a/5630287-350bc3c474eef0e8 3.jpg b/5630287-350bc3c474eef0e8 3.jpg new file mode 100644 index 0000000..8a4fd37 Binary files /dev/null and b/5630287-350bc3c474eef0e8 3.jpg differ diff --git a/5630287-350bc3c474eef0e8 4.jpg b/5630287-350bc3c474eef0e8 4.jpg new file mode 100644 index 0000000..8a4fd37 Binary files /dev/null and b/5630287-350bc3c474eef0e8 4.jpg differ diff --git a/5630287-350bc3c474eef0e8.jpg b/5630287-350bc3c474eef0e8.jpg new file mode 100644 index 0000000..5145be0 Binary files /dev/null and b/5630287-350bc3c474eef0e8.jpg differ diff --git a/5630287-f638cadea7b6cb96.webp b/5630287-f638cadea7b6cb96.webp new file mode 100644 index 0000000..93755d6 Binary files /dev/null and b/5630287-f638cadea7b6cb96.webp differ diff --git a/640 1.jpg b/640 1.jpg new file mode 100644 index 0000000..400ece8 Binary files /dev/null and b/640 1.jpg differ diff --git a/640.jpg b/640.jpg new file mode 100644 index 0000000..1990715 Binary files /dev/null and b/640.jpg differ diff --git a/71cfccb87da28e4cc5b618bcef02a2ac.png b/71cfccb87da28e4cc5b618bcef02a2ac.png new file mode 100644 index 0000000..6e9a3e3 Binary files /dev/null and b/71cfccb87da28e4cc5b618bcef02a2ac.png differ diff --git a/721070-20170504110246211-10684485.png b/721070-20170504110246211-10684485.png new file mode 100644 index 0000000..08ceae5 Binary files /dev/null and b/721070-20170504110246211-10684485.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 1.png b/912883e51327e0c7a9d753d11896326511272 1.png new file mode 100644 index 0000000..8486a20 Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 1.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 2.png b/912883e51327e0c7a9d753d11896326511272 2.png new file mode 100644 index 0000000..b4f5f6e Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 2.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 3.png b/912883e51327e0c7a9d753d11896326511272 3.png new file mode 100644 index 0000000..fbe43d0 Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 3.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 4.png b/912883e51327e0c7a9d753d11896326511272 4.png new file mode 100644 index 0000000..77d6806 Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 4.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 5.png b/912883e51327e0c7a9d753d11896326511272 5.png new file mode 100644 index 0000000..012f981 Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 5.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 6.png b/912883e51327e0c7a9d753d11896326511272 6.png new file mode 100644 index 0000000..d51294a Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 6.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 7.png b/912883e51327e0c7a9d753d11896326511272 7.png new file mode 100644 index 0000000..7047966 Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 7.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 8.png b/912883e51327e0c7a9d753d11896326511272 8.png new file mode 100644 index 0000000..7c6c5f2 Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 8.png differ diff --git a/912883e51327e0c7a9d753d11896326511272 9.png b/912883e51327e0c7a9d753d11896326511272 9.png new file mode 100644 index 0000000..4123722 Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272 9.png differ diff --git a/912883e51327e0c7a9d753d11896326511272.png b/912883e51327e0c7a9d753d11896326511272.png new file mode 100644 index 0000000..de8707b Binary files /dev/null and b/912883e51327e0c7a9d753d11896326511272.png differ diff --git a/99730e6774737f2ecdd4cb029b952c24.png b/99730e6774737f2ecdd4cb029b952c24.png new file mode 100644 index 0000000..09484a2 Binary files /dev/null and b/99730e6774737f2ecdd4cb029b952c24.png differ diff --git a/Snipaste_2023-02-27_15-13-44 1.png b/Snipaste_2023-02-27_15-13-44 1.png new file mode 100644 index 0000000..5b163b2 Binary files /dev/null and b/Snipaste_2023-02-27_15-13-44 1.png differ diff --git a/Snipaste_2023-02-27_15-13-44 2.png b/Snipaste_2023-02-27_15-13-44 2.png new file mode 100644 index 0000000..5b163b2 Binary files /dev/null and b/Snipaste_2023-02-27_15-13-44 2.png differ diff --git a/Snipaste_2023-02-27_15-13-44 3.png b/Snipaste_2023-02-27_15-13-44 3.png new file mode 100644 index 0000000..333ac27 Binary files /dev/null and b/Snipaste_2023-02-27_15-13-44 3.png differ diff --git a/Snipaste_2023-02-27_15-13-44.png b/Snipaste_2023-02-27_15-13-44.png new file mode 100644 index 0000000..6535129 Binary files /dev/null and b/Snipaste_2023-02-27_15-13-44.png differ diff --git a/Snipaste_2023-02-28_09-45-09.png b/Snipaste_2023-02-28_09-45-09.png new file mode 100644 index 0000000..3e93a43 Binary files /dev/null and b/Snipaste_2023-02-28_09-45-09.png differ diff --git a/Snipaste_2023-02-28_11-48-27 1.png b/Snipaste_2023-02-28_11-48-27 1.png new file mode 100644 index 0000000..cb532c2 Binary files /dev/null and b/Snipaste_2023-02-28_11-48-27 1.png differ diff --git a/Snipaste_2023-02-28_11-48-27.png b/Snipaste_2023-02-28_11-48-27.png new file mode 100644 index 0000000..af9c10a Binary files /dev/null and b/Snipaste_2023-02-28_11-48-27.png differ diff --git a/Snipaste_2023-02-28_13-09-43 1.png b/Snipaste_2023-02-28_13-09-43 1.png new file mode 100644 index 0000000..ae94f9a Binary files /dev/null and b/Snipaste_2023-02-28_13-09-43 1.png differ diff --git a/Snipaste_2023-02-28_13-09-43 2.png b/Snipaste_2023-02-28_13-09-43 2.png new file mode 100644 index 0000000..9cf5407 Binary files /dev/null and b/Snipaste_2023-02-28_13-09-43 2.png differ diff --git a/Snipaste_2023-02-28_13-09-43 3.png b/Snipaste_2023-02-28_13-09-43 3.png new file mode 100644 index 0000000..0c062be Binary files /dev/null and b/Snipaste_2023-02-28_13-09-43 3.png differ diff --git a/Snipaste_2023-02-28_13-09-43 4.png b/Snipaste_2023-02-28_13-09-43 4.png new file mode 100644 index 0000000..15e5c7a Binary files /dev/null and b/Snipaste_2023-02-28_13-09-43 4.png differ diff --git a/Snipaste_2023-02-28_13-09-43 5.png b/Snipaste_2023-02-28_13-09-43 5.png new file mode 100644 index 0000000..8e7c3bd Binary files /dev/null and b/Snipaste_2023-02-28_13-09-43 5.png differ diff --git a/Snipaste_2023-02-28_13-09-43.png b/Snipaste_2023-02-28_13-09-43.png new file mode 100644 index 0000000..ae94f9a Binary files /dev/null and b/Snipaste_2023-02-28_13-09-43.png differ diff --git a/Snipaste_2023-03-01_17-52-19 1.png b/Snipaste_2023-03-01_17-52-19 1.png new file mode 100644 index 0000000..735c48e Binary files /dev/null and b/Snipaste_2023-03-01_17-52-19 1.png differ diff --git a/Snipaste_2023-03-01_17-52-19 2.png b/Snipaste_2023-03-01_17-52-19 2.png new file mode 100644 index 0000000..ce9e07e Binary files /dev/null and b/Snipaste_2023-03-01_17-52-19 2.png differ diff --git a/Snipaste_2023-03-01_17-52-19 3.png b/Snipaste_2023-03-01_17-52-19 3.png new file mode 100644 index 0000000..2cbdd7a Binary files /dev/null and b/Snipaste_2023-03-01_17-52-19 3.png differ diff --git a/Snipaste_2023-03-01_17-52-19 4.png b/Snipaste_2023-03-01_17-52-19 4.png new file mode 100644 index 0000000..af9f0a5 Binary files /dev/null and b/Snipaste_2023-03-01_17-52-19 4.png differ diff --git a/Snipaste_2023-03-01_17-52-19 5.png b/Snipaste_2023-03-01_17-52-19 5.png new file mode 100644 index 0000000..07e1218 Binary files /dev/null and b/Snipaste_2023-03-01_17-52-19 5.png differ diff --git a/Snipaste_2023-03-01_17-52-19 6.png b/Snipaste_2023-03-01_17-52-19 6.png new file mode 100644 index 0000000..fff9e00 Binary files /dev/null and b/Snipaste_2023-03-01_17-52-19 6.png differ diff --git a/Snipaste_2023-03-01_17-52-19.png b/Snipaste_2023-03-01_17-52-19.png new file mode 100644 index 0000000..328b04c Binary files /dev/null and b/Snipaste_2023-03-01_17-52-19.png differ diff --git a/Snipaste_2023-03-02_11-05-25 1.png b/Snipaste_2023-03-02_11-05-25 1.png new file mode 100644 index 0000000..4da24bd Binary files /dev/null and b/Snipaste_2023-03-02_11-05-25 1.png differ diff --git a/Snipaste_2023-03-02_11-05-25.png b/Snipaste_2023-03-02_11-05-25.png new file mode 100644 index 0000000..a9b9fc3 Binary files /dev/null and b/Snipaste_2023-03-02_11-05-25.png differ diff --git a/Snipaste_2023-03-06_09-25-52 1.png b/Snipaste_2023-03-06_09-25-52 1.png new file mode 100644 index 0000000..35f1700 Binary files /dev/null and b/Snipaste_2023-03-06_09-25-52 1.png differ diff --git a/Snipaste_2023-03-06_09-25-52 2.png b/Snipaste_2023-03-06_09-25-52 2.png new file mode 100644 index 0000000..a55da45 Binary files /dev/null and b/Snipaste_2023-03-06_09-25-52 2.png differ diff --git a/Snipaste_2023-03-06_09-25-52 3.png b/Snipaste_2023-03-06_09-25-52 3.png new file mode 100644 index 0000000..75c3808 Binary files /dev/null and b/Snipaste_2023-03-06_09-25-52 3.png differ diff --git a/Snipaste_2023-03-06_09-25-52 4.png b/Snipaste_2023-03-06_09-25-52 4.png new file mode 100644 index 0000000..7c10857 Binary files /dev/null and b/Snipaste_2023-03-06_09-25-52 4.png differ diff --git a/Snipaste_2023-03-06_09-25-52 5.png b/Snipaste_2023-03-06_09-25-52 5.png new file mode 100644 index 0000000..9cf2df4 Binary files /dev/null and b/Snipaste_2023-03-06_09-25-52 5.png differ diff --git a/Snipaste_2023-03-06_09-25-52 6.png b/Snipaste_2023-03-06_09-25-52 6.png new file mode 100644 index 0000000..1e53cea Binary files /dev/null and b/Snipaste_2023-03-06_09-25-52 6.png differ diff --git a/Snipaste_2023-03-06_09-25-52.png b/Snipaste_2023-03-06_09-25-52.png new file mode 100644 index 0000000..84f2fcb Binary files /dev/null and b/Snipaste_2023-03-06_09-25-52.png differ diff --git a/Snipaste_2023-03-06_10-26-39 1.png b/Snipaste_2023-03-06_10-26-39 1.png new file mode 100644 index 0000000..496de41 Binary files /dev/null and b/Snipaste_2023-03-06_10-26-39 1.png differ diff --git a/Snipaste_2023-03-06_10-26-39 2.png b/Snipaste_2023-03-06_10-26-39 2.png new file mode 100644 index 0000000..b3a913d Binary files /dev/null and b/Snipaste_2023-03-06_10-26-39 2.png differ diff --git a/Snipaste_2023-03-06_10-26-39 3.png b/Snipaste_2023-03-06_10-26-39 3.png new file mode 100644 index 0000000..60b9ccb Binary files /dev/null and b/Snipaste_2023-03-06_10-26-39 3.png differ diff --git a/Snipaste_2023-03-06_10-26-39.png b/Snipaste_2023-03-06_10-26-39.png new file mode 100644 index 0000000..1baa89e Binary files /dev/null and b/Snipaste_2023-03-06_10-26-39.png differ diff --git a/Snipaste_2023-03-07_10-40-14.png b/Snipaste_2023-03-07_10-40-14.png new file mode 100644 index 0000000..c82b85c Binary files /dev/null and b/Snipaste_2023-03-07_10-40-14.png differ diff --git a/Snipaste_2023-03-26_09-05-48 1.png b/Snipaste_2023-03-26_09-05-48 1.png new file mode 100644 index 0000000..27040bf Binary files /dev/null and b/Snipaste_2023-03-26_09-05-48 1.png differ diff --git a/Snipaste_2023-03-26_09-05-48 2.png b/Snipaste_2023-03-26_09-05-48 2.png new file mode 100644 index 0000000..3c09c90 Binary files /dev/null and b/Snipaste_2023-03-26_09-05-48 2.png differ diff --git a/Snipaste_2023-03-26_09-05-48.png b/Snipaste_2023-03-26_09-05-48.png new file mode 100644 index 0000000..14b63c6 Binary files /dev/null and b/Snipaste_2023-03-26_09-05-48.png differ diff --git a/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 1.png b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 1.png new file mode 100644 index 0000000..b9945c7 Binary files /dev/null and b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 1.png differ diff --git a/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 2.png b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 2.png new file mode 100644 index 0000000..88d9939 Binary files /dev/null and b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 2.png differ diff --git a/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 3.png b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 3.png new file mode 100644 index 0000000..06dbbfc Binary files /dev/null and b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 3.png differ diff --git a/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 4.png b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 4.png new file mode 100644 index 0000000..58a36d5 Binary files /dev/null and b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 4.png differ diff --git a/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 5.png b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 5.png new file mode 100644 index 0000000..fc20f3b Binary files /dev/null and b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 5.png differ diff --git a/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 6.png b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 6.png new file mode 100644 index 0000000..94c7d2b Binary files /dev/null and b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 6.png differ diff --git a/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA.png b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA.png new file mode 100644 index 0000000..ae02140 Binary files /dev/null and b/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA.png differ diff --git a/dbe22eed6ebbd55d48fbe3b1ccbd93e9.png b/dbe22eed6ebbd55d48fbe3b1ccbd93e9.png new file mode 100644 index 0000000..48be0a4 Binary files /dev/null and b/dbe22eed6ebbd55d48fbe3b1ccbd93e9.png differ diff --git a/dd0a29a1d43c8ac8917d25c202895363 1.png b/dd0a29a1d43c8ac8917d25c202895363 1.png new file mode 100644 index 0000000..c99b5f4 Binary files /dev/null and b/dd0a29a1d43c8ac8917d25c202895363 1.png differ diff --git a/dd0a29a1d43c8ac8917d25c202895363.png b/dd0a29a1d43c8ac8917d25c202895363.png new file mode 100644 index 0000000..85481e2 Binary files /dev/null and b/dd0a29a1d43c8ac8917d25c202895363.png differ diff --git a/fe889daa8be54dcaa1c7cc364c7a35f5.png b/fe889daa8be54dcaa1c7cc364c7a35f5.png new file mode 100644 index 0000000..6699fcf Binary files /dev/null and b/fe889daa8be54dcaa1c7cc364c7a35f5.png differ diff --git a/file-permissions-rwx.jpg b/file-permissions-rwx.jpg new file mode 100644 index 0000000..8e0faae Binary files /dev/null and b/file-permissions-rwx.jpg differ diff --git a/rwx-standard-unix-permission-bits.png b/rwx-standard-unix-permission-bits.png new file mode 100644 index 0000000..c754d01 Binary files /dev/null and b/rwx-standard-unix-permission-bits.png differ diff --git a/v2-1a675ef9a5b7e5d913c75e769a12c606_720w 1.webp b/v2-1a675ef9a5b7e5d913c75e769a12c606_720w 1.webp new file mode 100644 index 0000000..6b3e936 Binary files /dev/null and b/v2-1a675ef9a5b7e5d913c75e769a12c606_720w 1.webp differ diff --git a/v2-1a675ef9a5b7e5d913c75e769a12c606_720w.webp b/v2-1a675ef9a5b7e5d913c75e769a12c606_720w.webp new file mode 100644 index 0000000..8a5f5df Binary files /dev/null and b/v2-1a675ef9a5b7e5d913c75e769a12c606_720w.webp differ diff --git a/v2-7228673618b83bc0d93da19f8467fd3a_720w.jfif b/v2-7228673618b83bc0d93da19f8467fd3a_720w.jfif new file mode 100644 index 0000000..2a32482 Binary files /dev/null and b/v2-7228673618b83bc0d93da19f8467fd3a_720w.jfif differ diff --git a/v2-8b32b881d0d8ea046455a6d77868aebe_720w.webp b/v2-8b32b881d0d8ea046455a6d77868aebe_720w.webp new file mode 100644 index 0000000..f8830f3 Binary files /dev/null and b/v2-8b32b881d0d8ea046455a6d77868aebe_720w.webp differ diff --git a/v2-95b883ec66e961e520cca2516ed0ceb7_720w.webp b/v2-95b883ec66e961e520cca2516ed0ceb7_720w.webp new file mode 100644 index 0000000..726fa16 Binary files /dev/null and b/v2-95b883ec66e961e520cca2516ed0ceb7_720w.webp differ diff --git a/v2-b5804eb2bb1b3f2ff091c18bbf915819_720w.gif b/v2-b5804eb2bb1b3f2ff091c18bbf915819_720w.gif new file mode 100644 index 0000000..d6c9906 Binary files /dev/null and b/v2-b5804eb2bb1b3f2ff091c18bbf915819_720w.gif differ diff --git a/v2-bc178ed60f03fb733003a675e3828ba4_720w.webp b/v2-bc178ed60f03fb733003a675e3828ba4_720w.webp new file mode 100644 index 0000000..4b6f285 Binary files /dev/null and b/v2-bc178ed60f03fb733003a675e3828ba4_720w.webp differ diff --git a/v2-c2f717ff3e4aecd6d756f3f1321468dd_720w 1.webp b/v2-c2f717ff3e4aecd6d756f3f1321468dd_720w 1.webp new file mode 100644 index 0000000..0049271 Binary files /dev/null and b/v2-c2f717ff3e4aecd6d756f3f1321468dd_720w 1.webp differ diff --git a/v2-c2f717ff3e4aecd6d756f3f1321468dd_720w.webp b/v2-c2f717ff3e4aecd6d756f3f1321468dd_720w.webp new file mode 100644 index 0000000..c8d1051 Binary files /dev/null and b/v2-c2f717ff3e4aecd6d756f3f1321468dd_720w.webp differ diff --git a/vi-vim-cheat-sheet-sch.gif b/vi-vim-cheat-sheet-sch.gif new file mode 100644 index 0000000..7caed81 Binary files /dev/null and b/vi-vim-cheat-sheet-sch.gif differ diff --git a/work常用/Linux命令.md b/work常用/Linux命令.md new file mode 100644 index 0000000..2ae48b6 --- /dev/null +++ b/work常用/Linux命令.md @@ -0,0 +1,331 @@ +# 一、文件 +## 1.1 压缩及解压 ++ zip +```shell + # 压缩 + zip -r ./xxx.zip ./* + # 解压 + unzip xxx.zip + # 查看压缩文件内容 + unzip -v xxx.zip + # 从zip中删除xxx.txt文件 + zip xxx.zip -d xxx.txt +``` + ++ tar +```shell +# 解压 +tar -zxvf xxxx.tar.gz +tar -xvf xxxx.tar +tar -xjvf xxxx.tar.bz2 + +# 压缩 +# 将目录里所有jpg文件打包成tar.jpg +tar -cvf jpg.tar *.jpg +# 将目录里所有jpg文件打包成jpg.tar后,并且将其用gzip压缩 +tar -czf jpg.tar.gz *.jpg +``` + ++ 压缩命令过长 +```shell +find ./ -name '*' -print |zip files -@ +``` + +## 1.2 文件合并及切分 ++ 合并 +```shell + cat *.txt > target.txt +``` + ++ 拆分 +```shell +# 按大小切分 +split -b 100m xxxx +# 按行切分 +split -l xxxx +# 设置拆分后文件名,指定数字后缀 +split -b 100m -d xxxx xxxx_part_ +``` + +## 1.3 查看文件 ++ 文件大小 +```shell +du -sh xxx +``` ++ ls +``` +# 查看所有 +ls -a +# 列表形式显示 +ls -l +``` ++ ll +``` +# 以文件上次被修改的时间排序 +ll -lht|head -n 20 +``` +## 1.4 创建文件 +``` +# 创建文件 +touch xxx.txt +# 创建隐藏文件 +touch .xxxx.txt +``` + +## 1.5 移动及复制文件(夹) +```shell +# 将xxx.txt文件移动到abc文件夹 +mv xxx.txt abc +# 将1文件移动到2文件,实质上就是1文件重命名为2文件 +mv 1.txt 2.txt +``` + +``` +# 使用指令 cp 将当前目录 test/ 下的所有文件复制到新目录 newtest 下 +cp –r test/ newtest +# cp 命令使用 -r 参数可以将 packageA 下的所有文件拷贝到 packageB +cp -r /home/packageA/* /home/cp/packageB/ +``` + +## 1.6 查找文件 ++ find +``` +# 指定目录 +find /home -name 123.txt +# 模糊查找12开头的文件 +find /home -name ‘12*’ +# 将当前目录及其子目录下所有文件后缀为 .c 的文件列出来 +find . -name "*.c" +# 将当前目录及其子目录中的所有文件列出 +find . -type f +# 全局查找以txt后缀文件 +find / -name "*.txt*" +``` + +## 1.7 查看文件 ++ view +```shell +# 进入vi查看,不可编辑 +view xxx.txt +``` ++ cat +``` +# 输出文件 +cat xxx.txt +``` + +## 1.8 清空文件 ++ true +```shell +true > 1.txt +``` + +## 1.9 将结果转存文件 +``` +# 将内容追加到txt中 +ls > 1.txt +ll > 1.txt +``` + +## 1.10 grep +```shell +# 查找进程 +ps -ef|grep xxxx +# +``` +## 1.11 sed + +## 1.12 awk + +# 二、网络 +## 2.1 查看IP +yum install -y net-tools ++ ifconfig +``` +ifconfig +``` ++ lsof +``` +# 查看所有 +lsof -i +# 查看指定端口 +lsof -i:8080 +# 查看文件进程 +lsof xxx.txt +``` +## 2.2 查看网关 +``` +# 查看gateway +route -n +``` +## 2.3 查看端口占用 +``` +netstat -anp|grep 3306 +``` +## 2.4 ping +```shell +ping 192.168.1.1 +``` +## 2.5 host ++ hostname +``` +# 查看本机Hostname +hostname +``` + +```shell +# 查看Hosts文件 +vi /etc/hosts +``` + +## 2.6 SSH远程登录 +``` +# 默认端口22 +ssh username@ip +# 指定端口 +ssh -p 2233 username@ip +``` + +## 2.7 SCP远程复制 + +``` +# 指定用户名IP +scp local_file remote_username@remote_ip:remote_folder +或者 +scp local_file remote_username@remote_ip:remote_file +或者 +# 做了互信 +scp local_file remote_ip:remote_folder +或者 +scp local_file remote_ip:remote_file +scp local_file remote_host:remote_file +# 递归复制整个目录 +scp -r local_dir remote_host:remote_folder +``` + +# 三、权限 +## 3.1 用户权限及用户组 +Linux/Unix 的文件调用权限分为三级 : 文件所有者(Owner)、用户组(Group)、其它用户(Other Users) ++ chmod +``` +# 添加操作权限 +chmod +x xxx.sh +# +chmod 777 xxx.sh +``` + +![[file-permissions-rwx.jpg]]![[rwx-standard-unix-permission-bits.png]] ++ chown +``` +# 添加操作用户 +chown [-R] 所有者:所属组 文件或目录 +``` + +## 3.2 免密 +``` +# 安装ssh +yum -y install openssh openssh-server openssh-clients +systemctl start sshd +systemctl status sshd +``` + +``` +ssh-keygen -t rsa #三次回车 +# 复制rsa凭证到对方机cdh-master在hosts中配置,也可以改为 用户名@ip +ssh-copy-id -f -i ~/.ssh/id_rsa.pub cdh-master +ssh-copy-id -f -i ~/.ssh/id_rsa.pub cdh-slave1 +``` + + +# 四、系统 +## 4.1 系统时间 +``` +date +``` +## 4.2 防火墙 +``` +systemctl disable firewalld +systemctl stop firewalld +``` +## 4.3 selinux +``` +[root@dev-server ~]# getenforce +Disabled +[root@dev-server ~]# /usr/sbin/sestatus -v +SELinux status: disabled +``` +临时关闭 +``` +##设置SELinux 成为permissive模式 +##setenforce 1 设置SELinux 成为enforcing模式 +setenforce 0 +``` +永久关闭 +``` +vi /etc/selinux/config +# 将SELINUX=enforcing改为SELINUX=disabled +# 设置后需要重启才能生效 +``` +## 4.4 linux内核及版本 ++ lsb_release +``` +# 查看发行版 +lsb_release -a +``` ++ uname +``` +# 查看内核版本 +uname -a +``` + +## 4.5 查看内存、CPU ++ free 查看内存 +```shell +# 以G为单位 +free -h +# 以M为单位 +free -m +``` + +CPU +``` +# 查看CPU型号及总线程数 +cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c +# 查看CPU线程数 +cat /proc/cpuinfo |grep "processor"|wc -l +``` + +## 4.6 修改密码 +``` +# 修改root密码 +passwd root +``` + +## 4.7 查看磁盘 +```shell +df -h +``` + +## 4.8 查看进程 ++ jps +``` +查看java虚拟机进程 +jsp +``` + +# 五、vim +![[vi-vim-cheat-sheet-sch.gif]] + + +| 操作 | 命令 | +| -------------- | ------------------------------- | +| 复制 | yy 复制当前行 | +| 粘贴 | p | +| 查找 | 普通模式下 按下/ 并输入待查字符 | +| 保存/强制保存 | wq/wq! | +| 退出vim | q/q! | +| 取消插入模式 | esc | +| 显示/取消行号 | :set nu/:set nonu | +| 修改文件format | :set fileformat=unix | +| 删除 | dd 删除当前行 | +| 方向键 | h:左 j:下 k:上 l:右 | diff --git a/work常用/docker 命令.md b/work常用/docker 命令.md new file mode 100644 index 0000000..636ff73 --- /dev/null +++ b/work常用/docker 命令.md @@ -0,0 +1,237 @@ +# 一、容器操作命令 +## 1.列出容器 PS +- **-a :** 显示所有的容器,包括未运行的。 +- **-f :** 根据条件过滤显示的内容。 +- **–format :** 指定返回值的模板文件。 +- **-l :** 显示最近创建的容器。 +- **-n :** 列出最近创建的n个容器。 +- **–no-trunc :** 不截断输出。 +- **-q :** 静默模式,只显示容器编号。 +- **-s :** 显示总的文件大小。 +容器的7种状态:created(已创建)、restarting(重启中)、running(运行中)、removing(迁移中)、paused(暂停)、exited(停止)、dead(死亡)。 + +## 2. 列出指定的容器的端口映射 docker port xxx +![[Snipaste_2023-03-06_09-25-52.png]] + +## 3. 获取容器的日志 docker logs -f xxxx +- **-f :** 跟踪日志输出 +- **–since :** 显示某个开始时间的所有日志 +- **-t :** 显示时间戳 +- **–tail :** 仅列出最新N条容器日志 +![[Snipaste_2023-03-06_09-25-52 1.png]] + +### 3.1 第二种方法 +```shell + docker inspect --format '{{.LogPath}}' 86a50748c5d9 +``` +会返回日志路径 +```shell +/var/lib/docker/containers/86a50748c5d9e63b1d30be71a043d2202b573306df0b0062072fa45234bf2836/86a50748c5d9e63b1d30be71a043d2202b573306df0b0062072fa45234bf2836-json.log +``` + +## 4. 查看容器中运行的进程信息 docker top xxx +![[Snipaste_2023-03-06_09-25-52 2.png]] + +## 5.创建一个新的容器 docker run +语法: +``` +docker run [OPTIONS] IMAGE [COMMAND] [ARG...] +``` +OPTIONS说明: + +- **-a stdin:** 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项; + +- **-d:** 后台运行容器,并返回容器ID; + +- **-i:** 以交互模式运行容器,通常与 -t 同时使用; + +- **-P:** 随机端口映射,容器内部端口**随机**映射到主机的端口 + +- **-p:** 指定端口映射,格式为:主机(宿主)端口:容器端口 + +- **-t:** 为容器重新分配一个伪输入终端,通常与 -i 同时使用; + +- **--name="nginx-lb":** 为容器指定一个名称; + +- **--dns 8.8.8.8:** 指定容器使用的DNS服务器,默认和宿主一致; + +- **--dns-search example.com:** 指定容器DNS搜索域名,默认和宿主一致; + +- **-h "mars":** 指定容器的hostname; + +- **-e username="ritchie":** 设置环境变量; + +- **--env-file=[]:** 从指定文件读入环境变量; + +- **--cpuset="0-2" or --cpuset="0,1,2":** 绑定容器到指定CPU运行; + +- **-m :**设置容器使用内存最大值; + +- **--net="bridge":** 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型; + +- **--link=[]:** 添加链接到另一个容器; + +- **--expose=[]:** 开放一个端口或一组端口; + +- **--volume , -v:** 绑定一个卷 + + +使用镜像 nginx:latest,以后台模式启动一个容器,将容器的 80 端口映射到主机的 80 端口,主机的目录 /data 映射到容器的 /data。 +```shell +docker run -p 80:80 -v /data:/data -d nginx:latest +``` + +## 6. 启动、停止、重启 docker start/stop/restart +```shell +docker start xxx +docker stop xxx +docker restart xxx +``` + + +## 7.杀掉一个运行中的容器 docker kill +```shell +docker kill xxx +``` + +## 8. 删除一个或多个容器 docker rm +强制删除容器 db01、db02 +```shell +docker rm -f db1 db3 +``` + + +## 9. 在运行的容器中执行命令或进入容器 docker exec +- **-d :** 分离模式: 在后台运行 +- **-i :** 即使没有附加也保持STDIN 打开 +- **-t :** 分配一个伪终端 +在容器 mynginx 中以交互模式执行容器内 /root/runoob.sh 脚本: +```shell +docker exec -it mynginx /bin/sh /root/runoob.sh +``` + +在容器 mynginx 中开启一个交互模式的终端: +```shell +docker exec -i -t mynginx /bin/bash +``` + +通过 docker ps -a 命令查看已经在运行的容器,然后使用容器 ID 进入容器。 +```shell +# docker ps -a ... +9df70f9a0714 openjdk "/usercode/script.sh…" +... +``` +第一列的 9df70f9a0714 就是容器 ID。 +通过 exec 命令对指定的容器执行 bash: +``` +docker exec -it 9df70f9a0714 /bin/bash +``` + +# 二、容器rootfs命令 +## 1. 容器与主机之间的数据拷贝 docker cp +- **-L :** 保持源目标中的链接 +语法格式: +```shell +docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|- +docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH +``` + +```shell +docker cp tomcat:/usr/local/tomcat/README.md ./ +``` + +# 三、镜像仓库命令 +## 1. 从镜像仓库中拉取或者更新指定镜像 docker pull +- **-a :** 拉取所有 tagged 镜像 +- **–disable-content-trust :** 忽略镜像的校验,默认开启 + +```shell +docker pull mysql +``` + +## 2. 将本地的镜像上传到镜像仓库,要先登陆到镜像仓库 docker push +- **–disable-content-trust :** 忽略镜像的校验,默认开启 +```shell +docker push mysql:v2 +``` + +## 3.从Docker Hub查找镜像 docker search +- **–automated :** 只列出 automated build类型的镜像; +- **–no-trunc :** 显示完整的镜像描述; +- **-f \<过滤条件>:** 列出指定条件的镜像。 +```shell +docker search hadoop +``` +![[Snipaste_2023-03-06_09-25-52 3.png]] +每列参数说明: +- **NAME:** 镜像仓库源的名称 +- **DESCRIPTION:** 镜像的描述 +- **OFFICIAL:** 是否 docker 官方发布 +- **stars:** 类似 Github 里面的 star,表示点赞、喜欢的意思 +- **AUTOMATED:** 自动构建 + +# 四、本地镜像管理命令 + +## 1. 列出本地镜像 docker images +- **-a :** 列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层) +- **–digests :** 显示镜像的摘要信息 +- **-f :** 显示满足条件的镜像 +- **–format :** 指定返回值的模板文件 +- **–no-trunc :** 显示完整的镜像信息 +- **-q :** 只显示镜像ID +```shell +docker images +``` +![[Snipaste_2023-03-06_09-25-52 4.png]] + +## 2. 删除本地一个或多个镜像 docker rmi +- **-f :** 强制删除; +- **–no-prune :** 不移除该镜像的过程镜像,默认移除; +``` +强制删除本地镜像 guodong/ubuntu:v4  docker rmi -f guodong/ubuntu:v4   +``` + +## 3.将指定镜像保存成 tar 归档文件 docker save +- **-o :** 输出到的文件。 +将镜像 runoob/ubuntu:v3 生成 my_ubuntu_v3.tar 文档 +```shell +docker save -o my_ubuntu_v3.tar runoob/ubuntu:v3 +``` + +## 4.load导入使用 `docker save` 命令导出的镜像 docker load +- **–input , -i :** 指定导入的文件,代替 STDIN。 +- **–quiet , -q :** 精简输出信息。 +``` +导入镜像  docker load --input fedora.tar   +``` + +# 五、基础版本信息 + +## 1.显示 Docker 系统信息,包括镜像和容器数。 docker info +![[Snipaste_2023-03-06_09-25-52 5.png]] + +## 2.显示 Docker 版本信息 docker version +![[Snipaste_2023-03-06_09-25-52 6.png]] + + +# 六、本地文件上传、下载docker +## 6.1 上传 +1. 拿到容器ID +```shell +docker ps -a +``` +2. docker cp 本地文件路径 ID全称:容器路径 +```shell +docker cp mysql-connector-java-5.1.47.jar c8af6ce9bc12:/usr/share/java +``` + +## 6.2 下载 +1. 拿到容器ID +```shell +docker ps -a +``` +2. docker cp ID全称:容器路径 本地文件路径 +```shell +docker cp c8af6ce9bc12:/usr/share/java /root +``` + diff --git a/work常用/windows 修改hosts.md b/work常用/windows 修改hosts.md new file mode 100644 index 0000000..6f0be11 --- /dev/null +++ b/work常用/windows 修改hosts.md @@ -0,0 +1,2 @@ +1、Win11 hosts文件目录:【C:\Windows\System32\drivers\etc】; +2、复制hosts文件生成新副本,在副本中进行修改后,删除原有hosts文件并将副本重命名 diff --git a/复习面试/复习计划(一个月).md b/复习面试/复习计划(一个月).md deleted file mode 100644 index a381deb..0000000 --- a/复习面试/复习计划(一个月).md +++ /dev/null @@ -1,64 +0,0 @@ -## 一、在FH项目及流程 -+ FSP/AFSP - FSP:1. 立项:规划项目目标、范围、计划、资源投入、支撑立项决策(立项PPT、用户需求说明书、可行性研究报告) - 2. 定义:对需求细化拆解、工作量评估并形成计划表(需求管理完善并评估工时) - 3. 设计:架构设计和详细设计,并开展测试设计及用例输出(概设、详设、数据库、测试用例) - 4. 开发:代码编写及自测(代码评审) - 5. 实施:上架调试、用户验收及版本发布(版本发布说明) - 6. 结项:总结项目经验(结项PPT) -+ 开发流程及项目活动 -## 二、IPDS四层架构 -#### 1. IAAS -+ KMSJ 机房 -+ 浪潮机房 -+ 分光还原 - (固网)运营商机房分光设备--->KMSJ机房还原设备--->解析出报文(报文头、报文体) - VPN破解设备 -#### 2. PAAS -+ NS (传统集群) -+ FX (容器化) -#### 3. DAAS -+ 数据接入 - 3,4G MY (124) - 固网 (111) - 其他第三方数据:网吧、医院、社采、铁公鸡、卡口、电围 - 云数据(129) - 接入工具 DS | Crust - DS用于流数据接入;Crust用于结构化数据接入 -+ 数据治理 - ![[image.png]] - pangu 数据治理工具 -+ 主题库 - 人员、车辆、人车关联、案件、人案等 -#### 4. SAAS - 全息:身份证全息、手机全息、虚拟身份全息 - 万花筒:基于报文还原文本及图片 - 图上作战:基于位置轨迹 - 全文:基于报文还原文本及图片 -## 三、JAVA基础 - -## 四、Spring基础 - -## 五、大数据基础 - -### 1.hadoop - -### 2.kafka - -### 3.hive - -### 4.hbase - -### 5.云服务平台工作流 - -## 六、数据分析 - -## 七、简历优化 - -## 八、面试目标 - -按优先级 -+ 亮风台(智慧城市) -+ 神州泰岳(GA) -+ 数美科技(阿里云大数据) -+ 美亚(GA) \ No newline at end of file diff --git a/复习面试/复习计划.md b/复习面试/复习计划.md deleted file mode 100644 index 549a191..0000000 --- a/复习面试/复习计划.md +++ /dev/null @@ -1,9 +0,0 @@ -+ 数据结构及算法 -+ Java基础、jvm、tomcat、设计模式 -+ Spring、Spring boot -+ Docker -+ kafka -+ 数据库:postgresql -+ mybatis -+ 大数据(hadoop、hive、hbase、sqoop) -+ 做项目总结 \ No newline at end of file diff --git a/复习面试/面试准备.md b/复习面试/面试准备.md deleted file mode 100644 index 0df6ccf..0000000 --- a/复习面试/面试准备.md +++ /dev/null @@ -1,35 +0,0 @@ -### 一、个人介绍 -+ 云南楚雄人、从事开发工作有5年公安行业从业4年、财政从业1年。去烽火之前是云南艾拓,主要业务早期省厅数据接入与地州科信大数据平台建设(德宏)。烽火主要在昆明市局负责网安COP15项目与定制化开发。 -+ 对公安行业的理解:1. 数据量大,用户少-->在做业务系统架构设计时不需要过于复杂 2. 硬件占合同比例高、软件偏少 3. 客户对数据分析能力要求高 4. 客户值班你值班、客户休息你不一定休息 -+ 为什么选择面试美亚:1. 都是公安行业 2. 之前在市局与省厅(滇仁杰)接触过张贵阳(开发氛围不错)、所红芬、李志兴觉得不错 -+ 未来个人规划:大数据开发与数据分析类,传统开发很快会被低代码平台替代,烽火已有自研前端快速开发平台;计划明年考PMP及postgresql认证 -### 二、离职原因 -1. 经常半夜或通宵协助客户处理疫情核查任务导致得肺炎 -2. 烽火明年没合同 -3. 与个人期望的技术路线不符。在烽火是地方研发中心与总部相差甚远,地方研发中心只相当于运维型开发。 -### 三、项目介绍 -### 四、技术面试 -1. spring - + 对IOC(控制反转)的理解:bean实例化的过程由原来的new变成-->事先创建好后放入容器,需要时再取出,并且是默认单例模式,也可以进行lazyload;bean的生命周期交由容器管理;在调用时使用注入即可@Resource和@Autowride;bean的生命周期:实例化对象-->设置对象属性-->前置处理-->后置处理-->销毁 - + 对AOP的理解:基于动态代理技术将共同调用的逻辑(日志、事务、权限控制)进行封装便于减少重复代码及耦合 - + 对SpringMvc的理解:通过贫血模型将数据、逻辑、显示分离解耦; - http请求发送-->有dispatcherServlet对请求进行分发-->HandlerMapping根据uri匹配对应的处理器-->Controller-->处理完成后通过视图解析器返回对应视图 - + Spring 中使用的设计模式: - 1. 工厂模式:Bean工厂、ApplicationContext创建bean对象 - 2. 代理模式:AOP实现 - 3. 单例模式:Spring Bean默认单例 - 4. 模板方法模式:jdbcTemplate - 5. 包装器:动态数据源 - 6. 观察者:Spring事件监听 - 7. 适配器模式:Controller - + Spring事务:注解式事务(@Transactional)及申明式事务; -1. 大数据 -2. 数据治理 -3. 缓存 -4. http请求 -### 五、期望薪资与最快入职时间 -1. 看公积金比例,如果是10%,13,不到10%,14 -2. 最快入职下周一 -### 六、对自己的规划 -1. 明年考PMP及postgresql 的认证 -2. 希望做大数据开发项目 \ No newline at end of file diff --git a/日常学习/java 常用框架/mybatis/Mybatis 工作原理.md b/日常学习/java 常用框架/mybatis/Mybatis 工作原理.md new file mode 100644 index 0000000..4e586f2 --- /dev/null +++ b/日常学习/java 常用框架/mybatis/Mybatis 工作原理.md @@ -0,0 +1,77 @@ +MyBatis的底层操作封装了JDBC的API,MyBatis的工作原理以及核心流程与JDBC的使用步骤一脉相承,MyBatis的核心对象(SqlSession,Executor)与JDBC的核心对象(Connection,Statement)相互对应。从JDBC入手并立足于JDBC,才能深入的理解MyBatis的工作原理以及核心流程。 + +# 一、Mybatis工作原理 +JDBC有四个核心对象: +(1)DriverManager,用于注册数据库连接 +(2)Connection,与数据库连接对象 +(3)Statement/PrepareStatement,操作数据库SQL语句的对象 +(4)ResultSet,结果集或一张虚拟表 + +而MyBatis也有四大核心对象: +(1)SqlSession对象,该对象中包含了执行SQL语句的所有方法【1】。类似于JDBC里面的Connection 【2】。 +(2)Executor接口,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。类似于JDBC里面的Statement/PrepareStatement。 +(3)MappedStatement对象,该对象是对映射SQL的封装,用于存储要映射的SQL语句的id、参数等信息。 +(4)ResultHandler对象,用于对返回的结果进行处理,最终得到自己想要的数据格式或类型。可以自定义返回类型。 + +在JDBC中,Connection不直接执行SQL方法,而是利用Statement或者PrepareStatement来执行方法。在使用JDBC建立了连接之后,可以使用Connection接口的createStatement()方法来获取Statement对象,也可以调用prepareStatement()方法获得PrepareStatement对象,通过executeUpdate()方法来执行SQL语句。而在MyBatis中,SqlSession对象包含了执行SQL语句的所有方法,但是它是委托Executor执行的。从某种意义上来看,MyBatis里面的SqlSession类似于JDBC中的Connection,他们都是委托给其他类去执行。 + +虽然SqlSession对象包含了执行SQL语句的所有方法,但是它同样包括了: +```java + T getMapper(Class type); +``` + +所以SqlSession也可以委托给映射器来执行数据的增删改查操作。如下代码所示: +```java + // 获得mapper接口的代理对象 + PersonMapper pm = session.getMapper(PersonMapper.class); + // 直接调用接口的方法,查询id为1的Peson数据 + Person p2 = pm.selectPersonById(1); +``` + +# 二、MyBatis的工作原理以及核心流程详解 +Mybatis的运行过程分为两大步: +- 第1步,读取配置文件缓存到Configuration对象,用于创建SqlSessionFactory; +- 第2步,SqlSession的执行过程。相对而言,SqlSessionFactory的创建还算比较容易理解,而SqlSession的执行过程就不那么简单了,它包括许多复杂的技术,要先掌握反射技术和动态代理,这里主要用到的是JDK动态代理。 +```java +public class TestMybatis { + public static void main(String[] args) { + String resource = "mybatis-config.xml"; + Reader reader = null; + try { + reader = Resources.getResourceAsReader(resource); + SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + SqlSession session = sqlSessionFactory.openSession(); + UserMapper mapper = session.getMapper(UserMapper.class); + User user = mapper.findById(2); + System.out.println("name:" + user.getName()); + session.close(); + SqlSession session1 = sqlSessionFactory.openSession(); + List users = session1.selectList("findAll"); + session1.commit(); + System.out.println("allSize:" + users.size()); + session1.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +## 2.1 mybatis 的主要构件 +从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个: +- SqlSession              作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增改查功能 +- Executor                  MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护 +- StatementHandler   封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。 +- ParameterHandler   负责对用户传递的参数转换成JDBC Statement 所需要的参数, +- ResultSetHandler    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合; +- TypeHandler            负责java数据类型和jdbc数据类型之间的映射和转换 +- MappedStatement   MappedStatement维护了一条节点的封装, +- SqlSource                负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回 +- BoundSql                 表示动态生成的SQL语句以及相应的参数信息 +- Configuration            MyBatis所有的配置信息都维持在Configuration对象之中。 + +![[Snipaste_2023-03-02_11-05-25 1.png]] + + +## 2.2 mybatis 执行过程 +![[1745215-20201204225139159-906801326 1.png]] \ No newline at end of file diff --git a/日常学习/java 常用框架/mybatis/Mybatis 缓存机制.md b/日常学习/java 常用框架/mybatis/Mybatis 缓存机制.md new file mode 100644 index 0000000..c035c17 --- /dev/null +++ b/日常学习/java 常用框架/mybatis/Mybatis 缓存机制.md @@ -0,0 +1,16 @@ +Mybatis提供了两种缓存,它们分别为:一级缓存和二级缓存。 +- 一级缓存:指的是SqlSession对象级别的缓存。当我们执行查询后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是一个HashMap,**不同的SqlSession的缓存区域是互相不受影响的**。当我们再次查询同样的数据,Mybatis会先去SqlSession的缓存区域中查询是否有,有的话直接拿出来用,没有则去数据库查询。当SqlSession对象消失后(被flush或close),Mybatis的一级缓存也就消失了。(一级缓存默认是启动的,而且是一直存在的) +- 二级缓存:指的是Mapper对象(Namspace)级别的缓存(也可以说是SqlSessionFactory对象级别的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存)。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。(二级缓存Mybatis默认是关闭的,需要自己去手动配置开启或可以自己选择用哪个厂家的缓存来作为二级缓存) + +# 一级缓存 +一级缓存是SqlSession对象级别的缓存,Mybatis会在SqlSession内部维护一个HashMap用于存储,缓存Key为hashcode+sqlid+sql,value则为查询的结果集,当执行查询时会先从缓存区域查找,如果存在则直接返回数据,否则从数据库查询,并将结果集写入缓存区。 + +## 一级缓存的清空 +以下的两个操作会导致一级缓存的清空: +1. 执行了insert、update、delete的sql语句,又或者只执行了commit操作,都会导致一级缓存失效。 + 添加、修改,删除操作不管有没有成功,只要你执行了增删改的SQL,缓存都会清空,即使没有通过commit方法提交,而二级缓存必须通过commit方法提交,才能清空缓存,因为二级缓存必须要在sqlSession关闭或者提交(commit)才能生效。 + ![[1745215-20201120153019722-1747791924.png]] +2. 手动清空,通过sqlSession.clearCache + +## 一级缓存的问题 +必须通过sqlSession进行更新,如果手动执行sql的话,mybatis无法感知,因此一般没人用mybatis缓存。 \ No newline at end of file diff --git a/日常学习/java基础/java模型/GC.md b/日常学习/java基础/java模型/GC.md new file mode 100644 index 0000000..398449e --- /dev/null +++ b/日常学习/java基础/java模型/GC.md @@ -0,0 +1,16 @@ +[jvm系列(三):GC算法 垃圾收集器 (qq.com)](https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247483952&idx=1&sn=ea12792a9b7c67baddfaf425d8272d33&chksm=ebf6da4fdc815359869107a4acd15538b3596ba006b4005b216688b69372650dbd18c0184643&cur_album_id=1326602114365276164&scene=189#wechat_redirect) +# 一、介绍 +垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。 jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。 + +# 二、对象存活判断 +判断对象是否存活一般有两种方式: +1. **引用计数**:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。 +2. **可达性分析**(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。 + +在Java语言中,GC Roots包括: +- 虚拟机栈中引用的对象。 +- 方法区中类静态属性实体引用的对象。 +- 方法区中常量引用的对象。 +- 本地方法栈中JNI引用的对象。 + +# 三、垃圾收集算法 diff --git a/日常学习/java基础/java模型/JMM-JAVA内存模型.md b/日常学习/java基础/java模型/JMM-JAVA内存模型.md new file mode 100644 index 0000000..e7d8e66 --- /dev/null +++ b/日常学习/java基础/java模型/JMM-JAVA内存模型.md @@ -0,0 +1,71 @@ +# 一、硬件内存模型 +![[1742867-20191104115215072-2033982939.jpg]] +将运算需要使用到的数据复制到缓存中,让运算能够快速进行。当运算完成之后,再将缓存中的结果写入主内存,这样运算器就不用等待主内存的读写操作了。 +每个处理器都有自己的高速缓存,同时又共同操作同一块主内存,当多个处理器同时操作主内存时,可能导致数据不一致,因此需要“缓存一致性协议”来保障。 + +# 二、java内存模型 +Java内存模型即Java Memory Model,简称JMM。**用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各平台下都能够达到一致的内存访问效果。** + +## 2.1 并发编程存在的问题: +1. 原子性,是指在一个操作中就是**cpu不可以在中途暂停然后再调度**,既不被中断操作,要么执行完成,要么就不执行。 +2. 可见性,是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 +3. 有序性,程序执行的顺序按照代码的先后顺序执行。 +**缓存一致性问题**其实就是**可见性问题**。而**处理器优化**(处理器可能会对输入代码进行乱序执行处理)是可以导致**原子性问题**的。**指令重排**(Java虚拟机的即时编译器(JIT)也会做**指令重排**)即会导致**有序性问题** + +### 2.1.1 原子性 +在Java中,为了保证原子性,提供了两个高级的字节码指令`monitorenter`和`monitorexit`。在[synchronized的实现原理](http://www.hollischuang.com/archives/1883)文章中,介绍过,这两个字节码,在Java中对应的关键字就是`synchronized`。 +因此,在Java中可以使用`synchronized`来保证方法和代码块内的操作是原子性的。 + +### 2.1.2 可见性(多线程计数器实现) +Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存作为传递媒介的方式来实现的。 +Java中的`volatile`关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。因此,可以使用`volatile`来保证多线程操作时变量的可见性。 +除了`volatile`,Java中的`synchronized`和`final`两个关键字也可以实现可见性。只不过实现方式不同,这里不再展开了。 + +### 2.1.3 有序性 +在Java中,可以使用`synchronized`和`volatile`来保证多线程之间操作的有序性。实现方式有所区别: +`volatile`关键字会禁止指令重排。`synchronized`关键字保证同一时刻只允许一条线程操作。 +好了,这里简单的介绍完了Java并发编程中解决原子性、可见性以及有序性可以使用的关键字。读者可能发现了,好像`synchronized`关键字是万能的,他可以同时满足以上三种特性,这其实也是很多人滥用`synchronized`的原因。但是`synchronized`是比较影响性能的,虽然编译器提供了很多锁优化技术,但是也不建议过度使用。 + + + +## 2.2 JMM详解 +![[1742867-20191104115215072-2033982939 1.jpg]] +JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。 + +JMM与Java内存结构并不是同一个层次的内存划分,两者基本没有关系。如果一定要勉强对应,那从变量、主内存、工作内存的定义看,主内存主要对应Java堆中的对象实例数据部分,工作内存则对应虚拟机栈的部分区域。 +![[1742867-20191104115215072-2033982939 2.jpg]] + +### 2.2.1 主内存 +主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常量、静态变量。共享数据区域,多条线程对同一个变量进行访问可能会发现线程安全问题。 + +### 2.2.2 工作内存 +要存储当前方法的所有本地变量信息(工作内存中存储着主内存中的变量副本拷贝),每个线程只能访问自己的工作内存,即线程中的本地变量对其它线程是不可见的,就算是两个线程执行的是同一段代码,它们也会各自在自己的工作内存中创建属于当前线程的本地变量,当然也包括了字节码行号指示器、相关Native方法的信息。由于工作内存是每个线程的私有数据,线程间无法相互访问工作内存,因此存储在工作内存的数据不存在线程安全问题。 + +### 2.2.3 模型简化 +JMM模型与硬件模型直接的对照关系可简化为下图: +![[1742867-20191104115215072-2033982939 3.jpg]] + +### 2.2.4 内存之间的交互操作 +线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量(隔离性),线程间变量值的传递均需要通过主内存来完成。 +![[1742867-20191104115215072-2033982939 4.jpg]] +如上图,本地内存A和B有主内存中共享变量x的副本,初始值都为0。线程A执行之后把x更新为1,存放在本地内存A中。当线程A和线程B需要通信时,线程A首先会把本地内存中x=1值刷新到主内存中,主内存中的x值变为1。随后,线程B到主内存中去读取更新后的x值,线程B的本地内存的x值也变为了1。 + +举个例子:两个人(可以理解为2个线程),向SVN提交一个相同的类,但是内容不同。先提交的人SVN回复OK。后提交的人,SVN显示OUT OF DATE,此时后提交的人需要先更新自己的代码(基于前人的基础)然后再提交。 + +在此交互过程中,Java内存模型定义了8种操作来完成,虚拟机实现必须保证每一种操作都是原子的、不可再拆分的(double和long类型例外)。 +- lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。 +- unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。 +- read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。 +- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。 +- use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。 +- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。 +- store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。 +- write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。 + +### 2.2.5 半个变量 +ava内存模型要求lock,unlock,read,load,assign,use,store,write这8个操作都具有原子性,但对于64位的数据类型(long或double),在模型中定义了一条相对宽松的规定,允许虚拟机将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作来进行,即允许虚拟机实现选择可以不保证64位数据类型的load,store,read,write这4个操作的原子性,即long和double的非原子性协定。 +如果多线程的情况下double或long类型并未声明为volatile,可能会出现“半个变量”的数值,也就是既非原值,也非修改后的值。 +虽然Java规范允许上面的实现,但商用虚拟机中基本都采用了原子性的操作,因此在日常使用中几乎不会出现读取到“半个变量”的情况。 + +### 2.2.6 总结 +JMM与COW(copy-on-write)机制很像,都从统一内存(存储)读取,当需要修改的时候复制一份到自己本地并副本进行修改。 \ No newline at end of file diff --git a/日常学习/java基础/java模型/JVM.md b/日常学习/java基础/java模型/JVM.md new file mode 100644 index 0000000..829ac21 --- /dev/null +++ b/日常学习/java基础/java模型/JVM.md @@ -0,0 +1,111 @@ +# 一、类的加载介绍 +类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 `java.lang.Class`对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的 `Class`对象, `Class`对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。 +![[640.jpg]] +类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。 + +# 二、类的生命周期 +![[Snipaste_2023-02-27_15-13-44 1.png]] +其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。 + +## 2.1 加载 +查找并加载类的二进制数据加载时类加载过程的第一个阶段,在加载阶段,虚拟机需要完成以下三件事情: +1. 通过一个类的全限定名来获取其定义的二进制字节流 +2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 +3. 在Java堆中生成一个代表这个类的 `java.lang.Class`对象,作为对方法区中这些数据的访问入口 + +## 2.2 连接 + +### 2.2.1 验证 - 确保被加载的类的正确性 +目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作: +1. **文件格式验证**:验证字节流是否符合Class文件格式的规范;例如:是否以 `0xCAFEBABE`开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。 +2. **元数据验证**:对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了 `java.lang.Object`之外。 +3. **字节码验证**:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。 +4. **符号引用验证**:确保解析动作能正确执行。 + +### 2.2.2 准备 - 为类的 `静态变量分`配内存,并将其初始化为默认值 +准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意: +1. 这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。 +2. 这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。 +### 2.2.3 解析 - 把类中的符号引用转换为直接引用 +解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。 +直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。 + +## 2.3 初始化 +初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式: +1. 声明类变量是指定初始值 +2. 使用静态代码块为类变量指定初始值 +**JVM初始化步骤** +1. 假如这个类还没有被加载和连接,则程序先加载并连接该类 +2. 假如该类的直接父类还没有被初始化,则先初始化其直接父类 +3. 假如类中有初始化语句,则系统依次执行这些初始化语句 + +**类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:** +- 创建类的实例,也就是new的方式 +- 访问某个类或接口的静态变量,或者对该静态变量赋值 +- 调用类的静态方法 +- 反射(如 `Class.forName(“com.shengsiyuan.Test”)`) +- 初始化某个类的子类,则其父类也会被初始化 +- Java虚拟机启动时被标明为启动类的类( `JavaTest`),直接使用 `java.exe`命令来运行某个主类 + +## 2.4 结束生命周期 +在如下几种情况下,Java虚拟机将结束生命周期: +- 执行了 `System.exit()`方法 +- 程序正常执行结束 +- 程序在执行过程中遇到了异常或错误而异常终止 +- 由于操作系统出现错误而导致Java虚拟机进程终止 + +# 三、类的加载方式 +类加载有三种方式: +1. 命令行启动应用时候由JVM初始化加载 +2. 通过Class.forName()方法动态加载 +3. 通过ClassLoader.loadClass()方法动态加载 + +**Class.forName()和ClassLoader.loadClass()区别**(是否执行static块): +- `Class.forName()`:将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块; +- `ClassLoader.loadClass()`:只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。 +- `Class.forName(name,initialize,loader)`带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象。 + +# 四、双亲委派 +双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。 +![[Snipaste_2023-02-28_13-09-43 4.png]] +双亲委派机制: +- 1、当 `AppClassLoader`加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器`ExtClassLoader`去完成。 +- 2、当 `ExtClassLoader`加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader```去完成。 +- 3、如果 `BootStrapClassLoader`加载失败(例如在 `$JAVA_HOME/jre/lib`里未查找到该class),会使用 `ExtClassLoader`来尝试加载; +- 4、若ExtClassLoader也加载失败,则会使用 `AppClassLoader`来加载,如果 `AppClassLoader`也加载失败,则会报出异常 `ClassNotFoundException`。 + +# 五、JVM 内存结构(堆、方法区、栈) +![[640 1.jpg]] + +JVM内存结构主要有三大块:**堆内存**、**方法区**和**栈**。堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,**Eden空间**、**From Survivor空间**、**To Survivor空间**,默认情况下年轻代按照**8:1:1**的比例来分配; + +## 5.1 堆(Heap) +对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中**最大**的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,**几乎所有的对象实例都在这里分配内存**。 +Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“**GC堆**”。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:**新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。** +根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。 +如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。 + +控制参数 +- -Xms设置堆的最小空间大小。 +- -Xmx设置堆的最大空间大小。 +- -XX:NewSize设置新生代最小空间大小。 +- -XX:MaxNewSize设置新生代最大空间大小。 +- -Xss设置每个线程的堆栈大小。 + +## 5.2 方法区或永久代(Method Area) +方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,**它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。**虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。对于习惯在HotSpot虚拟机上开发和部署程序的开发者来说,很多人愿意把方法区称为“永久代”。根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。 + +- -XX:PermSize设置永久代最小空间大小。 +- -XX:MaxPermSize设置永久代最大空间大小。 + +## 5.3 栈(Stacks) +Java虚拟机栈(Java Virtual Machine Stacks)是线程私有的,**它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:**每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。**每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。** +局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。 +在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。 + +## 5.4 出现OOM的情况 +1. `Exception in thread “main”: java.lang.OutOfMemoryError: Java heap space` 原因:对象不能被分配到堆内存中 +2. 1. `Exception in thread “main”: java.lang.OutOfMemoryError: PermGen space` 原因:类或者方法不能被加载到老年代。它可能出现在一个程序加载很多类的时候,比如引用了很多第三方的库; +3. 1. `Exception in thread “main”: java.lang.OutOfMemoryError: Requested array size exceeds VM limit` 原因:创建的数组大于堆内存的空间 +4. 1. `Exception in thread “main”: java.lang.OutOfMemoryError: request bytes for . Out of swap space?` 原因:分配本地分配失败。JNI、本地库或者Java虚拟机都会从本地堆中分配内存空间。 +5. 1. `Exception in thread “main”: java.lang.OutOfMemoryError: (Native method)` 原因:同样是本地方法内存分配失败,只不过是JNI或者本地方法或者Java虚拟机发现 \ No newline at end of file diff --git a/日常学习/java基础/并发编程/CompletionService.md b/日常学习/java基础/并发编程/CompletionService.md new file mode 100644 index 0000000..332a997 --- /dev/null +++ b/日常学习/java基础/并发编程/CompletionService.md @@ -0,0 +1,21 @@ +内部通过阻塞队列+FutureTask,实现了**任务先完成可优先获取到,即结果按照完成先后顺序排序。** + +````java +// 创建线程池 +ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE); +CompletionService cService = new ExecutorCompletionService<>(pool); + +// 向里面扔任务 +for (int i = 0; i < TOTAL_TASK; i++) { + //这边不是直接通过线程池来提交任务,而是通过CompletionService来提交 + cService.submit(new WorkTask("ExecTask" + i)); +} + +// 检查线程池任务执行结果 +for (int i = 0; i < TOTAL_TASK; i++) { + //同样也是从CompletionService来取出结果,因为CompletionService内部实现通过一个BlockingQueue来保存已经完成的结果。 + int sleptTime = cService.take().get(); + System.out.println(" slept "+sleptTime+" ms ..."); + count.addAndGet(sleptTime); +} +```` diff --git a/日常学习/java基础/并发编程/ThreadLocal.md b/日常学习/java基础/并发编程/ThreadLocal.md new file mode 100644 index 0000000..72abbdc --- /dev/null +++ b/日常学习/java基础/并发编程/ThreadLocal.md @@ -0,0 +1,132 @@ +通过学习JMM明白了主内存(共享)与工作内存,ThreadLocal存在于工作内存中,存在线程隔离特性,其他线程无法访问。通俗点说就是线程的私有变量。 +```JAVA +public class ThreadLocalTest02 { + + public static void main(String[] args) { + + ThreadLocal local = new ThreadLocal<>(); + + IntStream.range(0, 10).forEach(i -> new Thread(() -> { + local.set(Thread.currentThread().getName() + ":" + i); + System.out.println("线程:" + Thread.currentThread().getName() + ",local:" + local.get()); + }).start()); + } +} +``` +```text +输出结果: +线程:Thread-0,local:Thread-0:0 +线程:Thread-1,local:Thread-1:1 +线程:Thread-2,local:Thread-2:2 +线程:Thread-3,local:Thread-3:3 +线程:Thread-4,local:Thread-4:4 +线程:Thread-5,local:Thread-5:5 +线程:Thread-6,local:Thread-6:6 +线程:Thread-7,local:Thread-7:7 +线程:Thread-8,local:Thread-8:8 +线程:Thread-9,local:Thread-9:9 +``` +从结果可以看到,每一个线程都有自己的local 值,这就是TheadLocal的基本使用 。 + +使用场景也是很丰富。 +- 1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。 +- 2、线程间数据隔离 +- 3、进行事务操作,用于存储线程事务信息。 +- 4、数据库连接,`Session`会话管理。 + +**ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景** + +# 例 +[这才是 Thread Local 的正确原理与适用场景 根本没有内存泄漏 | 技术世界 | java,thread local,java 8,CAS,多线程,并发,技术世界,郭俊 Jason (jasongj.com)](http://www.jasongj.com/java/threadlocal/) +对于 Java Web 应用而言,Session 保存了很多信息。很多时候需要通过 Session 获取信息,有些时候又需要修改 Session 的信息。一方面,需要保证每个线程有自己单独的 Session 实例。另一方面,由于很多地方都需要操作 Session,存在多方法共享 Session 的需求。如果不使用 ThreadLocal,可以在每个线程内构建一个 Session实例,并将该实例在多个方法间传递。 + +```java +public class SessionHandler { + + @Data + public static class Session { + private String id; + private String user; + private String status; + } + + public Session createSession() { + return new Session(); + } + + public String getUser(Session session) { + return session.getUser(); + } + + public String getStatus(Session session) { + return session.getStatus(); + } + + public void setStatus(Session session, String status) { + session.setStatus(status); + } + + public static void main(String[] args) { + new Thread(() -> { + SessionHandler handler = new SessionHandler(); + Session session = handler.createSession(); + handler.getStatus(session); + handler.getUser(session); + handler.setStatus(session, "close"); + handler.getStatus(session); + }).start(); + } +} +``` +该方法是可以实现需求的。但是每个需要使用 Session 的地方,都需要显式传递 Session 对象,方法间耦合度较高。 + +```java +public class SessionHandler { + + public static ThreadLocal session = ThreadLocal.withInitial(() -> new Session()); + + @Data + public static class Session { + private String id; + private String user; + private String status; + } + + public String getUser() { + return session.get().getUser(); + } + + public String getStatus() { + return session.get().getStatus(); + } + + public void setStatus(String status) { + session.get().setStatus(status); + } + + public static void main(String[] args) { + new Thread(() -> { + SessionHandler handler = new SessionHandler(); + handler.getStatus(); + handler.getUser(); + handler.setStatus("close"); + handler.getStatus(); + }).start(); + } +} +``` +使用 ThreadLocal 改造后的代码,不再需要在各个方法间传递 Session 对象,并且也非常轻松的保证了每个线程拥有自己独立的实例。 + +如果单看其中某一点,替代方法很多。比如可通过在线程内创建局部变量可实现每个线程有自己的实例,使用静态变量可实现变量在方法间的共享。但如果要同时满足变量在线程间的隔离与方法间的共享,ThreadLocal再合适不过。 + + +# 内存泄漏 +ThreadLocal.ThreadLocalMap.Entry中的key是弱引用的,也即是当某个ThreadLocal对象不存在强引用时,就会被GC回收,但是value是基于强引用的,所以当key被回收,但是value还存在其他强引用时,就会出现内存的泄露情况,在最新的ThreadLocal中已经做出了修改,即在调用set、get、remove方法时,会清除key为null的Entry,但是如果不调用这些方法,仍然还是会出现内存泄漏 :),所以要养成**用完ThreadLocal对象之后及时remove的习惯**。 + +# 总结 +- ThreadLocal 并不解决线程间共享数据的问题 +- ThreadLocal 通过隐式的在不同线程内创建独立实例副本避免了实例线程安全的问题 +- 每个线程持有一个 Map 并维护了 ThreadLocal 对象与具体实例的映射,该 Map 由于只被持有它的线程访问,故不存在线程安全以及锁的问题 +- ThreadLocalMap 的 Entry 对 ThreadLocal 的引用为弱引用,避免了 ThreadLocal 对象无法被回收的问题 +- ThreadLocalMap 的 set 方法通过调用 replaceStaleEntry 方法回收键为 null 的 Entry 对象的值(即为具体实例)以及 Entry 对象本身从而防止内存泄漏 +- ThreadLocal 适用于变量在线程间隔离且在方法间共享的场景 diff --git a/日常学习/java基础/并发编程/线程池-美团文章.md b/日常学习/java基础/并发编程/线程池-美团文章.md new file mode 100644 index 0000000..b72be9a --- /dev/null +++ b/日常学习/java基础/并发编程/线程池-美团文章.md @@ -0,0 +1,141 @@ +[Java线程池实现原理及其在美团业务中的实践 - 美团技术团队 (meituan.com)](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html) +[动态线程池)](https://dynamictp.cn/) + +# 一、线程池 +## 1.1 什么是线程池 +线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。 +线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。 +而本文描述线程池是JDK中提供的ThreadPoolExecutor类。 +当然,使用线程池可以带来一系列好处: +- **降低资源消耗**:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。 +- **提高响应速度**:任务到达时,无需等待线程创建即可立即执行。 +- **提高线程的可管理性**:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。 +- **提供更多更强大的功能**:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。 + + +## 1.2 线程池解决的问题是什么 +线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题: +1. 频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。 +2. 对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。 +3. 系统无法合理管理内部的资源分布,会降低系统的稳定性。 +为解决资源分配这个问题,线程池采用了“池化”(Pooling)思想。池化,顾名思义,是为了最大化收益并最小化风险,而将资源统一在一起管理的一种思想。 +“池化”思想不仅仅能应用在计算机领域,在金融、设备、人员管理、工作管理等领域也有相关的应用。 +在计算机领域中的表现为:统一管理IT资源,包括服务器、存储、和网络资源等等。通过共享资源,使用户在低投入中获益。除去线程池,还有其他比较典型的几种使用策略包括: +1. 内存池(Memory Pooling):预先申请内存,提升申请内存速度,减少内存碎片。 +2. 连接池(Connection Pooling):预先申请数据库连接,提升申请连接的速度,降低系统的开销。 +3. 实例池(Object Pooling):循环使用对象,减少资源在初始化和释放时的昂贵损耗。 + +# 二、线程池核心设计与实现 ThreadPoolExecutor类 +Java中的线程池核心实现类是ThreadPoolExecutor,我们首先来看一下ThreadPoolExecutor的UML类图,了 +解下ThreadPoolExecutor的继承关系。 +![[912883e51327e0c7a9d753d11896326511272.png]] + +ThreadPoolExecutor实现的顶层接口是Executor,顶层接口Executor提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。ExecutorService接口增加了一些能力:(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;(2)提供了管控线程池的方法,比如停止线程池的运行。AbstractExecutorService则是上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。最下层的实现类ThreadPoolExecutor实现最复杂的运行部分,ThreadPoolExecutor将会一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。 + +## 2.1 运行机制 +![[912883e51327e0c7a9d753d11896326511272 1.png]] + +线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。线程池的运行主要分成两部分:任务管理、线程管理。任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:(1)直接申请线程执行该任务;(2)缓冲到队列中等待线程执行;(3)拒绝该任务。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。 + +## 2.2 生命周期管理 +线程池运行的状态,并不是用户显式设置的,而是伴随着线程池的运行,由内部来维护。线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量 (workerCount)。在具体实现中,线程池将运行状态(runState)、线程数量 (workerCount)两个关键参数的维护放在了一起,如下代码所示: +```java +private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); +``` +`ctl`这个AtomicInteger类型,是对线程池的运行状态和线程池中有效线程的数量进行控制的一个字段, 它同时包含两部分的信息:线程池的运行状态 (runState) 和线程池内有效线程的数量 (workerCount),高3位保存runState,低29位保存workerCount,两个变量之间互不干扰。用一个变量去存储两个值,可避免在做相关决策时,出现不一致的情况,不必为了维护两者的一致,而占用锁资源。通过阅读线程池源代码也可以发现,经常出现要同时判断线程池运行状态和线程数量的情况。线程池也提供了若干方法去供用户获得线程池当前的运行状态、线程个数。这里都使用的是位运算的方式,相比于基本运算,速度也会快很多。 + +关于内部封装的获取生命周期状态、获取线程池线程数量的计算方法如以下代码所示: +```java +private static int runStateOf(int c) { return c & ~CAPACITY; } //计算当前运行状态 private static int workerCountOf(int c) { return c & CAPACITY; } //计算当前线程数量 private static int ctlOf(int rs, int wc) { return rs | wc; } //通过状态和线程数生成ctl +``` + +### 2.2.1 ThreadPoolExecutor的运行状态 +| 运行状态 | 状态描述 | +| -------- | ---------------------------------------------------------------------- | +| RUNNING | 能接受新提交的任务,并且也能处理阻塞队列中的任务 | +| SHUTDOWN | 关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务 | +| STOP | 不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程 | +| TIDYING | 所有的任务都已终止了,workerCount(有效线程数)为0 | +| TERMINATED| 在terminated() 方法执行完后进入该状态 | + +![[912883e51327e0c7a9d753d11896326511272 2.png]] + +## 2.3 任务执行机制 + +### 2.3.1 任务调度 +任务调度是线程池的主要入口,当用户提交了一个任务,接下来这个任务将如何执行都是由这个阶段决定的。了解这部分就相当于了解了线程池的核心运行机制。 + +首先,所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下: + +1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。 +2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。 +3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。 +4. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。 +5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。 + +![[912883e51327e0c7a9d753d11896326511272 3.png]] + +### 2.3.2 任务队列 +任务缓冲模块是线程池能够管理任务的核心部分。线程池的本质是对任务和线程的管理,而做到这一点最关键的思想就是将任务和线程两者解耦,不让两者直接关联,才可以做后续的分配工作。线程池中是以生产者消费者模式,通过一个阻塞队列来实现的。阻塞队列缓存任务,工作线程从阻塞队列中获取任务。 + +阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。 + +![[912883e51327e0c7a9d753d11896326511272 4.png]] + +| 名称 | 描述 | | +| ------------------- | ------------------------------------------------------------------------------------------------ | --- | +| ArrayBlockingQueue | 由数组实现的有界阻塞队列,按照先进先出规则进行排序。支持公平锁和非公平锁 | | +| LinkedBlockingQueue | 由链表组成的有界队列,按照先进先出规则进行排序,默认长度为Integer.MAX_VALUE,默认创建下有容量危险 | | + +### 2.3.3 任务申请 +任务的执行有两种可能:一种是任务直接由新创建的线程执行。另一种是线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行。第一种情况仅出现在线程初始创建的时候,第二种是线程获取任务绝大多数的情况。 +![[912883e51327e0c7a9d753d11896326511272 5.png]] + +### 2.3.4 任务拒绝 +任务拒绝模块是线程池的保护部分,线程池有一个最大的容量,当线程池的任务缓存队列已满,并且线程池中的线程数目达到maximumPoolSize时,就需要拒绝掉该任务,采取任务拒绝策略,保护线程池。 +拒绝策略是一个接口,其设计如下: +```java +public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ThreadPoolExecutor executor); } +``` + +用户可以通过实现这个接口去定制拒绝策略,也可以选择JDK提供的四种已有拒绝策略: +| 名称 | 描述 | +| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| ThreadPoolExecutor.AbortPolicy | 丢弃并抛出RejectedExecutionException异常。这是线程池的默认策略。如果是比较关键的业务,推荐使用,在系统不能承载更大的并发量的时候,能够及时发现 | +| ThreadPoolExecutor.DiscardPolicy | 丢弃任务,,但是不抛异常。建议是一些无关紧要的业务采用此策略 | +| ThreadPoolExecutor.DiscardOldestPolicy | 丢弃队列最前面的任务,然后重新提交被拒绝策略。需要根据实际业务中是否允许丢弃老任务来权衡 | +| ThreadPoolExecutor.CallerRunsPolicy | 由调用线程(提交任务的线程)处理该任务。这种情况是需要让所有任务都执行完毕,适合大量的计算任务,最终必须要让每个任务都执行完毕 | + + +## 2.4 Worker +线程池为了掌握线程的状态并维护线程的生命周期,设计了线程池内的工作线程Worker。 +```java +private final class Worker extends AbstractQueuedSynchronizer implements Runnable{ final Thread thread;//Worker持有的线程 Runnable firstTask;//初始化的任务,可以为null } +``` + +Worker这个工作线程,实现了Runnable接口,并持有一个线程thread,一个初始化的任务firstTask。thread是在调用构造方法时通过ThreadFactory来创建的线程,可以用来执行任务;firstTask用它来保存传入的第一个任务,这个任务可以有也可以为null。如果这个值是非空的,那么线程就会在启动初期立即执行这个任务,也就对应核心线程创建时的情况;如果这个值是null,那么就需要创建一个线程去执行任务列表(workQueue)中的任务,也就是非核心线程的创建。 + +Worker执行任务的模型如下图所示: +![[912883e51327e0c7a9d753d11896326511272 6.png]] + +线程池需要管理线程的生命周期,需要在线程长时间不运行的时候进行回收。线程池使用一张Hash表去持有线程的引用,这样可以通过添加引用、移除引用这样的操作来控制线程的生命周期。这个时候重要的就是如何判断线程是否在运行。 + +​Worker是通过继承AQS,使用AQS来实现独占锁这个功能。没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态。 + +1.lock方法一旦获取了独占锁,表示当前线程正在执行任务中。 2.如果正在执行任务,则不应该中断线程。 3.如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断。 4.线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;如果线程是空闲状态则可以安全回收。 + +![[912883e51327e0c7a9d753d11896326511272 7.png]] + +### 2.4.1  Worker线程增加 +增加线程是通过线程池中的addWorker方法,该方法的功能就是增加一个线程,该方法不考虑线程池是在哪个阶段增加的该线程,这个分配线程的策略是在上个步骤完成的,该步骤仅仅完成增加线程,并使它运行,最后返回是否成功这个结果。addWorker方法有两个参数:firstTask、core。firstTask参数用于指定新增的线程执行的第一个任务,该参数可以为空;core参数为true表示在新增线程时会判断当前活动线程数是否少于corePoolSize,false表示新增线程前需要判断当前活动线程数是否少于maximumPoolSize,其执行流程如下图所示: +![[912883e51327e0c7a9d753d11896326511272 8.png]] + + + +### 2.4.2 Worker线程执行任务 + +在Worker类中的run方法调用了runWorker方法来执行任务,runWorker方法的执行过程如下: +1.while循环不断地通过getTask()方法获取任务。 2.getTask()方法从阻塞队列中取任务。 3.如果线程池正在停止,那么要保证当前线程是中断状态,否则要保证当前线程不是中断状态。 4.执行任务。 5.如果getTask结果为null则跳出循环,执行processWorkerExit()方法,销毁线程。 + +![[912883e51327e0c7a9d753d11896326511272 9.png]] + diff --git a/日常学习/java基础/并发编程/线程池.md b/日常学习/java基础/并发编程/线程池.md new file mode 100644 index 0000000..45900de --- /dev/null +++ b/日常学习/java基础/并发编程/线程池.md @@ -0,0 +1,72 @@ +线程的创建与销毁需要依赖操作系统,其代价是比较高昂的,频繁地创建与销毁线程对系统性能影响较大。 +出于线程管理的需要,线程池应运而生。线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理。使用线程池的好处在于: +- 降低资源消耗:线程池通常会维护一些线程(数量为 corePoolSize),这些线程被重复使用来执行不同的任务,任务完成后不会销毁。在待处理任务量很大的时候,通过对线程资源的复用,避免了线程的频繁创建与销毁,从而降低了系统资源消耗。 +- 提高响应速度:由于线程池维护了一批 alive 状态的线程,当任务到达时,不需要再创建线程,而是直接由这些线程去执行任务,从而减少了任务的等待时间。 +- 提高线程的可管理性:使用线程池可以对线程进行统一的分配,调优和监控。 + +# 一、java中提供的线程池 + +## 1.1 Executor +在Java中,线程池是由Executor框架实现的,Executor是最顶层的接口定义,其子类和实现类包括:**ExecutorService,ScheduledExecutorService,ThreadPoolExecutor,ScheduledThreadPoolExecutor,ForkJoinPool**等。 +1. **Executor**:Executor是一个接口,只定义了一个execute()方法(void execute(Runnable command);),只能提交Runnable形式的任务,不支持提交Callable带有返回值的任务。 +2. **ExecutorService**:ExecutorService在Executor的基础上加入了线程池的生命周期管理,可以通过shutdown或者shutdownNow方法来关闭线程池,关于这两个方法后文有详细说明。ExecutorService支持提交Callable形式的任务,提交完Callable任务后拿到一个Future(代表一个异步任务执行的结果)。 +3. **ThreadPoolExecutor**:是线程池中最核心的类,后面有详细说明。 +4. **ScheduledThreadPoolExecutor**:ThreadPoolExecutor子类,它在ThreadPoolExecutor基础上加入了任务定时执行的功能。 + + +## 1.2 线程池的创建 +Executors中提供了一系列静态方法创建线程池: +- **newSingleThreadExecutor**:一个单线程的线程池。如果因异常结束,会再创建一个新的,保证按照提交顺序执行。 +- **newFixedThreadPool**:创建固定大小的线程池。根据提交的任务逐个增加线程,直到最大值保持不变。如果因异常结束,会新创建一个线程补充。 newCachedThreadPool:创建一个可缓存的线程池。会根据任务自动新增或回收线程。 +- **newScheduledThreadPool**:支持定时以及周期性执行任务的需求。 +- **newWorkStealingPool**:JDK8新增,根据所需的并行层次来动态创建和关闭线程,通过使用多个队列减少竞争,底层使用ForkJoinPool来实现。优势在于可以充分利用多CPU,把一个任务拆分成多个“小任务”,放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。 +- newCachedThreadPool:缓存线程池 + +![[Snipaste_2023-02-28_11-48-27.png]] + + +如下图所示,这些线程池都大部分是由ThreadPoolExecutor构造而成 +![[Snipaste_2023-02-28_11-48-27 1.png]] + +## 1.3 ThreadPoolExecutor +![[Snipaste_2023-02-28_13-09-43.png]] + +### corePoolSize +线程池容量(初始化线程数),这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法(从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程)。当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。 + +### maximumPoolSize +线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程。 + +### keepAliveTime +表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。 + +### unit +参数keepAliveTime的时间单位 + +### workQueue +工作队列,一般选择有界队列防止内存溢出 +1. **ArrayBlockingQueue**:基于数组结构的有界阻塞队列,按FIFO排序任务; +2. **LinkedBlockingQuene**:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene; +3. **SynchronousQuene**:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene; +4. **priorityBlockingQuene**:具有优先级的无界阻塞队列; + +### threadFactory + +### handler +表示当拒绝处理任务时的策略 +1. **ThreadPoolExecutor.AbortPolicy**:丢弃任务并抛出RejectedExecutionException异常。 +2. **ThreadPoolExecutor.DiscardPolicy**:也是丢弃任务,但是不抛出异常。 +3. **ThreadPoolExecutor.DiscardOldestPolicy**:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) +4. **ThreadPoolExecutor.CallerRunsPolicy**:由调用线程处理该任务 + + +## 1.4 线程池的关闭 +ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中: +- **shutdown()**:不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务 +- **shutdownNow()**:立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务 + +## 1.5 线程池容量的动态调整 +ThreadPoolExecutor提供了动态调整线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(), +- **setCorePoolSize**:设置核心池大小 +- **setMaximumPoolSize**:设置线程池最大能创建的线程数目大小 +当上述参数从小变大时,ThreadPoolExecutor进行线程赋值,还可能立即创建新的线程来执行任务。 \ No newline at end of file diff --git a/日常学习/java基础/并发编程/锁.md b/日常学习/java基础/并发编程/锁.md new file mode 100644 index 0000000..75aa61b --- /dev/null +++ b/日常学习/java基础/并发编程/锁.md @@ -0,0 +1,293 @@ +# 一、synchronized +synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: +1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; +```java +class SyncLock implements Runnable{ + @Override + public void run() { + synchronized (this){ + for (int i = 0; i < 5; i++) { + try { + System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++)); + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } +} +``` +synchronized (this)锁定的是当前对象,如果new一个其他对象的话,会生成新锁。 + +2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; +```java +public synchronized void run() { + { + for (int i = 0; i < 5; i++) { + try { + System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++)); + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } +``` +在用synchronized修饰方法时要注意以下几点: ++ synchronized关键字不能继承 ++ 在定义接口方法时不能使用synchronized关键字 ++ 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步 + +3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象; +静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。 +```java +class SyncThread implements Runnable { + private static int count; + + public SyncThread() { + count = 0; + } + + public synchronized static void method() { + for (int i = 0; i < 5; i ++) { + try { + System.out.println(Thread.currentThread().getName() + ":" + (count++)); + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public synchronized void run() { + method(); + } +} +``` +假如创建了2个SyncThread对象,但是由于有静态方法锁且锁属于这个类,无论创建多少对象,run()方法的锁依然生效。 + +4. 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。 +```java +class SyncThread implements Runnable { + private static int count; + + public SyncThread() { + count = 0; + } + + public static void method() { + synchronized(SyncThread.class) { + for (int i = 0; i < 5; i ++) { + try { + System.out.println(Thread.currentThread().getName() + ":" + (count++)); + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + public synchronized void run() { + method(); + } +} +``` +效果与修饰静态方法相同 + + +# 二、volatile +不能用于自增操作,例如count ++ +作用如下: +1. 保证可见性,不保证原子性 + 当写一个volatile变量时,JMM会把该线程本地内存中的变量强制刷新到主内存中去; + 这个写会操作会导致其他线程中的volatile变量缓存无效。 +2. 禁止指令重排 + 重排序在单线程下一定能保证结果的正确性,但是在多线程环境下,可能发生重排序,影响结果 + +假设线程A先执行writer方法,线程B随后执行reader方法,初始时线程的本地内存中flag和a都是初始状态,下图是线程A执行volatile写后的状态图。 +![[Snipaste_2023-02-28_13-09-43 2.png]] + +当volatile变量写后,线程中本地内存中共享变量就会置为失效的状态,因此线程B再需要读取从主内存中去读取该变量的最新值。下图就展示了线程B读取同一个volatile变量的内存变化示意图。 +![[Snipaste_2023-02-28_13-09-43 3.png]] + +从横向来看,线程A和线程B之间进行了一次通信,线程A在写volatile变量时,实际上就像是给B发送了一个消息告诉线程B你现在的值都是旧的了,然后线程B读这个volatile变量时就像是接收了线程A刚刚发送的消息。既然是旧的了,那线程B该怎么办了?自然而然就只能去主内存去取啦。 + +# 三、ReentrantLock +```java +public class ReentrantLockDemo { + // 实例化一个非公平锁,构造方法的参数为true表示公平锁,false为非公平锁。 + private final ReentrantLock lock = new ReentrantLock(false); + private int i; + + public void testLock() { + // 拿锁,如果拿不到会一直等待 + lock.lock(); + try { + // 再次尝试拿锁(可重入),拿锁最多等待100毫秒 + if (lock.tryLock(100, TimeUnit.MILLISECONDS)) + i++; + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + // 释放锁 + lock.unlock(); + lock.unlock(); + } + } +} +``` +代码中创建了非公平锁,lock.lock()会进行拿锁操作,如果拿不到锁则会一直等待。如果拿到锁之后则会执行try代码块中的代码。接下来在try代码块中又通过tryLock(100, TimeUnit.MILLISECONDS)方法尝试再次拿锁,此时,拿锁最多会等待100毫秒,如果在100毫秒内能获得锁,则tryLock方法返回true,拿锁成功,执行i++操作,如果返回false,获取锁失败,i++不会被执行。(因为第一次线程已经拿到锁了,由于ReentrantLock是可重入,因此,第二次必定能拿到锁。此处仅仅为了演示ReetranLock的使用,不必纠结)。 +另外,要注意被ReentrantLock加锁区域必须用try代码块包裹,且释放锁需要在finally中来避免死锁。执行几次加锁,就需要几次释放锁。 + +## 3.1 公平与非公平 +**公平锁**是指多个线程按照申请锁的顺序来获取锁,线程直接进入同步队列中排队,队列中最先到的线程先获得锁。**非公平锁**是多个线程加锁时每个线程都会先去尝试获取锁,如果刚好获取到锁,那么线程无需等待,直接执行,如果获取不到锁才会被加入同步队列的队尾等待执行。 +公平锁的优点在于各个线程公平平等,每个线程等待一段时间后,都有执行的机会,而它的缺点相较于于非公平锁整体执行速度更慢,吞吐量更低。同步队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大。 +而非公平锁非公平锁的优点是可以减少唤起线程的开销,整体的吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程。它的缺点呢也比较明显,即队列中等待的线程可能一直或者长时间获取不到锁。 + +## 3.2 可重入与非可重入 +可重入锁不会阻塞 +**可重入锁**又名递归锁,是指同一个线程在获取外层同步方法锁的时候,再进入该线程的内层同步方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。**非可重入锁**与可重入锁是对立的关系,即一个线程在获取到外层同步方法锁后,再进入该方法的内层同步方法无法获取到锁,即使锁是同一个对象。 +synchronized与本篇讲的ReentrantLock都属于可重入锁。可重入锁可以有效避免死锁的产生。 + +# 四、CAS与原子类 + +原子操作类,指的是java.util.concurrent.atomic包下,一系列以Atomic开头的包装类。例如`AtomicBoolean`,`AtomicInteger`,`AtomicLong`。它们分别用于`Boolean`,`Integer`,`Long`类型的原子性操作。它们都是基于CAS实现。 + +## 4.1 CAS机制 +CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。 +CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。 +更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。 + +用伪代码解释就是: +if(V=A){ ----- 比较相等 + swap() ----- 替换旧值,此时V=B,A=B,新的B需要到下一次修改 +}else { + retry() ----- 重试(自旋) +} + +在 Java 中,Java 并没有直接实现 CAS,CAS 相关的实现是通过 C++ 内联汇编的形式实现的。Java 代码需通过 JNI 才能调用。CAS 是一条 CPU 的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,Unsafe 提供的 CAS 方法(如compareAndSwapXXX)底层实现即为 CPU 指令 cmpxchg。 +![[Snipaste_2023-02-28_13-09-43 5.png]] +```cpp +inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { + // alternative for InterlockedCompareExchange + int mp = os::is_MP(); + __asm { + mov edx, + dest mov ecx, + exchange_value mov eax, + compare_value LOCK_IF_MP(mp) + cmpxchg dword ptr [edx], ecx + } + } +``` +如上面源代码所示,程序会根据当前处理器的类型来决定是否为cmpxchg指令添加lock前缀。如果程序是在多处理器上运行,就为cmpxchg指令加上lock前缀(Lock Cmpxchg)。反之,如果程序是在单处理器上运行,就省略lock前缀(单处理器自身会维护单处理器内的顺序一致性,不需要lock前缀提供的内存屏障效果)。 + +## 4.2 示例 +1. 在内存地址V中,存储着值为10的变量 +![[5630287-350bc3c474eef0e8.jpg]] +2. 此时线程1想要把变量的值增加1。对线程1来说,旧的预期值A=10,要修改的新值B=11。 +![[5630287-350bc3c474eef0e8 1.jpg]] +3. 在线程1要提交更新之前,另一个线程2抢先一步,把内存地址V中的变量值率先更新成了11。 +![[5630287-350bc3c474eef0e8 2.jpg]] +4. 线程1开始提交更新,首先进行A和地址V的实际值比较(Compare),发现A不等于V的实际值,提交失败。 +![[5630287-350bc3c474eef0e8 3.jpg]] +5. 线程1重新获取内存地址V的当前值,并重新计算想要修改的新值。此时对线程1来说,A=11,B=12。这个重新尝试的过程被称为自旋。 +![[5630287-f638cadea7b6cb96.webp]] +6. 这一次比较幸运,没有其他线程改变地址V的值。线程1进行Compare,发现A和地址V的实际值是相等的 +![[5630287-0a3d0b3926366d63.jpg]] +7. 线程1进行SWAP,把地址V的值替换为B,也就是12。 +![[5630287-0a3d0b3926366d63 1.jpg]] + +## 4.3 优缺点 + +#### 优点: +- 可以保证变量操作的原子性; +- 并发量不是很高的情况下,使用CAS机制比使用锁机制效率更高; +- 在线程对共享资源占用时间较短的情况下,使用CAS机制效率也会较高。 + +#### 缺点: +1. CPU开销较大 +在并发量比较高的情况下,如果许多线程反复尝试更新(自旋锁 +)某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。 + +2. 不能保证代码块的原子性 +CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。 + +3. ABA问题 +CAS在操作的时候会检查变量的值是否被更改过,如果没有则更新值,但是带来一个问题,最开始的值是A,接着变成B,最后又变成了A。经过检查这个值确实没有修改过,因为最后的值还是A,但是实际上这个值确实已经被修改过了。为了解决这个问题,在每次进行操作的时候加上一个版本号,每次操作的就是两个值,一个版本号和某个值,A——>B——>A问题就变成了1A——>2B——>3A。在jdk中提供了AtomicStampedReference类解决ABA问题,用Pair这个内部类实现,包含两个属性,分别代表版本号和引用,在compareAndSet中先对当前引用进行检查,再对版本号标志进行检查,只有全部相等才更新值。 + +# 五、AQS +CAS中的自旋锁有2个缺点: ++ 第一个是锁饥饿问题。在锁竞争激烈的情况下,可能存在一个线程一直被其他线程”插队“而一直获取不到锁的情况。 ++ 第二是性能问题。在实际的多核上运行的自旋锁在锁竞争激烈时性能较差。 + +TASLock 和 TTASLock 与上文代码类似,都是针对**一个原子状态变量**轮询的自旋锁实现,显然,自旋锁的性能和理想情况相距甚远。这是因为自旋锁锁状态中心化,在竞争激烈的情况下,锁状态变更会导致多个 CPU 的高速缓存的频繁同步,从而拖慢 CPU 效率。 + +为了解决这2个问题就有了CLH锁,CLH是AQS的核心。 +![[721070-20170504110246211-10684485.png]] +CLH 锁是对自旋锁的一种改进,有效的解决了以上的两个缺点。首先它将线程组织成一个队列,保证先请求的线程先获得锁,避免了饥饿问题。其次锁状态去中心化,**让每个线程在不同的状态变量中自旋**,这样当一个线程释放它的锁时,只能使其后续线程的高速缓存失效,缩小了影响范围,从而减少了 CPU 的开销。 + +CLH 锁数据结构很简单,类似一个链表队列,所有请求获取锁的线程会排列在链表队列中,自旋访问队列中前一个节点的状态。当一个节点释放锁时,只有它的后一个节点才可以得到锁。CLH 锁本身有一个队尾指针 Tail,它是一个原子变量,指向队列最末端的 CLH 节点。每一个 CLH 节点有两个属性:所代表的线程和标识是否持有锁的状态变量。当一个线程要获取锁时,它会对 Tail 进行一个 getAndSet 的原子操作。该操作会返回 Tail 当前指向的节点,也就是当前队尾节点,然后使 Tail 指向这个线程对应的 CLH 节点,成为新的队尾节点。入队成功后,该线程会轮询上一个队尾节点的状态变量,当上一个节点释放锁后,它将得到这个锁。 + +AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。 + +不同的自定义同步器争用共享资源的方式也不同。**自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可**,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法: + +- isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。 +- tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。 +- tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。 +- tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。 +- tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。 + +以ReentrantLock为例,state(同步状态)初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。 + +再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。 + +## 5.1 CLH的数据结构 +CLH中的节点表示待获取锁的对象,里面包含6个方法及属性 +| 方法和属性值 | 含义 | +| ------------ | -------------------------------------------------------------------------------------------- | +| waitStatus | 当前节点在队列中的状态 | +| thread | 表示处于该节点的线程 | +| prev | 前驱指针 | +| predecessor | 返回前驱节点,没有的话抛出npe | +| nextWaiter | 指向下一个处于CONDITION状态的节点(由于本篇文章不讲述Condition Queue队列,这个指针不多介绍) | +| next | 后继指针 | + + +waitStatus有下面几个枚举值: +| 枚举 | 含义 | +| --------- | ---------------------------------------------- | +| 0 | 当一个Node被初始化的时候的默认值 | +| CANCELLED | 为1,表示线程获取锁的请求已经取消了 | +| CONDITION | 为-2,表示节点在等待队列中,节点线程等待唤醒 | +| PROPAGATE | 为-3,当前线程处在SHARED情况下,该字段才会使用 | +| SIGNAL | 为-1,表示线程已经准备好了,就等资源释放了 | + + +## 5.2 同步状态State +AQS中维护了一个名为state的字段,意为同步状态,是由Volatile修饰的,用于展示当前临界资源的获锁情况。 +| 方法名 | 描述 | +| ------------------------------------------------------------------ | -------------------- | +| protected final int getState() | 获取State的值 | +| protected final void setState(int newState) | 设置State的值 | +| protected final boolean compareAndSetState(int expect, int update) | 使用CAS方式更新State | + +可以通过修改State字段表示的同步状态来实现多线程的独占模式和共享模式(加锁过程)。 + +### 5.2.1 独占模式 +![[27605d483e8935da683a93be015713f331378.png]] +以ReentrantLock为例,state(同步状态)初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。 + +举个例子:排队打饭,只有一个窗口(state),第一个站住窗口(lock(),tryAcquire(),state+1),后一个人需要等待前人离开(unlock,state=0) + +#### 线程加入等待队列 +当其他线程tryAcquire()失败时,会调用addWaiter加入到等待队列中去。总的来说,一个线程获取锁失败了,被放入等待队列,acquireQueued会把放入队列中的线程不断去获取锁,直到获取成功或者不再需要获取(中断)。 + + +### 5.2.2 共享模式 +![[27605d483e8935da683a93be015713f331378 1.png]] +以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。 \ No newline at end of file diff --git a/日常学习/java基础/集合/ArrayDeque.md b/日常学习/java基础/集合/ArrayDeque.md index 199ab4e..88dd239 100644 --- a/日常学习/java基础/集合/ArrayDeque.md +++ b/日常学习/java基础/集合/ArrayDeque.md @@ -2,7 +2,7 @@ ArrayDeque是数组实现的双端队列,类似于LinkedList,效率高于LinkedList 同时也可以当作栈来使用 ## 二、特点 -+ ArrayDeque是 Deque接口的一个实现,使用了可变数组,所以没有容量上的限制。同时, ArrayDeque是线程不安全的,在没有外部同步的情况下,不能再多线程环境下使用。 ++ ArrayDeque是 Deque接口的一个实现,使用了可变数组,所以没有容量上的限制。同时,ArrayDeque是线程不安全的,在没有外部同步的情况下,不能再多线程环境下使用。 + ArrayDeque是 Deque的实现类,可以作为栈来使用,效率高于 Stack;也可以作为队列来使用,效率高于 LinkedList。 + ArrayDeque 是 Java 集合中双端队列的数组实现,双端队列的链表实现(LinkedList) + ArrayDeque不支持 null值。 diff --git a/日常学习/java基础/集合/CurrentHashMap.md b/日常学习/java基础/集合/CurrentHashMap.md new file mode 100644 index 0000000..e69de29 diff --git a/日常学习/java基础/集合/LinkedHashMap.md b/日常学习/java基础/集合/LinkedHashMap.md new file mode 100644 index 0000000..ea9aed1 --- /dev/null +++ b/日常学习/java基础/集合/LinkedHashMap.md @@ -0,0 +1,30 @@ +只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是**迭代HashMap的顺序并不是HashMap放置的顺序**,也就是无序。HashMap的这一缺点往往会带来困扰,因为有些场景,我们期待一个有序的Map。 +这个时候,LinkedHashMap就闪亮登场了,它虽然增加了时间和空间上的开销,但是**通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序**。**该迭代顺序可以是插入顺序或者是访问顺序。** + +LinkedHashMap可以认为是**HashMap+LinkedList**,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序。 + + +看一下LinkedHashMap的基本数据结构,也就是Entry: +```java +private static class Entry extends HashMap.Entry { + // These fields comprise the doubly linked list used for iteration. + Entry before, after; + + Entry(int hash, K key, V value, HashMap.Entry next) { + super(hash, key, value, next); + } + ... +} +``` + +列一下Entry里面有的一些属性吧: + +1、K key +2、V value +3、Entry next +4、int hash +5、Entry before +6、Entry after + +其中5、6相当于前后指针 + diff --git a/日常学习/java基础/集合/TreeMap.md b/日常学习/java基础/集合/TreeMap.md new file mode 100644 index 0000000..75955eb --- /dev/null +++ b/日常学习/java基础/集合/TreeMap.md @@ -0,0 +1 @@ +实现了红黑树,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。 \ No newline at end of file diff --git a/日常学习/spring/spring/事务失效的情况.md b/日常学习/spring/spring/事务失效的情况.md index b4092eb..b07ea03 100644 --- a/日常学习/spring/spring/事务失效的情况.md +++ b/日常学习/spring/spring/事务失效的情况.md @@ -19,8 +19,7 @@ spring是通过事务管理器了来管理事务的,一定不要忘记配置 ## 1.4 自身调用问题 -spring是通过aop的方式,对需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会 -起效。 +spring是通过aop的方式,对需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会起效。 ![[Snipaste_2023-02-20_10-02-28 16.png]] 显然不会生效,因为m1中通过this的方式调用了m2方法,而this并不是代理对象,this.m2()不会被事务 拦截器,所以事务是无效的 @@ -28,7 +27,7 @@ spring是通过aop的方式,对需要spring管理事务的bean生成了代理 ## 1.5 异常类型错误 spring事务回滚的机制:对业务方法进行try catch,当捕获到有指定的异常时,spring自动对事务进行 回滚,那么问题来了,哪些异常spring会回滚事务呢? -并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。也可以自定义回滚的异常类型. +并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。也可以自定义回滚的异常类型。 ## 1.6 异常被吞了 当业务方法抛出异常,spring感知到异常的时候,才会做事务回滚的操作,若方法内部将异常给吞了, diff --git a/日常学习/spring/spring/对Spring的理解.md b/日常学习/spring/spring/对Spring的理解.md new file mode 100644 index 0000000..517ab02 --- /dev/null +++ b/日常学习/spring/spring/对Spring的理解.md @@ -0,0 +1,21 @@ +# IOC +创建对象的过程从原来的new变为由Spring控制对象创建,并将创建好的对象统一交给容器管理。配合依赖注入实现了对象的创建与使用解耦(开发者只关心使用) + +# 上下文 +提供了操作容器的统一API入口,可以通过调用直接创建Bean、或者读取配置等 + +# AOP +利用代理机制实现横向抽象,例如@transactional和@Async 注解都是通过注解拦截器读取到被注解类,然后生成代理对象,例如事务注解,自动加入开启事务、提交事务、回滚事务。 + +# 为什么使用三级缓存 + +因为初始化bean并赋属性以及依赖关系注入不是同步的过程,而是异步完成,例如有些bean已经注入到其他bean,但是此时它的属性并没有完全赋值,导致早期bean和最终bean不一致,也是个一致性问题。所以需要使用三级缓存解决。 + +# Bean的生命周期 +1. bean元数据配置读取、解析 +2. 注册到容器 +3. bean class加载 +4. 属性赋值 +5. 初始化创建 +6. 使用 +7. 销毁 \ No newline at end of file diff --git a/日常学习/中间件/MQ/点对点与发布订阅的区别.md b/日常学习/中间件/MQ/点对点与发布订阅的区别.md new file mode 100644 index 0000000..e69de29 diff --git a/日常学习/中间件/redis/5种基础数据结构详解.md b/日常学习/中间件/redis/5种基础数据结构详解.md index 1f321e7..02f8d60 100644 --- a/日常学习/中间件/redis/5种基础数据结构详解.md +++ b/日常学习/中间件/redis/5种基础数据结构详解.md @@ -59,10 +59,10 @@ Redis字典相当于Java的HashMap,属于无序字典,在数据结构上使 #### 获取字典中的所有键值对 ![[Snipaste_2023-02-22_16-10-55 3.png]] ### 3.2 底层数据结构 -采用于HashMao相同的散列表结构,redis字段的值只能是字符串 +采用于HashMap相同的散列表结构,redis字段的值只能是字符串 ![[Snipaste_2023-02-22_16-10-55 5.png]] #### 3.2.1 rehash -Redis为了追求高性能采用了渐进式rehash策略,可以不堵塞服务,但是需要牺牲空间,可以理解为空间换时间。渐进式rehash会在执行同时保留新旧两个hash结构,查询时会同时查询两个hash,然后再后续的定时任务以及hash操作指令中,循序渐进地将旧hash的内容迁移到新hash结构,当迁移完成就使用新hash代替。 +Redis为了追求高性能采用了渐进式rehash策略,可以不堵塞服务,但是需要牺牲空间,可以理解为空间换时间。渐进式rehash会在执行同时保留新旧两个hash结构,查询时会同时查询两个hash,然后再后续的定时任务以及hash操作指令中,循序渐进地将旧hash的内容迁移到新hash结构,当迁移完成就使用新hash代替。(空间换时间) ![[Snipaste_2023-02-22_16-10-55 6.png]] ### 3.3优缺点 diff --git a/日常学习/中间件/redis/redis和数据库一致性.md b/日常学习/中间件/redis/redis和数据库一致性.md new file mode 100644 index 0000000..0353067 --- /dev/null +++ b/日常学习/中间件/redis/redis和数据库一致性.md @@ -0,0 +1,31 @@ +一致性问题多发生在数据更新阶段,目前有2种: +**(1) 先删除缓存,再更新数据库** +**(2) 先更新数据库,再删除缓存** + +# 一、为什么不能更新缓存而要删除 +因为并发情况下的执行顺序与预期不一致,会出现旧值覆盖新值的情况,如图所示 +![[Snipaste_2023-03-01_17-52-19.png]] +线程1 先发起的更新请求,过了一段时间线程2也发起更新,但是由于线程2比线程1先执行完,当线程1执行完成后会覆盖线程2更新的缓存值,就导致了库与缓存数据不一致问题。因此并发环境下不要更新缓存。 + +# 二、为什么不能先删除缓存,再更新数据库 +![[Snipaste_2023-03-01_17-52-19 1.png]] +如图所示,读请求如果没有命中缓存,会先查库再更新缓存。假如先删除缓存,再更新库,如下图所示: +![[Snipaste_2023-03-01_17-52-19 2.png]] +当写、读两个线程同时执行,由于读先执行完,读线程将缓存的val更新为a1,但是库中实际值是a2,此时出现不一致情况。 + +# 三、Cache-Aside Pattern +Cache-Aside Pattern,即旁路缓存模式,它的提出是为了尽可能地解决缓存与数据库的数据不一致问题。 +通俗的说就是先更新库,再删除缓存,理想情况下会出现极短的不一致问题。 +![[Snipaste_2023-03-01_17-52-19 3.png]] +如图所示,线程2 的查询在线程1之前完成,将缓存val设为a1,随后线程1删除缓存,当下一次查询时缓存没有命中,就会将缓存val设为a2,实现了一致性。 + +## 3.1 不一致场景 +![[Snipaste_2023-03-01_17-52-19 4.png]] +当读线程比写线程执行慢的时候又会产生不一致情况,直到缓存过期或下一次更新。因此引入了延迟双删策略。只需要保证,删除动作在写动作之后完成就能保证一致性。 + +## 3.2 延迟双删 +![[Snipaste_2023-03-01_17-52-19 6.png]] +引入了第二次删除,将要删除的Key交给定时任务或MQ,只要线程2的写缓存操作在设定的延时范围内执行完成,就能保证一致性。延迟时间需要更新业务来设置。 + +## 3.3 改进延迟双删 +假如删除缓存的动作失败了,需要加入重试机制。 \ No newline at end of file diff --git a/日常学习/中间件/tomcat/tomcat 处理一个HTTP请求的过程.md b/日常学习/中间件/tomcat/tomcat 处理一个HTTP请求的过程.md new file mode 100644 index 0000000..21ba8b3 --- /dev/null +++ b/日常学习/中间件/tomcat/tomcat 处理一个HTTP请求的过程.md @@ -0,0 +1,17 @@ +![[1079203-20190304104037017-1495719611.png]] + +1.用户在浏览器中输入网址localhost:8080/test/index.jsp,请求被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得; +2.Connector把该请求交给它所在的Service的Engine(Container)来处理,并等待Engine的回应; +3.Engine获得请求localhost/test/index.jsp,匹配所有的虚拟主机Host; +4.Engine 匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为 localhost的Host获得请求/test/index.jsp,匹配它所拥有的所有Context。Host匹配到路径为/test的 Context(如果匹配不到就把该请求交给路径名为“ ”的Context去处理); +5.path=“/test”的Context获得请求/index.jsp,在它的mapping table中寻找出对应的Servlet。Context匹配到URL Pattern为*.jsp的Servlet,对应于JspServlet类; +6.构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet()或doPost(),执行业务逻辑、数据存储等; +7.Context把执行完之后的HttpServletResponse对象返回给Host; +8.Host把HttpServletResponse对象返回给Engine; +9.Engine把HttpServletResponse对象返回Connector; +10.Connector把HttpServletResponse对象返回给客户Browser。 + +简化: +1.浏览器发起请求后,通过IP及端口找到对应的tomcat服务。再通过url找到具体的Service,Connector将请求交给它所在Service的容器进行处理,并等待响应。 +2.容器获得请求后,开始匹配并解析URL找到对应的Servlet,构建HttpServletRequest对象用于处理不同类型的请求(GET或POST),Servlet返回HttpServletResponse到Connector。 +3.Connector 将HttpServletResponse 对象返回给客户端。 \ No newline at end of file diff --git a/日常学习/中间件/tomcat/tomcat 线程池.md b/日常学习/中间件/tomcat/tomcat 线程池.md new file mode 100644 index 0000000..941a392 --- /dev/null +++ b/日常学习/中间件/tomcat/tomcat 线程池.md @@ -0,0 +1,14 @@ +conf/server.xml 中的线程池配置 +```xml + +``` + +name: 线程名称 +namePrefix: 线程前缀 +maxThreads : 最大并发连接数,不配置时默认200,一般建议设置500~ 800 ,要根据自己的硬件设施条件和实际业务需求而定。 +minSpareThreads:Tomcat启动初始化的线程数,默认值25 +prestartminSpareThreads:在tomcat初始化的时候就初始化minSpareThreads的值, 不设置true时minSpareThreads +maxQueueSize: 最大的等待队列数,超过则拒绝请求 +minSpareThreads:线程最大空闲时间60秒。 + + diff --git a/日常学习/中间件/tomcat/tomcat原理.md b/日常学习/中间件/tomcat/tomcat原理.md index 16dae49..d6ce394 100644 --- a/日常学习/中间件/tomcat/tomcat原理.md +++ b/日常学习/中间件/tomcat/tomcat原理.md @@ -15,4 +15,7 @@ Catalina用于管理Server,它提供了三个方法load、start、stop用于 Bootstrap的作用类似一个CatalinaAdaptor,具体处理过程还是使用Catalina来完成,这么做的好处是可以把启动的入口和具体的管理类分开,从而可以很方便地创建出多种启动方式,每种启动方式只需要写一个相应的CatalinaAdaptor即可。Bootstrap是tomcat的入口,启动tomcat就是调用Bootstrap的main方法。 ### 4.启动流程图 -![[Snipaste_2023-02-13_20-13-01.png]] \ No newline at end of file +![[Snipaste_2023-02-13_20-13-01.png]] + +### 5. 总结 +tomcat 的结构像石榴,最外层Server(唯一实例),其中包含多个Service,每个Service包含各自的容器及连接器。具体的初始化、启停由Catalina进行管理,init()读取conf下的配置文件、start()逐层调用(Service::start---->Container::start),stop方法也是如此 \ No newline at end of file diff --git a/日常学习/大数据/Hadoop/DataNode.md b/日常学习/大数据/Hadoop/DataNode.md new file mode 100644 index 0000000..bbd9f41 --- /dev/null +++ b/日常学习/大数据/Hadoop/DataNode.md @@ -0,0 +1,30 @@ +![[v2-c2f717ff3e4aecd6d756f3f1321468dd_720w.webp]] +# 一、概述 +datanode是负责当前节点上的数据的管理,具体目录内容是在初始阶段自动创建的,保存的文件夹位置由hdfs-site.xml文件配置选项{dfs.datanode.data.dir}决定。 ++ datanode以数据块的形式存储HDFS文件 ++ datanode响应HDFS 客户端读写请求 ++ datanode周期性向NameNode汇报心跳信息,数据块信息,缓存数据块信息 + +1. 一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。 +2. DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。 +3. DataNode与NameNode之间有一个**心跳事件**,心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令,如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。 +4. 集群运行中可以安全加入和退出一些机器 + +# 二、数据完整性校验 +![[v2-c2f717ff3e4aecd6d756f3f1321468dd_720w 1.webp]] +1. 当DataNode读取Block的时候,它会计算CheckSum(校验和) +2. 如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏 +3. Client读取其他DataNode上的Block +4. DataNode在其文件创建后周期验证CheckSum,如上图 + +# 三、掉线时参数设置 +DataNode进程死亡或者网络故障造成DataNode无法与NameNode通信时的TimeOut参数设置 +1. NameNode不会立即把该节点判断为死亡,要经过一段时间,这段时间称作**超时时长** +2. HDFS默认的超时时长为10分钟+30秒 +3. 超时时长的计算公式为: +```text +# dfs.namenode.heartbeat.recheck-interval默认为300000ms,dfs.heartbeat.interval默认为5s +TimeOut = 2 * dfs.namenode.heartbeat.recheck-interval + 10 * dfs.heartbeat.interval +``` + +实际开发的时候,可以根据自己服务器的情况进行调整,比如服务器性能比较低,那么可以适当的把时间调长;如果服务器性能很好,那么可以适当缩短。 \ No newline at end of file diff --git a/日常学习/大数据/Hadoop/HDFS副本机制.md b/日常学习/大数据/Hadoop/HDFS副本机制.md new file mode 100644 index 0000000..9f07405 --- /dev/null +++ b/日常学习/大数据/Hadoop/HDFS副本机制.md @@ -0,0 +1,81 @@ +HDFS 中的文件,在物理上都是以分块(block)存储的,hadoop2.x 默认块大小为128m +块大小可以通过 `hdfs-site.xml` 文件中的参数 `dfs.block.size` 进行设置: +```XML + + dfs.block.size + 134217728 + +``` +**分块存储的好处有:** +1)一个文件有可能大于集群中的所有磁盘,块机制可以很好地解决这个问题; +2)使用块作为文件存储的逻辑单位,可以简化存储子系统; +3)块非常适合用于数据备份(冗余的数据副本),进而提供数据容错能力。 +![[1438655-20210220083347391-59878081.png]] + +块大小的设置是否合理,对系统性能影响很大: +1. 设置太小,会增加寻址时间:一个正常大小的文件,被拆分成很多小 block,在读取的时候需要找到所有这些 block。 +2. 设置太大,从磁盘传输数据的时间会明显大于定位到这个 block 具体位置所需的时间,进而导致处理此 block 时速度变慢。 + +# 一、HDFS 的机架感知机制 + +**HDFS 的机架感知,又叫机架策略,用来决定副本存放位置的策略。** +以默认的副本数=3为例: +- **1st replica**. 如果写请求方所在机器是其中一个datanode,则直接存放在本地,否则随机在集群中选择一个datanode. +- **2nd replica**. 第二个副本存放于不同第一个副本的所在的机架. +- **3rd replica**.第三个副本存放于第二个副本所在的机架,但是属于不同的节点. +HDFS采用一种称为机架感知的策略来改进数据的可靠性、可用性和网络带宽的利用率。通过一个机架感知的过程,NameNode可以确定每一个DataNode所属的机架id。 + +# 二、HDFS机架感知 + +## 2.1 网络拓扑结构 +在海量数据处理中,主要限制原因之一是节点之间数据的传输速率,即带宽。因此,将两个节点之间的带宽作为两个节点之间的距离的衡量标准。 + +**hadoop为此采用了一个简单的方法**:把网络看作一棵树,两个节点之间的距离是他们到最近共同祖先的距离总和。该树中的层次是没有预先设定的, 但是相对与数据中心,机架和正在运行的节点,通常可以设定等级。具体想法是针对以下每个常见,可用带宽依次递减: +1>.同一节点上的进程 +2>.同一机架上的不同节点 +3>.同一数据中心中不同机架上的节点 +4>.不同数据中心的节点 + +举个例子,假设有数据中心d1,机架r1中的节点n1。该节点可以表示为“/d1/r1/n1”。利用这种标记,这里给出四种距离描述: +![[v2-bc178ed60f03fb733003a675e3828ba4_720w.webp]] +**注意:** +1)**当没有配置机架信息时,全部的机器Hadoop都默认在同一个默认的机架下**,以名为”/default-rack”。这样的情况下,不论什么一台datanode机器,无论物理上是否属于同一个机架。都会被觉得是在同一个机架下。 +2)一旦配置`topology.script.file.name`。就依照网络拓扑结构来寻找datanode。`topology.script.file.name`这个配置选项的value指定为一个可运行程序。通常为一个脚本。 + +# 三、HDFS负载均衡 + +## 3.1 HDFS负载均衡 +Hadoop的HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,例如:当集群内新增、删除节点,或者某个节点机器内硬盘存储达到饱和值。当数据不平衡时,Map任务可能会分配到没有存储数据的机器,这将导致网络带宽的消耗,也无法很好的进行本地计算。 +当HDFS负载不均衡时,需要对HDFS进行数据的负载均衡调整,即对各节点机器上数据的存储分布进行调整。从而,让数据均匀的分布在各个DataNode上,均衡IO性能,防止热点的发生。进行数据的负载均衡调整,必须要满足如下原则: +- 数据平衡不能导致数据块减少,数据块备份丢失 +- 管理员可以中止数据平衡进程 +- 每次移动的数据量以及占用的网络资源,必须是可控的 +- 数据均衡过程,不能影响namenode的正常工作 + +## 3.2 HDFS负载均衡原理 +数据均衡过程的核心是一个数据均衡算法,该数据均衡算法将不断迭代数据均衡逻辑,直至集群内数据均衡为止。该数据均衡算法每次迭代的逻辑如下: +![[v2-1a675ef9a5b7e5d913c75e769a12c606_720w.webp]] +步骤分析如下: +1. 数据均衡服务(Rebalancing Server)首先要求 NameNode 生成 DataNode 数据分布分析报告,获取每个DataNode磁盘使用情况 +2. Rebalancing Server汇总需要移动的数据分布情况,计算具体数据块迁移路线图。数据块迁移路线图,确保网络内最短路径 +3. 开始数据块迁移任务,Proxy Source Data Node复制一块需要移动数据块 +4. 将复制的数据块复制到目标DataNode上 +5. 删除原始数据块 +6. 目标DataNode向Proxy Source Data Node确认该数据块迁移完成 +7. Proxy Source Data Node向Rebalancing Server确认本次数据块迁移完成。然后继续执行这个过程,直至集群达到数据均衡标准 + +### 3.2.1 DataNode分组 +在第2步中,HDFS会把当前的DataNode节点,根据阈值的设定情况划分到Over、Above、Below、Under四个组中。在移动数据块的时候,Over组、Above组中的块向Below组、Under组移动。 +![[v2-1a675ef9a5b7e5d913c75e769a12c606_720w 1.webp]] + +### 3.2.2 HDFS数据自动平衡脚本使用 +在Hadoop中,包含一个start-balancer.sh脚本,通过运行这个工具,启动HDFS数据均衡服务。该工具可以做到热插拔,即无须重启计算机和 Hadoop 服务。HadoopHome/bin目录下的start−balancer.sh脚本就是该任务的启动脚本。启动命令为:HadoopHome/bin目录下的start−balancer.sh脚本就是该任务的启动脚本。启动命令为:`Hadoop_home/bin/start-balancer.sh –threshold` + +**影响Balancer的几个参数:** +- -threshold +- 默认设置:10,参数取值范围:0-100 +- 参数含义:判断集群是否平衡的阈值。理论上,该参数设置的越小,整个集群就越平衡 + +- dfs.balance.bandwidthPerSec +- 默认设置:1048576(1M/S), 需要跟进自己的集群带宽情况进行设置,一般为30M或者60M左右 +- 参数含义:Balancer运行时允许占用的带宽 \ No newline at end of file diff --git a/日常学习/大数据/Hadoop/HDFS基础.md b/日常学习/大数据/Hadoop/HDFS基础.md new file mode 100644 index 0000000..9d8f42b --- /dev/null +++ b/日常学习/大数据/Hadoop/HDFS基础.md @@ -0,0 +1,76 @@ + # 一、概述 + “HDFS(Hadoop Distributed File System)基于Google发布的GFS论文设计开发。HDFS是Hadoop技术框架中的分布式文件系统,对部署在多台独立物理机器上的文件进行管理。 + +![[99730e6774737f2ecdd4cb029b952c24.png]] + +## 1.1 优缺点及设计特点 + +### 1.1.1 优点 +1. 大数据文件,非常适合上T级别的大文件或者一堆大数据文件的存储,如果文件只有几个G甚至更小就没啥意思了。 +2. 文件分块存储,HDFS会将一个完整的大文件平均分块存储到不同计算器上,它的意义在于读取文件时可以同时从多个主机取不同区块的文件,多主机读取比单主机读取效率要高得多得都。 +3. 流式数据访问,一次写入多次读写,这种模式跟传统文件不同,它不支持动态改变文件内容,而是要求让文件一次写入就不做变化,要变化也只能在文件末添加内容。 +4. 廉价硬件,HDFS可以应用在普通PC机上,这种机制能够让给一些公司用几十台廉价的计算机就可以撑起一个大数据集群。 +5. 硬件故障,HDFS认为所有计算机都可能会出问题,为了防止某个主机失效读取不到该主机的块文件,它将同一个文件块副本分配到其它某几个主机上,如果其中一台主机失效,可以迅速找另一块副本取文件。 + +### 1.1.2 缺点 +1. 低时间延迟数据访问的应用,例如几十毫秒范围。 +原因:HDFS是为高数据吞吐量应用优化的,这样就会造成以高时间延迟为代价。 +2. 大量小文件 。 +原因:NameNode启动时,将文件系统的元数据加载到内存,因此文件系统所能存储的文件总数受限于NameNode内存容量。,那么需要的内存空间将是非常大的。 +3. 多用户写入,任意修改文件。 +原因:现在HDFS文件只有一个writer,而且写操作总是写在文件的末尾。 + +## 1.2 HDFS架构 +![[v2-95b883ec66e961e520cca2516ed0ceb7_720w.webp]] +HDFS架构包含三个部分:NameNode,DataNode,Client。(图中Secondary NameNode也属于NameNode,是备节点) +- NameNode:NameNode用于存储、生成文件系统的元数据。管理数据块映射;处理客户端的读写请求;配 置副本策略;管理 HDFS 的名称空间。 + NameNode 保存的 metadata 包括: + + 文件 ownership 和 permission + + 文件包含的 block 信息(Block 保存在DataNode 节点上) +- DataNode:DataNode用于存储实际的数据,将自己管理的数据块上报给NameNode ,负责存储client发来的数据块;执行数据块读写操作。 +- Client:支持业务访问HDFS,从NameNode ,DataNode获取数据返回给业务。多个实例,和业务一起运行。 + +## 1.3 读数据流程 +![[dd0a29a1d43c8ac8917d25c202895363.png]] +1. 业务应用调用HDFS Client提供的API打开文件。 +2. HDFS Client联系NameNode,获取到文件信息(数据块、DataNode位置信息)。 +3. 业务应用调用read API读取文件。 +4. HDFS Client根据从NameNode获取到的信息,联系DataNode,获取相应的数据块。(Client采用就近原则读取数据)。 +5. HDFS Client会与多个DataNode通讯获取数据块。 +6. 数据读取完成后,业务调用close关闭连接。 + + + +## 1.4 写数据流程 +![[dd0a29a1d43c8ac8917d25c202895363 1.png]] +1. 业务应用调用HDFS Client提供的API,请求写入文件。 +2. HDFS Client联系NameNode,NameNode在元数据中创建文件节点。 +3. 业务应用调用write API写入文件。 +4. HDFS Client收到业务数据后,从NameNode获取到数据块编号、位置信息后,联系DataNode,并将需要写入数据的DataNode建立起流水线。完成后,客户端再通过自有协议写入数据到DataNode1,再由DataNode1复制到DataNode2, DataNode3。 +5. 写完的数据,将返回确认信息给HDFS Client。 +6. 所有数据确认完成后,业务调用HDFS Client关闭文件。 +7. 业务调用close, flush后HDFSClient联系NameNode,确认数据写完成,NameNode持久化元数据。 + +# 二、HDFS 关键特性 +![[dbe22eed6ebbd55d48fbe3b1ccbd93e9.png]] + +## 2.1 HA高可靠性 +- HDFS的高可靠性(HA)主要体现在利用zookeeper实现主备NameNode,以解决单点NameNode故障问题。 +- ZooKeeper主要用来存储HA下的状态文件,主备信息。ZK个数建议3个及以上且为奇数个。 +- NameNode主备模式,主提供服务,备同步主元数据并作为主的热备。 +- ZKFC(ZooKeeper Failover Controller)用于监控NameNode节点的主备状态。 +- JN(JournalNode)用于存储Active NameNode生成的Editlog。Standby NameNode加载JN上Editlog,同步元数据。 + +## 2.2 ZKFC控制NameNode主备仲裁 +- ZKFC作为一个精简的仲裁代理,其利用zookeeper的分布式锁功能,实现主备仲裁,再通过命令通道,控制NameNode的主备状态。ZKFC与NN部署在一起,两者个数相同。 + +## 2.3 元数据同步 +- 主NameNode对外提供服务。生成的Editlog同时写入本地和JN,同时更新主NameNode内存中的元数据。 +- 备NameNode监控到JN上Editlog变化时,加载Editlog进内存,生成新的与主NameNode一样的元数据。元数据同步完成。 +- 主备的FSImage仍保存在各自的磁盘中,不发生交互。FSImage是内存中元数据定时写到本地磁盘的副本,也叫元数据镜像。 + +## 2.4 元数据持久化 +- EditLog:记录用户的操作日志,用以在FSImage的基础上生成新的文件系统镜像。 +- FSImage:用以阶段性保存文件镜像。 +- FSImage.ckpt:在内存中对fsimage文件和EditLog文件合并(merge)后产生新的fsimage,写到磁盘上,这个过程叫checkpoint.。备用NameNode加载完fsimage和EditLog文件后,会将merge后的结果同时写到本地磁盘和NFS。此时磁盘上有一份原始的fsimage文件和一份新生成的checkpoint文件:fsimage.ckpt. 而后将fsimage.ckpt改名为fsimage(覆盖原有的fsimage)。 +- EditLog.new: NameNode每隔1小时或Editlog满64MB就触发合并,合并时,将数据传到Standby NameNode时,因数据读写不能同步进行,此时NameNode产生一个新的日志文件Editlog.new用来存放这段时间的操作日志。Standby NameNode合并成fsimage后回传给主NameNode替换掉原有fsimage,并将Editlog.new 命名为Editlog。 \ No newline at end of file diff --git a/日常学习/大数据/Hadoop/HDFS常用命令.md b/日常学习/大数据/Hadoop/HDFS常用命令.md new file mode 100644 index 0000000..6ef0354 --- /dev/null +++ b/日常学习/大数据/Hadoop/HDFS常用命令.md @@ -0,0 +1,72 @@ +1、查看目录下的文件列表:hadoop fs –ls [文件目录] +hadoop fs -ls -h /lance 加入 -R参数可以递归 + +2、将本机文件夹存储至hadoop上:hadoop fs –put [本机目录] [hadoop目录] +hadoop fs -put lance / + +3、在hadoop指定目录内创建新目录:hadoop fs –mkdir [目录] +hadoop fs -mkdir /lance +hadoop fs -mkdir -p 递归创建 + +4、在hadoop指定目录下新建一个文件,使用touchz命令: +hadoop fs -touchz /lance/tmp.txt + +5、将本机文件存储至hadoop上:hadoop fs –put [本机地址] [hadoop目录] +hadoop fs -put tmp.txt /lance #直接目录 +hadoop fs -put tmp.txt hdfs://www.lance.com/lance #远程服务器地址 + +6、打开已存在文件:hadoop fs –cat [文件名称] +hadoop fs -cat /lance/tmp.txt +hadoop fs -cat xxx.nb /tail -n 10 显示后10行 +hadoop fs -text /dahua/original_data/shiwu.csv | head -n 3 读取前3行 + +7、重命名hadoop fs –mv [旧文件名] [新文件名] +hadoop fs -mv /tmp /tmp_bak #修改文件夹名 + +8、将hadoop上文件down至本机已有目录下:hadoop fs -get [文件目录] [本机目录] +hadoop fs -get /lance/tmp.txt /lance + +9、删除hadoop上文件:hadoop fs -rm [文件地址] +hadoop fs -rm /lance/tmp.txt + +10、删除hadoop上指定文件夹(包含子目录等):hadoop fs –rm -r [目录地址] +hadoop fs -rm -r /lance + +11、将hadoop指定目录下所有内容保存为一个文件,同时下载至本机 +hadoop dfs –getmerge /user /home/t + +12、将正在运行的hadoop作业kill掉 +hadoop job –kill [jobId] + +13、查看文件夹或文件信息 +hadoop fs -stat '%y' hdfsurl 参数 y 表示修改时间 +| 参数 | 用途 | +| --- | --- | +|a |权限| +|A |权限| +|b |长度| +|F |是否文件夹| +|g |group| +|n |name| +|o |blockSize| +|r |副本数| +|u |拥有者| +|x |访问时间| +|y |修改时间| + +https://github.com/apache/hadoop/blob/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Stat.java#L31 + +14、统计HDFS路径下的文件数 +hadoop fs -count [路径] + +15、复制 +hadoop fs -cp + +16、查看回收站 +hadoop fs -ls .Trash + +17、统计文件行数 +hadoop fs -cat xxxx|wc -l + +18、查看文件大小 +hadoop fs -du -h [路径] diff --git a/日常学习/大数据/Hadoop/MapReduce.md b/日常学习/大数据/Hadoop/MapReduce.md new file mode 100644 index 0000000..e69de29 diff --git a/日常学习/大数据/Hadoop/NameNode.md b/日常学习/大数据/Hadoop/NameNode.md new file mode 100644 index 0000000..086f6f3 --- /dev/null +++ b/日常学习/大数据/Hadoop/NameNode.md @@ -0,0 +1,131 @@ +![[fe889daa8be54dcaa1c7cc364c7a35f5.png]] + +# 一、概述 + +NameNode是整个文件系统的管理节点,它主要维护着整个文件系统的文件目录树,文件/目录的信息 和 每个文件对应的数据块列表,并且还负责接收用户的操作请求 + +### 文件目录树 +表示目录之间的层级关系,就是我们在hdfs上执行ls命令可以看到的那个目录结构信息 + +### 文件/目录的信息 +表示文件/目录的的一些基本信息,所有者 属组 修改时间 文件大小等信息 + +### 数据块列表 +如果一个文件太大,那么在集群中存储的时候会对文件进行切割,这个时候就类似于会给文件分成一块一块的,存储到不同机器上面。所以HDFS还要记录一下一个文件到底被分了多少块,每一块都在什么地方存储着 + +# 二、元数据文件 ++ fsimage:元数据镜像文件,存储某一时段NameNode内存元数据信息 ++ edits:操作日志文件 ++ seen_txid:是存放transactionId的文件,format之后是0,它代表的是namenode里面的edits_*文件的尾数,namenode重启的时候,会按照seen_txid的数字,顺序从头跑edits_0000001~到seen_txid的数字。如果根据对应的seen_txid无法加载到对应的文件,NameNode进程将不会完成启动以保护数据一致性。 ++ VERSION:保存了集群的版本信息 + +根据hdfs-site.xml文件配置,找到dfs.namenode.name.dir属性,可以找到元数据目录$dfs.namenode.name.dir/current +![[Snipaste_2023-03-06_10-26-39 1.png]] +in_use.lock 这个只是一个普通文件,但是它其实有特殊的含义,文件名后缀值lock 表示是锁的意思,文件名是in_use 表示这个文件现在正在使用,不允许你再启动namenode。当我们启动namonde的时候 会判断这个目录下是否有in_use.lock 这个相当于一把锁,如果没有的话,才可以启动成功,启动成功之后就会加一把锁, 停止的时候会把这个锁去掉。 +![[Snipaste_2023-03-06_10-26-39.png]] + +## 2.1 fsimage +fsimage文件有两个文件名相同的,有一个后缀是md5,md5是一种加密算法,这个其实主要是为了做md5校验的,为了保证文件传输的过程中不出问题,相同内容的md5是一样的,所以后期如果我把这个fsimage和对应的fsimage.md5发给你 然后你根据md5对fsimage的内容进行加密,获取一个值 和fsimage.md5中的内容进行比较,如果一样,说明你接收到的文件就是完整的。 + +通过命令将fsimage转为xml,方便查看 +```shell +hdfs oiv -p XML -i fsimage_0000000000000000056 -o fsimage56.xml +``` + +里面最外层是一个fsimage标签,看里面的inode标签, +这个inode表示是hdfs中的每一个目录或者文件信息 +```xml + +            16393 +            FILE +            LICENSE.txt +            2 +            1586332513657 +            1586332513485 +            134217728 +            root:supergroup:0644 +            +                +                    1073741827 +                    1003 +                    150569 +                +            +            0 + +``` + +id:唯一编号 +type:文件类型 +name:文件名称 +replication:文件的副本数量 +mtime:修改时间 +atime:访问时间 +preferredBlockSize:推荐每一个数据块的大小 +permission:权限信息 +blocks:包含多少数据块【文件被切成数据块】 +block:内部的id表示是块id,genstamp是一个唯一编号,numBytes表示当前数据块的实际大小,storagePolicyId表示是数据的存储策略 + +**这个文件中其实就维护了整个文件系统的文件目录树,文件/目录的元信息和每个文件对应的数据块列表,所以说fsimage中存放了hdfs最核心的数据。** + +## 2.2 edits +当我们上传大文件的时候,一个大文件会分为多个block,那么edits文件中就会记录这些block的上传状态,只有当全部block都上传成功了以后,这个时候edits中才会记录这个文件上传成功了,那么我们执行hdfs dfs -ls 的时候就能查到这个文件了,所以当我们在hdfs中执行ls命令的时候,其实会查询fsimage和edits中的内容。 + +查看edits文件: +```shell +hdfs oev -i edits_0000000000000000057-0000000000000000065 -o edits.xml +``` + +OP_ADD:执行上传操作 +OP_ALLOCATE_BLOCK_ID:申请block块id +OP_SET_GENSTAMP_V2:设置GENSTAMP +OP_ADD_BLOCK:添加block块 +OP_CLOSE:关闭上传操作 + +```xml + +    OP_ADD +    +        58 +        0 +        16396 +        /user.txt +        3 +        1586349095694 +        1586349095694 +        134217728 +        DFSClient_NONMAPREDUCE_-1768454371_1 +        192.168.182.1 +        true +        +            yehua +            supergroup +            420 +        +        0 +        1722b83a-2dc7-4c46-baa9-9fa956b755cd +        0 +    + + +    OP_ALLOCATE_BLOCK_ID +    +        59 +        1073741830 +    + +``` + +每一个record都有一个事务id,txid,事务id是连续的(从58到59,正常情况下有5个步骤,所以有5个连续的txid),其实一个put操作会在edits文件中产生很多的record,对应的就是很多步骤,这些步骤对我们是屏蔽的。 + +## 2.3 seen_txid +![[Snipaste_2023-03-06_10-26-39 3.png]] +seen_txid文件,HDFS format之后是0,它代表的是namenode里面的edits_*文件的尾数,namenode重启的时候,会按照seen_txid的数字,顺序从头跑edits_0000001~到seen_txid的数字。如果根据对应的seen_txid无法加载到对应的文件,NameNode进程将不会完成启动以保护数据一致性。 + +## 2.4 VERSION +![[Snipaste_2023-03-06_10-26-39 2.png]] + +# 三、SNN合并日志 +edits隔一段时间就会对fsimage进行同步(合并)。edits在执行读写合并操作时,是被占用状态,整个namenode不能对外提供服务。用户一来会先找edits。这样不好。namenode是整个集群的管理和元数据记录。读写操作消耗很大。合并这件事可以定期发送给SecondaryNameNode来做。为了保证实时提供服务创建新的edits临时替代原来的edits对外提供服务。SecondaryNameNode的fsimage和edits合并后得到新的fsimage(SecondaryNameNode的edits被删除)传回给namenode,namenode的edits被清空。 + +![[5128967-24284b0c0ed22be3.png]] \ No newline at end of file diff --git a/日常学习/大数据/Hadoop/YARN.md b/日常学习/大数据/Hadoop/YARN.md new file mode 100644 index 0000000..5202e1b --- /dev/null +++ b/日常学习/大数据/Hadoop/YARN.md @@ -0,0 +1,36 @@ +# 一、概述 +在古老的 Hadoop1.0 中,MapReduce 的 JobTracker 负责了太多的工作,包括资源调度,管理众多的 TaskTracker 等工作。这自然是不合理的,于是 Hadoop 在 1.0 到 2.0 的升级过程中,便将 JobTracker 的资源调度工作独立了出来,而这一改动,直接让 Hadoop 成为大数据中最稳固的那一块基石。,**而这个独立出来的资源管理框架,就是 Yarn** 。 +在详细介绍 Yarn 之前,我们先简单聊聊 Yarn ,Yarn 的全称是 ** Yet Another Resource Negotiator**,意思是“_另一种资源调度器_”。 + +# 二、架构 +![[v2-b5804eb2bb1b3f2ff091c18bbf915819_720w.gif]] + +## 2.1 Container +容器(Container)这个东西是 Yarn 对资源做的一层抽象。就像我们平时开发过程中,经常需要对底层一些东西进行封装,只提供给上层一个调用接口一样,Yarn 对资源的管理也是用到了这种思想。 +![[Snipaste_2023-03-07_10-40-14.png]] +如上所示,Yarn 将CPU核数,内存这些计算资源都封装成为一个个的容器(Container)。需要注意两点: +- 容器由 NodeManager 启动和管理,并被它所监控。 +- 容器被 ResourceManager 进行调度。 + +## 2.2 三个重要组件 + +### 2.2.1 ResourceManager +从名字上我们就能知道这个组件是负责资源管理的,整个系统有且只有一个 RM ,来负责资源的调度。它也包含了两个主要的组件:定时调用器(Scheduler)以及应用管理器(ApplicationManager)。 +1. 定时调度器(Scheduler):从本质上来说,定时调度器就是一种策略,或者说一种算法。当 Client 提交一个任务的时候,它会根据所需要的资源以及当前集群的资源状况进行分配。注意,它只负责向应用程序分配资源,并不做监控以及应用程序的状态跟踪。 +2. 应用管理器(ApplicationManager):同样,听名字就能大概知道它是干嘛的。应用管理器就是负责管理 Client 用户提交的应用。上面不是说到定时调度器(Scheduler)不对用户提交的程序监控嘛,其实啊,监控应用的工作正是由应用管理器(ApplicationManager)完成的。 + +### 2.2.2 NodeManager +NodeManager 是 ResourceManager 在每台机器的上代理,负责容器的管理,并监控他们的资源使用情况(cpu,内存,磁盘及网络等),以及向 ResourceManager/Scheduler 提供这些资源使用报告。 + +### 2.2.3 ApplicationMaster +每当 Client 提交一个 Application 时候,就会新建一个 ApplicationMaster 。由这个 ApplicationMaster 去与 ResourceManager 申请容器资源,获得资源后会将要运行的程序发送到容器上启动,然后进行分布式计算。 +这里可能有些难以理解,为什么是把运行程序发送到容器上去运行?如果以传统的思路来看,是程序运行着不动,然后数据进进出出不停流转。但当数据量大的时候就没法这么玩了,因为海量数据移动成本太大,时间太长。但是中国有一句老话**山不过来,我就过去。**大数据分布式计算就是这种思想,既然大数据难以移动,那我就把容易移动的应用程序发布到各个节点进行计算呗,这就是大数据分布式计算的思路。 + +## 2.3 提交Application到yarn的流程 +![[v2-8b32b881d0d8ea046455a6d77868aebe_720w.webp]] + +1. Client 向 Yarn 提交 Application,这里我们假设是一个 MapReduce 作业。 +2. ResourceManager 向 NodeManager 通信,为该 Application 分配第一个容器。并在这个容器中运行这个应用程序对应的 ApplicationMaster。 +3. ApplicationMaster 启动以后,对 作业(也就是 Application) 进行拆分,拆分 task 出来,这些 task 可以运行在一个或多个容器中。然后向 ResourceManager 申请要运行程序的容器,并定时向ResourceManager 发送心跳。 +4. 申请到容器后,ApplicationMaster 会去和容器对应的 NodeManager 通信,而后将作业分发到对应的 NodeManager 中的容器去运行,这里会将拆分后的 MapReduce 进行分发,对应容器中运行的可能是 Map 任务,也可能是 Reduce 任务。 +5. 容器中运行的任务会向 ApplicationMaster 发送心跳,汇报自身情况。当程序运行完成后, ApplicationMaster 再向 ResourceManager 注销并释放容器资源。 \ No newline at end of file diff --git a/日常学习/大数据/Hadoop/YARN命令.md b/日常学习/大数据/Hadoop/YARN命令.md new file mode 100644 index 0000000..9cdbc72 --- /dev/null +++ b/日常学习/大数据/Hadoop/YARN命令.md @@ -0,0 +1,55 @@ +### 1.application +使用语法:yarn  application [options] #打印报告,申请和杀死任务 +```text +-appStates #与-list一起使用,可根据输入的逗号分隔的应用程序状态列表来过滤应用程序。有效的应用程序状态可以是以下之一:ALL,NEW,NEW_SAVING,SUBMITTED,ACCEPTED,RUNNING,FINISHED,FAILED,KILLED +-appTypes #与-list一起使用,可以根据输入的逗号分隔的应用程序类型列表来过滤应用程序。 +-list #列出RM中的应用程序。支持使用-appTypes来根据应用程序类型过滤应用程序,并支持使用-appStates来根据应用程序状态过滤应用程序。 +-kill #终止应用程序。 +-status #打印应用程序的状态。 +``` + +### 2.applicationattempt +使用语法:yarn applicationattempt [options] #打印应用程序尝试的报告 +```text +-help #帮助 +-list #获取到应用程序尝试的列表,其返回值ApplicationAttempt-Id 等于 +-status #打印应用程序尝试的状态。 +``` + +### 3.classpath +使用语法:yarn classpath #打印需要得到Hadoop的jar和所需要的lib包路径 + +### 4.container +使用语法:yarn container [options] #打印container(s)的报告 +```text +-help #帮助 +-list #应用程序尝试的Containers列表 +-status #打印Container的状态 +``` + +### 5. logs +使用语法:yarn logs -applicationId +```text +-applicationId #指定应用程序ID,应用程序的ID可以在yarn.resourcemanager.webapp.address配置的路径查看(即:ID) +-appOwner #应用的所有者(如果没有指定就是当前用户)应用程序的ID可以在yarn.resourcemanager.webapp.address配置的路径查看(即:User) +-containerId #Container Id +-help #帮助 +-nodeAddress #节点地址的格式:nodename:port (端口是配置文件中:yarn.nodemanager.webapp.address参数指定) +``` + +### 6.node +使用语法:yarn node [options] #打印节点报告 +```text +-all #所有的节点,不管是什么状态的。 +-list #列出所有RUNNING状态的节点。支持-states选项过滤指定的状态,节点的状态包含:NEW,RUNNING,UNHEALTHY,DECOMMISSIONED,LOST,REBOOTED。支持--all显示所有的节点。 +-states #和-list配合使用,用逗号分隔节点状态,只显示这些状态的节点信息。 +-status #打印指定节点的状态。 +``` + +### 7.queue +使用语法:yarn queue [options] #打印队列信息 +```text +-help #帮助 +-status # 打印队列的状态 +``` + diff --git a/日常学习/数据库/postgresql/PG-SQL优化技巧.md b/日常学习/数据库/postgresql/PG-SQL优化技巧.md new file mode 100644 index 0000000..e69de29 diff --git a/日常学习/数据库/postgresql/PG索引.md b/日常学习/数据库/postgresql/PG索引.md new file mode 100644 index 0000000..e69de29 diff --git a/日常学习/数据库/postgresql/Postgresql 事务实现.md b/日常学习/数据库/postgresql/Postgresql 事务实现.md new file mode 100644 index 0000000..f015e0f --- /dev/null +++ b/日常学习/数据库/postgresql/Postgresql 事务实现.md @@ -0,0 +1,14 @@ +SQL标准定义了四个级别的事务隔离。 +| 隔离级别 | 脏读 | 幻读 | 不可重复读取 | +| -------- | ------ | ------ | ------------ | +| 读未提交 | 可能 | 可能 | 可能 | +| 读已提交 | 不可能 | 可能 | 可能 | +| 可重复读 | 不可能 | 可能 | 不可能 | +| 可串行读 | 不可能 | 不可能 | 不可能 | + +PostgreSQL只提供两种隔离级别的原因是,这是把标准的隔离级别与多版本并发控制架构映射相关的唯一合理方法。 + +1. 读已提交 + 这是PostgreSQL中默认的隔离级别,当一个事务运行在这个隔离级别时,一个SELECT查询只能看到查询开始前已提交的数据,而无法看到未提交的数据或者在查询期间其他的事务已提交的数据。(通过快照实现) +2. 可串行化 + 可串行化提供最严格的事务隔离。这个级别模拟串行的事务执行,就好像事务是一个接着一个串行的执行。不过,这个级别的应用必须准备在串行化失败的时候重新启动事务。 \ No newline at end of file diff --git a/日常学习/数据结构/AVL平衡搜索树(自平衡二叉搜索树).md b/日常学习/数据结构/AVL平衡搜索树(自平衡二叉搜索树).md index 97e603f..1c22ab5 100644 --- a/日常学习/数据结构/AVL平衡搜索树(自平衡二叉搜索树).md +++ b/日常学习/数据结构/AVL平衡搜索树(自平衡二叉搜索树).md @@ -12,4 +12,7 @@ AVL树可以定义为高度平衡二叉搜索树,其中每个节点与平衡 ### 为什么使用AVL树: 通过自平衡解决二叉搜索树查找(搜索)节点问题!!! AVL树通过不让它倾斜来控制二叉搜索树的高度。 高度为`h`的二叉搜索树中的所有操作所花费的时间 -是`O(h)`。 但是,如果二叉搜索树变得偏斜(即最坏的情况),它可以扩展到`O(n)`。 通过将该高度限制为`log n`,AVL树将每个操作的上限强加为`O(log n)`,其中`n`是节点的数量。 \ No newline at end of file +是`O(h)`。 但是,如果二叉搜索树变得偏斜(即最坏的情况),它可以扩展到`O(n)`。 通过将该高度限制为`log n`,AVL树将每个操作的上限强加为`O(log n)`,其中`n`是节点的数量。 + +模拟AVL树插入及自平衡过程 +[AVL Tree Visualzation (usfca.edu)](https://www.cs.usfca.edu/~galles/visualization/AVLtree.html) \ No newline at end of file diff --git a/日常学习/数据结构/B+树.md b/日常学习/数据结构/B+树.md new file mode 100644 index 0000000..17a1974 --- /dev/null +++ b/日常学习/数据结构/B+树.md @@ -0,0 +1,70 @@ +# 一、概述 +一个m阶的B+树具有如下几个特征: ++ 有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。 ++ 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。 ++ 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。 + +B+树的结构: +![[71cfccb87da28e4cc5b618bcef02a2ac.png]] + +上面的这颗树中,得出结论: +根节点元素8是子节点2,5,8 的最大元素,也是叶子节点6,8 的最大元素; +根节点元素15是子节点11,15 的最大元素,也是叶子节点13,15 的最大元素; +根节点的最大元素也就是整个B+树的最大元素,以后无论插入删除多少元素,始终要保持最大的元素在根节点当中。 +由于父节点的元素都出现在子节点中,因此所有的叶子节点包含了全部元素信息,并且每一个叶子节点都带有指向下一个节点的指针,形成了一个有序链表。 + +# 二、卫星数据 +指的的是索引元素所指向的数据记录,比如数据库中的某一行,在B-树中,无论中间节点还是叶子节点都带有卫星数据。 + +B-树中的卫星数据: +![[1fd40497a31465f77367692a09a223c9.png]] + +B+树中的卫星数据: +只有叶子节点带有卫星数据,其余中间节点仅仅是索引,没有任何的数据关联。 +![[1fd40497a31465f77367692a09a223c9 1.png]] +需要补充的是,在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。 + +# 三、B+树的查询 + +## 1. 单行查询 +比如我们要查找的元素是3: +B+树会自顶向下逐层查找节点,最终找到匹配的叶子节点。 +第一次磁盘IO: +![[1fd40497a31465f77367692a09a223c9 2.png]] + +第二次磁盘IO: +![[1fd40497a31465f77367692a09a223c9 3.png]] + +第三次磁盘IO: +![[1fd40497a31465f77367692a09a223c9 4.png]] + +## 2. 范围查询 +比如我们要查询3到11的范围数据: +### B-树的范围查询过程: +自顶向下,查找到范围的下限(3): +![[1fd40497a31465f77367692a09a223c9 5.png]] + + +中序遍历到元素6: +![[1fd40497a31465f77367692a09a223c9 6.png]] + +中序遍历到元素8: +![[1fd40497a31465f77367692a09a223c9 7.png]] + +中序遍历到元素11,遍历结束: +![[1fd40497a31465f77367692a09a223c9 8.png]] + +### **B+树的范围查找过程** +自顶向下,查找到范围的下限(3): +![[1fd40497a31465f77367692a09a223c9 9.png]] + +通过链表指针,遍历到元素6, 8: +![[1fd40497a31465f77367692a09a223c9 10.png]] + +![[1fd40497a31465f77367692a09a223c9 11.png]] + +# 四、总结 +B+树比B-树的优势三个: +1. 单一节点存储更多的元素,使得查询的IO次数更少。 +2. 所有查询都要查找到叶子节点,查询性能稳定。 +3. 所有叶子节点形成有序链表,便于范围查询。 \ No newline at end of file diff --git a/日常学习/数据结构/RingBuffer(环形缓冲队列).md b/日常学习/数据结构/RingBuffer(环形缓冲队列).md new file mode 100644 index 0000000..9e5e2ce --- /dev/null +++ b/日常学习/数据结构/RingBuffer(环形缓冲队列).md @@ -0,0 +1,30 @@ +**RingBuffer**, 这种结构实现起来只需要几行代码即可,但使用场景却很广泛,比如在Linux内核中网络数据包的缓存,系统日志的存储等多处使用过该结构。同时它也被广泛的应用于异步通信以及嵌入式设备中,提供高效的数据缓存读写操作。 + +# 一、原理 +RingBufferr实现比较简单,基本上只需要一个数组结构,外加两个用于存储位置信息的变量即可。其中的数组采用固定大小容量,便于重用内存,不会出现动态内存不断分配和销毁的情况,这对于一些GC类编程语言来说,大幅减少了内存管理的成本。 +下面就是一个典型的RingBuffer图例,包含一个容量大小为6的数组,以及一个读和写指针。其中Write和Read用于管理读写的位置序列,读写开始后,不断增加该值来定位下一次的读写位置。(实际上由3个参数控制,读指针、写指针、容量) +![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA.png]] + +每次写数据:在Write对应位置写入新值,并向前移动对应的Write指针位置,如果遇到指针已经处于尾部,则移动到最开始位置,形成一个环形, 类似于双向链表。 +每次读取数据:在Read位置读取当前值,并移动Read位置,同样如果遇到已经到达尾部,则返回到最开始的初始位置。 +整个数据流的读写过程就是通过不断的操作Write和Read来实现数据的高效处理。 + +# 二、读写操作实例 +通过一个简单的实例来介绍如何操作RingBuffer来管理数据: 首先初始化一个空的数组,并设置Read=0和Write=0, 图例中黄色代表已写入的数据,绿色代表已读取的数据,红色代表异常情况: +(1) 写入三个元素分别是:1,2,3, 这时候读指针位置不变,写指针移动三个位置到索引为3的位置(数组索引位置从0开始) +![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 1.png]] + +(2)读取一个元素,读指针移动一个位置,写指针不变,获取数据值1 +![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 2.png]] + +(3)继续写入四个元素,分别是:4,5,6,7, 其中4,5,6分别放入剩余的数组空缺中,但是7 由于已经没有位置可写,则从0开始覆盖原有写入1的位置。注意这里我们没有设置write=0,而是直接在原Write值上继续加1,我们取模size即可获得新的写入位置(取模后重新返回头部)。 +![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 3.png]] + +(4)当我们再次写入两个值:8,9的时候,由于与上一轮的Read发生了交叉,为了保证前后读写的顺序性,**我们需要同时移动读指针的位置,使得读位置总是指向最旧的数据**。 +![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 4.png]] + +(5)这时候如果读取两个数据,则读指针只需要按照当前序列向前移动两个位置即可,分别获得值4,5 代表了最早的数据项,假如上面我们没有移动读指针,则读取的可能会是最新数据。 +![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 5.png]] + +(6)如果我们这时候读取速度加快,假如读取5个值,可成功读取6,7,8,9,当读取到4值的时候由于此时,读写位置重叠(读数据不能超过写数据的位置,否则重复读取的问题),无法进一步读取数据。退出读取流程。 +![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 6.png]] \ No newline at end of file diff --git a/日常学习/数据结构/二叉查找(排序)树.md b/日常学习/数据结构/二叉查找(排序)树(BST).md similarity index 89% rename from 日常学习/数据结构/二叉查找(排序)树.md rename to 日常学习/数据结构/二叉查找(排序)树(BST).md index cf83bbc..1ef2947 100644 --- a/日常学习/数据结构/二叉查找(排序)树.md +++ b/日常学习/数据结构/二叉查找(排序)树(BST).md @@ -9,5 +9,3 @@ BST以链接方式存储,保持了链接存储结构在执行插入或删除操作时不用移动元素的优点,只要找到合适的插入和删除位置后,仅需要修改链接指针即可。插入删除的时间性能比较好。而对于BST的查找,其比较次数等于给定值的结点在二叉排序树的层数,极端情况下,最少为1次,最多不会超过树的深度。也就是说BST的查找性能取决于BST的形状。 我们希望二叉排序树是比较平衡的,其深度与完全二叉树相同,均为\log_{2}nlog2​n + 1,那么查找的时间复杂度也就为O(logn),近似于折半查找。这就引申出一个问题,即如何让BST**平衡化**。 -模拟AVL树插入及自平衡过程 -[AVL Tree Visualzation (usfca.edu)](https://www.cs.usfca.edu/~galles/visualization/AVLtree.html) \ No newline at end of file diff --git a/日常学习/数据结构/跳跃列表(skiplist).md b/日常学习/数据结构/跳跃列表(skiplist).md index c152710..113254a 100644 --- a/日常学习/数据结构/跳跃列表(skiplist).md +++ b/日常学习/数据结构/跳跃列表(skiplist).md @@ -16,7 +16,7 @@ ![[Snipaste_2023-02-23_10-00-41 11.png]] 构建L3层,从头遍历L2层元素,过程与L1到L2相同 ![[Snipaste_2023-02-23_10-00-41 12.png]] -跳跃的意义在于可以快速到达元素,例如从L2层可以之间从2到13只需要2步,而L1层从2到13需要4步,L0层需要5步。 +跳跃的意义在于可以快速到达元素,例如从L2层可以直接从2到13只需要2步,而L1层从2到13需要4步,L0层需要5步。 **跳跃列表的层数最好为Logn层** ## 2.2 查找 diff --git a/日常学习/计算机基础/COW机制.md b/日常学习/计算机基础/COW机制.md new file mode 100644 index 0000000..c2075f8 --- /dev/null +++ b/日常学习/计算机基础/COW机制.md @@ -0,0 +1,15 @@ +写入时复制(Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。 + +# 1.使用场景 +## 1.1 在文件系统中的应用 +数据的备份或者复制至少处于硬件(硬盘)和操作系统两个层级。硬件备份一般使用RAID0-10的方案,保护硬盘中的数据安全;操作系统中则使用Snapshot(快照)技术来维持数据的安全和有效。人们一直采用数据复制、备份、恢复等技术来保护重要的数据信 息,定期对数据进行备份或复制。由于数据备份过程会影响应用性能,并 且非常耗时,因此数据备份通常被安排在系统负载较轻时进行(如夜 间)。另外,为了节省存储空间,通常结合全量和增量备份技术。为了解决性能和持续运行的问题引入了Snapshot,Snapshot的实现又主要分为split mirror、changed block和current三大类,具体实现方式中会有部分依赖COW技术。 +## 1.2 软件和web系统中的应用 +传统方式下,`fork()函数在创建子进程时直接把所有资源复制给子进程`,即:正文段块,数据段块,堆块,栈块。这种实现方式简单,但是效率低下,而且复制的资源可能对子进程毫无用处。linux为了降低创建子进程的成本,改进fork()实现方式使用COW技术创建子进程。当父进程创建子进程时,内核只为子进程创建虚拟空间,父子两个进程使用的是相同的物理空间。`只有父子进程发生更改时才会为子进程分配独立的物理空间`。 +![[3669053-3b340bc054bdb1e0.png]] + +上图展示从P1进程创建子进程P2时的物理空间使用状况。通过COW技术,fork()延迟了数据拷贝,根据子进程的实际操作最终可能完全避免数据复制,如:子进程创建后运行一个与当前数据无关的可执行文件。 + +### Redis中的COW +Redis的快照就是使用fork()函数创建子进程,由子进程读取数据并持久化到磁盘完成。 +### JDK中的COW +`CopyOnWriteArrayList`和`CopyOnWriteArraySet`都是使用在读多写少且数据总量不大的场景下,在保证多线程写的原子性的同时又避免了读的冲突和竞争,使用迭代器的时候也绝对不会抛出ConcurrentModificationException。 \ No newline at end of file diff --git a/日常学习/计算机基础/EPOLL.md b/日常学习/计算机基础/EPOLL.md new file mode 100644 index 0000000..e69de29 diff --git a/日常学习/计算机基础/http/GET和POST的区别.md b/日常学习/计算机基础/http/GET和POST的区别.md new file mode 100644 index 0000000..d217898 --- /dev/null +++ b/日常学习/计算机基础/http/GET和POST的区别.md @@ -0,0 +1,26 @@ +最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。 +## 一、表面区别: +- GET在浏览器回退时是无害的,而POST会再次提交请求。 +- GET产生的URL地址可以被Bookmark,而POST不可以。 +- GET请求会被浏览器主动cache,而POST不会,除非手动设置。 +- GET请求只能进行url编码,而POST支持多种编码方式。 +- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。 +- GET请求在URL中传送的参数是有长度限制的,而POST么有。 +- 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。 +- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。 +- GET参数通过URL传递,POST放在Request body中。 + +## 二、本质区别: + +## 1.**GET参数通过URL传递,POST放在Request body中。** +其实GET请求也可以将参数放在Request body中,GET和POST本质上就是TCP链接,并无差别。只是浏览器规定限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。超过的部分,恕不处理。 + +## 2.GET产生一个TCP数据包;POST产生两个TCP数据包 +对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。 +为什么发送2次,这是对POST请求的可靠性要求,由于参数在Request body中,需要先发送包含head的请求到对方服务器确认。 + +## 3.能用GET替换POST吗? +因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么? +1. GET与POST都有自己的语义,不能随便混用。 +2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。 +3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。 diff --git a/日常学习/计算机基础/linux/目录结构.md b/日常学习/计算机基础/linux/目录结构.md new file mode 100644 index 0000000..65df011 --- /dev/null +++ b/日常学习/计算机基础/linux/目录结构.md @@ -0,0 +1,12 @@ +# / +![[Snipaste_2023-03-26_09-05-48.png]] + + +# /usr +usr(注意不是 user),全称为 Unix Software Resource,此目录用于存储系统软件资源。FHS 建议所有开发者,应把软件产品的数据合理的放置在 /usr 目录下的各子目录中,而不是为他们的产品创建单独的目录。 +Linux 系统中,所有系统默认的软件都存储在 /usr 目录下,/usr 目录类似 Windows 系统中 C:\Windows\ + C:\Program files\ 两个目录的综合体。 +![[Snipaste_2023-03-26_09-05-48 1.png]] + +# /var +/var 目录用于存储动态数据,例如缓存、日志文件、软件运行过程中产生的文件等。通常,此目录下建议包含如表 4 所示的这些子目录 +![[Snipaste_2023-03-26_09-05-48 2.png]] \ No newline at end of file diff --git a/本机环境/spark-hadoop/spark-hadoop.md b/本机环境/spark-hadoop/spark-hadoop.md new file mode 100644 index 0000000..cc41dbb --- /dev/null +++ b/本机环境/spark-hadoop/spark-hadoop.md @@ -0,0 +1,22 @@ +# 一、安装 +D:/env/docker-compose/spark +```shell +docker-compose up -d +``` + +## 启动Hadoop +1. 进入master 节点,执行./start-hadoop.sh + + +# 二、webUI +[spark 集群管理](http://localhost:8080/) +[hdfs管理](http://localhost:9870/dfshealth.html#tab-overview) +[YARN](http://localhost:8088/cluster) + +# 三、FAQ + +## WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable + +[hadoop 2.x安装:不能加载本地库 - java.library.path错误 - 爱码网 (likecs.com)](https://www.likecs.com/show-308371162.html) + +需要修改hadoop-env.sh文件配置 diff --git a/本机环境/开发环境.md b/本机环境/开发环境.md new file mode 100644 index 0000000..6a9cad7 --- /dev/null +++ b/本机环境/开发环境.md @@ -0,0 +1,4 @@ ++ PG12 +localhost:5432 postgres:root@123 ++ redis +localhost:6379 redispw \ No newline at end of file diff --git a/本机环境/本地Hadoop.md b/本机环境/本地Hadoop.md new file mode 100644 index 0000000..fc45a38 --- /dev/null +++ b/本机环境/本地Hadoop.md @@ -0,0 +1,6 @@ +启动 管理员执行`D:\env\hadoop\hadoop-3.2.0\hadoop\sbin\start-all.cmd` +停止 管理员执行`D:\env\hadoop\hadoop-3.2.0\hadoop\sbin\stop-all.cmd` + + +[Namenode](http://localhost:9870/dfshealth.html#tab-overview) +[YARN](http://localhost:8088/cluster) \ No newline at end of file