| | 646 | |
| | 647 | |
| | 648 | |
| | 649 | |
| | 650 | |
| | 651 | |
| | 652 | |
| | 653 | == Анализа и оптимизација на `Available_Tickets` |
| | 654 | |
| | 655 | Овој поглед овозможува моментален увид во инвентарот на достапни слободни седишта за активните настани. Во него е имплементирана комплексна логика која ја калкулира крајната продажна цена во реално време, земајќи го предвид актуелниот процент на попуст за тековниот временски период во кој се наоѓа купувачот. |
| | 656 | |
| | 657 | {{{ |
| | 658 | |
| | 659 | CREATE OR REPLACE VIEW "Available_Tickets" AS |
| | 660 | SELECT |
| | 661 | t.ticket_id, |
| | 662 | ROUND( |
| | 663 | t.base_price * (1 - COALESCE(ep.price_discount_percent, 0) / 100.0), |
| | 664 | 2 |
| | 665 | ) AS price, |
| | 666 | e.event_id, |
| | 667 | e.name AS event_name, |
| | 668 | eh.event_happening_id, |
| | 669 | eh.event_time, |
| | 670 | v.name AS venue_name, |
| | 671 | s.name AS section_name, |
| | 672 | st.row_number, |
| | 673 | st.seat_number |
| | 674 | FROM "Ticket" t |
| | 675 | JOIN "Event_Happening" eh ON t.event_happening_id = eh.event_happening_id |
| | 676 | JOIN "Event" e ON eh.event_id = e.event_id |
| | 677 | JOIN "Venue" v ON eh.venue_id = v.venue_id |
| | 678 | JOIN "Seat" st ON t.seat_id = st.seat_id |
| | 679 | JOIN "Section" s ON st.section_id = s.section_id |
| | 680 | LEFT JOIN "Event_Period" ep ON eh.event_happening_id = ep.event_happening_id |
| | 681 | AND CURRENT_DATE BETWEEN ep.start_date AND ep.end_date |
| | 682 | WHERE t.is_available = TRUE; |
| | 683 | |
| | 684 | }}} |
| | 685 | |
| | 686 | ==== 1. Примарен филтер: |
| | 687 | |
| | 688 | Погледот се потпира на два филтера: `event_happening_id` (за специфичниот настани) и `is_available` (за статусот на билетот). |
| | 689 | |
| | 690 | ==== 2. Случај на употреба: |
| | 691 | |
| | 692 | Ова е клучниот поглед што се користи во моментот кога корисникот сака да купи билет. Брзината овде директно го намалува ризикот од "Race Condition" - односно, двајца корисници да се обидат да го резервираат истото место истовремено поради доцнење на податоците. |
| | 693 | |
| | 694 | ==== 3. Иницијално време: |
| | 695 | |
| | 696 | * '''SELECT:''' 79.398 ms (Релативно бавно поради '''Bitmap Heap Scan''' и '''Filter''' операција врз табела со илјадници записи). |
| | 697 | * '''INSERT:''' 18.031 ms (Високо време поради проверките на констреинтите при вметнување нови билети). |
| | 698 | * '''UPDATE:''' 0.125 ms (Инстантна брзина преку '''primary key'''). |
| | 699 | |
| | 700 | ==== 4. Анализа на планот на извршување (без индекси): |
| | 701 | |
| | 702 | Без композитен индекс, базата користеше '''Bitmap Heap Scan''' за пребарување на билетите. Иако го пронаоѓаше настанот, таа мораше да прави дополнителна проверка во меморијата ('''Filter: `is_available`''') и да отфрла огромен број записи ('''Rows Removed by Filter: 374'''). Ова трошеше 754 читања од диск. |
| | 703 | |
| | 704 | * '''SELECT''' |
| | 705 | |
| | 706 | {{{ |
| | 707 | |
| | 708 | EXPLAIN ANALYZE |
| | 709 | SELECT * FROM "Available_Tickets" |
| | 710 | WHERE event_happening_id = 1; |
| | 711 | |
| | 712 | }}} |
| | 713 | |
| | 714 | ||= QUERY PLAN =|| |
| | 715 | ||Nested Loop (cost\=2838.67..2934.30 rows\=357 width\=125) (actual time\=77.881..79.398 rows\=360.00 loops\=1)|| |
| | 716 | || Buffers: shared hit\=36 read\=754 dirtied\=1|| |
| | 717 | || -> Nested Loop Left Join (cost\=5.31..45.04 rows\=1 width\=79) (actual time\=3.244..3.250 rows\=1.00 loops\=1)|| |
| | 718 | || Buffers: shared hit\=8 read\=5|| |
| | 719 | || -> Nested Loop (cost\=0.86..24.91 rows\=1 width\=75) (actual time\=2.758..2.763 rows\=1.00 loops\=1)|| |
| | 720 | || Buffers: shared hit\=8 read\=1|| |
| | 721 | || -> Nested Loop (cost\=0.57..16.61 rows\=1 width\=63) (actual time\=0.020..0.024 rows\=1.00 loops\=1)|| |
| | 722 | || Buffers: shared hit\=6|| |
| | 723 | || -> Index Scan using ""Event_Happening_pkey"" on ""Event_Happening"" eh (cost\=0.29..8.30 rows\=1 width\=32) (actual time\=0.013..0.016 rows\=1.00 loops\=1)|| |
| | 724 | || Index Cond: (event_happening_id \= 1)|| |
| | 725 | || Index Searches: 1|| |
| | 726 | || Buffers: shared hit\=3|| |
| | 727 | || -> Index Scan using ""Event_pkey"" on ""Event"" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.005..0.005 rows\=1.00 loops\=1)|| |
| | 728 | || Index Cond: (event_id \= eh.event_id)|| |
| | 729 | || Index Searches: 1|| |
| | 730 | || Buffers: shared hit\=3|| |
| | 731 | || -> Index Scan using ""Venue_pkey"" on ""Venue"" v (cost\=0.29..8.30 rows\=1 width\=28) (actual time\=2.734..2.734 rows\=1.00 loops\=1)|| |
| | 732 | || Index Cond: (venue_id \= eh.venue_id)|| |
| | 733 | || Index Searches: 1|| |
| | 734 | || Buffers: shared hit\=2 read\=1|| |
| | 735 | || -> Bitmap Heap Scan on ""Event_Period"" ep (cost\=4.45..20.12 rows\=1 width\=12) (actual time\=0.481..0.481 rows\=0.00 loops\=1)|| |
| | 736 | || Recheck Cond: (event_happening_id \= 1)|| |
| | 737 | || Filter: ((CURRENT_DATE >\= start_date) AND (CURRENT_DATE <\= end_date))|| |
| | 738 | || Rows Removed by Filter: 4|| |
| | 739 | || Heap Blocks: exact\=1|| |
| | 740 | || Buffers: shared read\=4|| |
| | 741 | || -> Bitmap Index Scan on uq_period_happening_name (cost\=0.00..4.45 rows\=4 width\=0) (actual time\=0.088..0.088 rows\=4.00 loops\=1)|| |
| | 742 | || Index Cond: (event_happening_id \= 1)|| |
| | 743 | || Index Searches: 1|| |
| | 744 | || Buffers: shared read\=3|| |
| | 745 | || -> Merge Join (cost\=2833.37..2881.23 rows\=357 width\=31) (actual time\=74.625..75.812 rows\=360.00 loops\=1)|| |
| | 746 | || Merge Cond: (st.seat_id \= t.seat_id)|| |
| | 747 | || Buffers: shared hit\=28 read\=749 dirtied\=1|| |
| | 748 | || -> Nested Loop (cost\=0.74..1196963.53 rows\=20753208 width\=18) (actual time\=0.227..1.135 rows\=734.00 loops\=1)|| |
| | 749 | || Buffers: shared hit\=23 read\=2|| |
| | 750 | || -> Index Scan using ""Seat_pkey"" on ""Seat"" st (cost\=0.44..671104.56 rows\=20753208 width\=24) (actual time\=0.024..0.343 rows\=734.00 loops\=1)|| |
| | 751 | || Index Searches: 1|| |
| | 752 | || Buffers: shared hit\=8 read\=2|| |
| | 753 | || -> Memoize (cost\=0.30..0.32 rows\=1 width\=10) (actual time\=0.001..0.001 rows\=1.00 loops\=734)|| |
| | 754 | || Cache Key: st.section_id|| |
| | 755 | || Cache Mode: logical|| |
| | 756 | || Hits: 729 Misses: 5 Evictions: 0 Overflows: 0 Memory Usage: 1kB|| |
| | 757 | || Buffers: shared hit\=15|| |
| | 758 | || -> Index Scan using ""Section_pkey"" on ""Section"" s (cost\=0.29..0.31 rows\=1 width\=10) (actual time\=0.009..0.009 rows\=1.00 loops\=5)|| |
| | 759 | || Index Cond: (section_id \= st.section_id)|| |
| | 760 | || Index Searches: 5|| |
| | 761 | || Buffers: shared hit\=15|| |
| | 762 | || -> Sort (cost\=2830.84..2831.74 rows\=357 width\=29) (actual time\=74.391..74.441 rows\=360.00 loops\=1)|| |
| | 763 | || Sort Key: t.seat_id|| |
| | 764 | || Sort Method: quicksort Memory: 41kB|| |
| | 765 | || Buffers: shared hit\=5 read\=747 dirtied\=1|| |
| | 766 | || -> Bitmap Heap Scan on ""Ticket"" t (cost\=22.15..2815.71 rows\=357 width\=29) (actual time\=1.866..73.806 rows\=360.00 loops\=1)|| |
| | 767 | || Recheck Cond: (event_happening_id \= 1)|| |
| | 768 | || Filter: is_available|| |
| | 769 | || Rows Removed by Filter: 374|| |
| | 770 | || Heap Blocks: exact\=744|| |
| | 771 | || Buffers: shared hit\=5 read\=747 dirtied\=1|| |
| | 772 | || -> Bitmap Index Scan on uq_ticket_happening_seat (cost\=0.00..22.06 rows\=733 width\=0) (actual time\=1.599..1.599 rows\=744.00 loops\=1)|| |
| | 773 | || Index Cond: (event_happening_id \= 1)|| |
| | 774 | || Index Searches: 1|| |
| | 775 | || Buffers: shared hit\=4 read\=4|| |
| | 776 | ||Planning:|| |
| | 777 | || Buffers: shared hit\=59 read\=8|| |
| | 778 | ||Planning Time: 1.571 ms|| |
| | 779 | ||Execution Time: 79.504 ms|| |
| | 780 | |
| | 781 | * '''INSERT''' |
| | 782 | |
| | 783 | {{{ |
| | 784 | |
| | 785 | EXPLAIN ANALYZE |
| | 786 | INSERT INTO "Ticket" (ticket_id, event_happening_id, seat_id, base_price, is_available) |
| | 787 | VALUES (99999999, 1, 99999, 1500.00, TRUE); |
| | 788 | |
| | 789 | }}} |
| | 790 | |
| | 791 | ||= QUERY PLAN =|| |
| | 792 | ||Insert on ""Ticket"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=16.622..16.622 rows\=0.00 loops\=1)|| |
| | 793 | || Buffers: shared hit\=8 read\=3 dirtied\=2|| |
| | 794 | || -> Result (cost\=0.00..0.01 rows\=1 width\=41) (actual time\=0.001..0.002 rows\=1.00 loops\=1)|| |
| | 795 | ||Planning Time: 0.028 ms|| |
| | 796 | ||Trigger for constraint fk_ticket_seat: time\=1.188 calls\=1|| |
| | 797 | ||Trigger for constraint fk_ticket_happening: time\=0.202 calls\=1|| |
| | 798 | ||Execution Time: 18.031 ms|| |
| | 799 | |
| | 800 | * '''UPDATE''' |
| | 801 | |
| | 802 | {{{ |
| | 803 | |
| | 804 | EXPLAIN ANALYZE |
| | 805 | UPDATE "Ticket" |
| | 806 | SET is_available = FALSE |
| | 807 | WHERE ticket_id = 99999999; |
| | 808 | |
| | 809 | }}} |
| | 810 | |
| | 811 | ||= QUERY PLAN =|| |
| | 812 | ||Update on ""Ticket"" (cost\=0.44..8.46 rows\=0 width\=0) (actual time\=0.086..0.087 rows\=0.00 loops\=1)|| |
| | 813 | || Buffers: shared hit\=7|| |
| | 814 | || -> Index Scan using ""Ticket_pkey"" on ""Ticket"" (cost\=0.44..8.46 rows\=1 width\=7) (actual time\=0.036..0.038 rows\=1.00 loops\=1)|| |
| | 815 | || Index Cond: (ticket_id \= 99999999)|| |
| | 816 | || Index Searches: 1|| |
| | 817 | || Buffers: shared hit\=4|| |
| | 818 | ||Planning Time: 0.254 ms|| |
| | 819 | ||Execution Time: 0.125 ms|| |
| | 820 | |
| | 821 | ==== 5. Оптимизација и индексирање: |
| | 822 | |
| | 823 | За да се елиминира потребата од скенирање на сите билети (вклучително и оние што веќе се продадени), го имплементиравме овој индекс: |
| | 824 | |
| | 825 | {{{ |
| | 826 | |
| | 827 | CREATE INDEX CONCURRENTLY idx_ticket_available_happening |
| | 828 | ON "Ticket" (event_happening_id) |
| | 829 | WHERE is_available = TRUE; |
| | 830 | |
| | 831 | |
| | 832 | ANALYZE "Ticket"; |
| | 833 | |
| | 834 | }}} |
| | 835 | |
| | 836 | ==== 6. Резултат по оптимизација: |
| | 837 | |
| | 838 | Со индексот, времето на извршување драматично се подобри: |
| | 839 | |
| | 840 | * Времето за '''SELECT''' падна на 1.800 ms, што е 44 пати побрзо. Базата сега користи директна патека до податоците и нема „филтрирање“ на отфрлени редици. |
| | 841 | * Времето за '''INSERT''' падна на 0.628 ms (околу 28 пати побрзо), бидејќи констреинтите сега побрзо ги потврдуваат постојните записи. |
| | 842 | |
| | 843 | * '''SELECT''' |
| | 844 | |
| | 845 | {{{ |
| | 846 | |
| | 847 | EXPLAIN ANALYZE |
| | 848 | SELECT * FROM "Available_Tickets" |
| | 849 | WHERE event_happening_id = 2; |
| | 850 | |
| | 851 | }}} |
| | 852 | |
| | 853 | ||= QUERY PLAN =|| |
| | 854 | ||Nested Loop (cost\=1436.93..1541.30 rows\=363 width\=125) (actual time\=0.899..1.800 rows\=360.00 loops\=1)|| |
| | 855 | || Buffers: shared hit\=402|| |
| | 856 | || -> Nested Loop Left Join (cost\=5.31..45.04 rows\=1 width\=79) (actual time\=0.078..0.081 rows\=1.00 loops\=1)|| |
| | 857 | || Buffers: shared hit\=13|| |
| | 858 | || -> Nested Loop (cost\=0.86..24.91 rows\=1 width\=75) (actual time\=0.031..0.034 rows\=1.00 loops\=1)|| |
| | 859 | || Buffers: shared hit\=9|| |
| | 860 | || -> Nested Loop (cost\=0.57..16.61 rows\=1 width\=63) (actual time\=0.025..0.027 rows\=1.00 loops\=1)|| |
| | 861 | || Buffers: shared hit\=6|| |
| | 862 | || -> Index Scan using ""Event_Happening_pkey"" on ""Event_Happening"" eh (cost\=0.29..8.30 rows\=1 width\=32) (actual time\=0.016..0.017 rows\=1.00 loops\=1)|| |
| | 863 | || Index Cond: (event_happening_id \= 2)|| |
| | 864 | || Index Searches: 1|| |
| | 865 | || Buffers: shared hit\=3|| |
| | 866 | || -> Index Scan using ""Event_pkey"" on ""Event"" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.005..0.005 rows\=1.00 loops\=1)|| |
| | 867 | || Index Cond: (event_id \= eh.event_id)|| |
| | 868 | || Index Searches: 1|| |
| | 869 | || Buffers: shared hit\=3|| |
| | 870 | || -> Index Scan using ""Venue_pkey"" on ""Venue"" v (cost\=0.29..8.30 rows\=1 width\=28) (actual time\=0.005..0.005 rows\=1.00 loops\=1)|| |
| | 871 | || Index Cond: (venue_id \= eh.venue_id)|| |
| | 872 | || Index Searches: 1|| |
| | 873 | || Buffers: shared hit\=3|| |
| | 874 | || -> Bitmap Heap Scan on ""Event_Period"" ep (cost\=4.45..20.12 rows\=1 width\=12) (actual time\=0.043..0.043 rows\=0.00 loops\=1)|| |
| | 875 | || Recheck Cond: (event_happening_id \= 2)|| |
| | 876 | || Filter: ((CURRENT_DATE >\= start_date) AND (CURRENT_DATE <\= end_date))|| |
| | 877 | || Rows Removed by Filter: 4|| |
| | 878 | || Heap Blocks: exact\=1|| |
| | 879 | || Buffers: shared hit\=4|| |
| | 880 | || -> Bitmap Index Scan on uq_period_happening_name (cost\=0.00..4.45 rows\=4 width\=0) (actual time\=0.008..0.008 rows\=4.00 loops\=1)|| |
| | 881 | || Index Cond: (event_happening_id \= 2)|| |
| | 882 | || Index Searches: 1|| |
| | 883 | || Buffers: shared hit\=3|| |
| | 884 | || -> Merge Join (cost\=1431.62..1488.09 rows\=363 width\=31) (actual time\=0.815..1.498 rows\=360.00 loops\=1)|| |
| | 885 | || Merge Cond: (st.seat_id \= t.seat_id)|| |
| | 886 | || Buffers: shared hit\=389|| |
| | 887 | || -> Nested Loop (cost\=0.74..1197017.90 rows\=20753360 width\=18) (actual time\=0.177..0.692 rows\=735.00 loops\=1)|| |
| | 888 | || Buffers: shared hit\=25|| |
| | 889 | || -> Index Scan using ""Seat_pkey"" on ""Seat"" st (cost\=0.44..671106.84 rows\=20753360 width\=24) (actual time\=0.009..0.188 rows\=735.00 loops\=1)|| |
| | 890 | || Index Searches: 1|| |
| | 891 | || Buffers: shared hit\=10|| |
| | 892 | || -> Memoize (cost\=0.30..0.32 rows\=1 width\=10) (actual time\=0.000..0.000 rows\=1.00 loops\=735)|| |
| | 893 | || Cache Key: st.section_id|| |
| | 894 | || Cache Mode: logical|| |
| | 895 | || Hits: 730 Misses: 5 Evictions: 0 Overflows: 0 Memory Usage: 1kB|| |
| | 896 | || Buffers: shared hit\=15|| |
| | 897 | || -> Index Scan using ""Section_pkey"" on ""Section"" s (cost\=0.29..0.31 rows\=1 width\=10) (actual time\=0.003..0.004 rows\=1.00 loops\=5)|| |
| | 898 | || Index Cond: (section_id \= st.section_id)|| |
| | 899 | || Index Searches: 5|| |
| | 900 | || Buffers: shared hit\=15|| |
| | 901 | || -> Sort (cost\=1429.07..1429.98 rows\=363 width\=29) (actual time\=0.629..0.658 rows\=360.00 loops\=1)|| |
| | 902 | || Sort Key: t.seat_id|| |
| | 903 | || Sort Method: quicksort Memory: 41kB|| |
| | 904 | || Buffers: shared hit\=364|| |
| | 905 | || -> Bitmap Heap Scan on ""Ticket"" t (cost\=7.25..1413.64 rows\=363 width\=29) (actual time\=0.088..0.525 rows\=360.00 loops\=1)|| |
| | 906 | || Recheck Cond: ((event_happening_id \= 2) AND is_available)|| |
| | 907 | || Heap Blocks: exact\=361|| |
| | 908 | || Buffers: shared hit\=364|| |
| | 909 | || -> Bitmap Index Scan on idx_ticket_available_happening (cost\=0.00..7.16 rows\=363 width\=0) (actual time\=0.038..0.038 rows\=361.00 loops\=1)|| |
| | 910 | || Index Cond: (event_happening_id \= 2)|| |
| | 911 | || Index Searches: 1|| |
| | 912 | || Buffers: shared hit\=3|| |
| | 913 | ||Planning:|| |
| | 914 | || Buffers: shared hit\=100 read\=2|| |
| | 915 | ||Planning Time: 8.763 ms|| |
| | 916 | ||Execution Time: 1.992 ms|| |
| | 917 | |
| | 918 | * '''INSERT''' |
| | 919 | |
| | 920 | {{{ |
| | 921 | |
| | 922 | EXPLAIN ANALYZE |
| | 923 | INSERT INTO "Ticket" (ticket_id, event_happening_id, seat_id, base_price, is_available) |
| | 924 | VALUES (88888888, 2, 77777, 1800.00, TRUE); |
| | 925 | |
| | 926 | }}} |
| | 927 | |
| | 928 | ||= QUERY PLAN =|| |
| | 929 | ||Insert on ""Ticket"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=0.304..0.304 rows\=0.00 loops\=1)|| |
| | 930 | || Buffers: shared hit\=15 dirtied\=2|| |
| | 931 | || -> Result (cost\=0.00..0.01 rows\=1 width\=41) (actual time\=0.001..0.001 rows\=1.00 loops\=1)|| |
| | 932 | ||Planning Time: 0.029 ms|| |
| | 933 | ||Trigger for constraint fk_ticket_seat: time\=0.201 calls\=1|| |
| | 934 | ||Trigger for constraint fk_ticket_happening: time\=0.107 calls\=1|| |
| | 935 | ||Execution Time: 0.628 ms|| |
| | 936 | |
| | 937 | * '''UPDATE''' |
| | 938 | |
| | 939 | {{{ |
| | 940 | |
| | 941 | EXPLAIN ANALYZE |
| | 942 | UPDATE "Ticket" |
| | 943 | SET is_available = FALSE |
| | 944 | WHERE ticket_id = 88888888; |
| | 945 | |
| | 946 | }}} |
| | 947 | |
| | 948 | ||= QUERY PLAN =|| |
| | 949 | ||Update on ""Ticket"" (cost\=0.44..8.46 rows\=0 width\=0) (actual time\=0.098..0.098 rows\=0.00 loops\=1)|| |
| | 950 | || Buffers: shared hit\=14|| |
| | 951 | || -> Index Scan using ""Ticket_pkey"" on ""Ticket"" (cost\=0.44..8.46 rows\=1 width\=7) (actual time\=0.026..0.028 rows\=1.00 loops\=1)|| |
| | 952 | || Index Cond: (ticket_id \= 88888888)|| |
| | 953 | || Index Searches: 1|| |
| | 954 | || Buffers: shared hit\=4|| |
| | 955 | ||Planning Time: 0.125 ms|| |
| | 956 | ||Execution Time: 0.155 ms|| |