diff --git a/wp-includes/default-filters.php b/wp-includes/default-filters.php
index 2fd1d33b69..5598add424 100644
--- a/wp-includes/default-filters.php
+++ b/wp-includes/default-filters.php
@@ -357,6 +357,9 @@ add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );
 add_action( 'publish_post', '_publish_post_hook', 5, 1 );
 add_action( 'transition_post_status', '_transition_post_status', 5, 3 );
 add_action( 'transition_post_status', '_update_term_count_on_transition_post_status', 10, 3 );
+add_action( 'added_term_relationship', 'wp_increment_term_relationship', 10, 3 );
+add_action( 'deleted_term_relationships', 'wp_decrement_term_relationship', 10, 3 );
+add_action( 'edit_term', 'maybe_recount_posts_for_term', 10, 3 );
 add_action( 'comment_form', 'wp_comment_form_unfiltered_html_nonce' );
 add_action( 'admin_init', 'send_frame_options_header', 10, 0 );
 add_action( 'welcome_panel', 'wp_welcome_panel' );
diff --git a/wp-includes/post.php b/wp-includes/post.php
index c76381ce95..05f8dc686f 100644
--- a/wp-includes/post.php
+++ b/wp-includes/post.php
@@ -7189,7 +7189,45 @@ function _update_term_count_on_transition_post_status( $new_status, $old_status,
 	// Update counts for the post's terms.
 	foreach ( (array) get_object_taxonomies( $post->post_type ) as $taxonomy ) {
 		$tt_ids = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'tt_ids' ) );
-		wp_update_term_count( $tt_ids, $taxonomy );
+
+		$countable_status = apply_filters( 'countable_status', [ 'publish' ], $taxonomy );
+
+		$count_new = in_array( $new_status, $countable_status, true );
+		$count_old = in_array( $old_status, $countable_status, true );
+		if ( $count_new && ! $count_old ) {
+			$transition = 'increment';
+		} elseif ( $count_old && ! $count_new ) {
+			$transition = 'decrement';
+		}
+
+		if ( $transition ) {
+			wp_quick_update_term_count( $tt_ids, $taxonomy, $transition );
+		}
+
+		// For non-attachments, let's check if there are any attachment children
+		// with 'inherited' post status -- if so those will need to be re-counted.
+		if ( 'attachment' !== $post->post_type ) {
+			$attachments = new WP_Query( array(
+				'post_type'           => 'attachment',
+				'post_parent'         => $post->ID,
+				'post_status'         => 'inherit',
+				'ignore_sticky_posts' => true,
+				'no_found_rows'       => true,
+				'posts_per_page'      => -1,
+				'fields'              => 'ids',
+				'orderby'             => 'ID',
+				'order'               => 'ASC',
+			) );
+
+			if ( $attachments->have_posts() ) {
+				foreach ( $attachments->posts as $attachment_id ) {
+					_update_term_count_on_transition_post_status( $new_status, $old_status, (object) [
+						'ID' => $attachment_id,
+						'post_type' => 'attachment',
+					] );
+				}
+			}
+		}
 	}
 }
 
diff --git a/wp-includes/taxonomy.php b/wp-includes/taxonomy.php
index d02aa9b9e4..88f26ee684 100644
--- a/wp-includes/taxonomy.php
+++ b/wp-includes/taxonomy.php
@@ -3151,6 +3151,99 @@ function wp_update_term_count( $terms, $taxonomy, $do_deferred = false ) {
 	return wp_update_term_count_now( $terms, $taxonomy );
 }
 
+/**
+ * Uses an increment/decrement system to perform term count.
+ *
+ * @since 5.5
+ *
+ * @param array  $terms           The term_taxonomy_id of terms to update.
+ * @param string $taxonomy        The context of the term.
+ * @param string $transition_type Accepts either 'increment' or 'decrement' value.
+ * @return true Always true when complete.
+ */
+function wp_quick_update_term_count( $terms, $taxonomy, $transition_type ) {
+	$terms = array_map( 'intval', $terms );
+
+	$taxonomy = get_taxonomy( $taxonomy );
+	if ( ! empty( $taxonomy->update_count_callback ) ) {
+		call_user_func( $taxonomy->update_count_callback, $terms, $taxonomy );
+	} elseif ( ! empty( $terms )) {
+		$tt_ids_string = '(' . implode( ',', $terms ) . ')';
+
+		if ( 'increment' === $transition_type ) {
+			// Incrementing.
+			$update_query = $wpdb->prepare( "UPDATE {$wpdb->term_taxonomy} AS tt SET tt.count = tt.count + 1 WHERE tt.term_taxonomy_id IN %s", $tt_ids_string );
+		} else {
+			// Decrementing.
+			$update_query = $wpdb->prepare( "UPDATE {$wpdb->term_taxonomy} AS tt SET tt.count = tt.count - 1 WHERE tt.term_taxonomy_id IN %s AND tt.count > 0", $tt_ids_string );
+		}
+
+		foreach ( $terms as $term ) {
+			/** This action is documented in wp-includes/taxonomy.php */
+			do_action( 'edit_term_taxonomy', $term, $taxonomy );
+		}
+
+		$wpdb->query( $update_query ); // WPCS: unprepared SQL ok.
+		foreach ( $terms as $term ) {
+			/** This action is documented in wp-includes/taxonomy.php */
+			do_action( 'edited_term_taxonomy', $term, $taxonomy );
+		}
+	}
+
+	clean_term_cache( $terms, '', false );
+
+	return true;
+}
+
+/**
+ * When a term relationship is added, increment the term count.
+ *
+ * @since 5.5
+ *
+* @param int    $object_id Object ID.
+* @param int    $tt_id     Single term taxonomy ID.
+* @param string $taxonomy  Taxonomy slug.
+ */
+function wp_increment_term_relationship( $object_id, $tt_id, $taxonomy ) {
+	_handle_term_relationship_change( $object_id, (array) $tt_id, $taxonomy, 'increment' );
+}
+
+/**
+ * When a term relationship is added, decrement the term count.
+ *
+ * @since 5.5
+ *
+* @param int    $object_id Object ID.
+* @param int    $tt_id     Single term taxonomy ID.
+* @param string $taxonomy  Taxonomy slug.
+ */
+function wp_decrement_term_relationship( $object_id, $tt_id, $taxonomy ) {
+	_handle_term_relationship_change( $object_id, (array) $tt_id, $taxonomy, 'decrement' );
+}
+
+/**
+ * Force-recount posts for a term.  Do this only when the update originates from the edit term screen.
+ *
+ * @since 5.5
+ * 
+ * @param  int    $term_id the term id.
+ * @param  int    $tt_id the term taxonomy id.
+ * @param  string $taxonomy the taxonomy.
+ *
+ * @return bool false if the screen check fails, true otherwise
+ */
+function maybe_recount_posts_for_term( $term_id, $tt_id, $taxonomy ) {
+	$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : '';
+	if ( ! ( $screen instanceof WP_Screen ) ) {
+		return false;
+	}
+	if ( "edit-$taxonomy" === $screen->id ) {
+		wp_update_term_count_now( [ $tt_id ], $taxonomy );
+	}
+	return true;
+}
+
+
 /**
  * Perform term count update immediately.
  *
@@ -3602,6 +3695,30 @@ function _get_term_children( $term_id, $terms, $taxonomy, &$ancestors = array()
 	return $term_list;
 }
 
+/**
+ * Update term counts when term relationships are added or deleted.
+ *
+ * @access private
+ * @since 5.5
+ *
+ * @param int    $object_id  Object ID.
+ * @param array  $tt_ids     Array of term taxonomy IDs.
+ * @param string $taxonomy   Taxonomy slug.
+ * @param string $transition Transition (increment or decrement).
+ */
+function _handle_term_relationship_change( $object_id, $tt_ids, $taxonomy, $transition ) {
+	$post = get_post( $object_id );
+
+	if ( ( ! $post || ! is_object_in_taxonomy( $post->post_type, $taxonomy ) ) ||
+		in_array( get_post_status( $post ), apply_filters( 'countable_status', [ 'publish' ], $taxonomy ), true ) ) {
+		// We use `get_post_status()` to check if parent status is 'inherit'.
+		wp_quick_update_term_count( $object_id, $tt_ids, $taxonomy, $transition );
+	} else {
+		clean_term_cache( $tt_ids, $taxonomy, false );
+	}
+}
+
+
 /**
  * Add count of children to parent count.
  *
